Forráskód Böngészése

添加付费解锁功能

wuzj 6 napja
szülő
commit
aed1f1a84d
1 módosított fájl, 156 hozzáadás és 52 törlés
  1. 156 52
      src/pages/room/waiting/index.vue

+ 156 - 52
src/pages/room/waiting/index.vue

@@ -1,6 +1,6 @@
 <template>
   <view class="waiting-room-page">
-    <!-- 房间头部信息 -->
+    <!-- 房间头部信息 保持不变 -->
     <view class="room-header">
       <view class="room-title">
         <view class="title">{{ currentRoom?.name || '等待房间' }}</view>
@@ -14,7 +14,7 @@
       </view>
     </view>
 
-    <!-- 房间码展示和分享 -->
+    <!-- 房间码展示和分享 保持不变 -->
     <RoomCode
       :code="currentRoom?.id || ''"
       :password="currentRoom?.password"
@@ -33,7 +33,7 @@
           游戏设置
         </nut-divider>
         
-        <!-- 主题选择 -->
+        <!-- 1. 主题选择 -->
         <view class="setting-item">
           <view class="setting-label">主题选择</view>
           <nut-cell 
@@ -46,12 +46,12 @@
           </nut-cell>
         </view>
         
-        <!-- 题目选择 -->
-        <view class="setting-item" v-if="selectedThemeId">
-          <view class="setting-label">题目选择</view>
+        <!-- 2. 难度选择 -->
+        <view class="setting-item">
+          <view class="setting-label">游戏难度</view>
           <nut-cell 
-            :desc="selectedPuzzle?.text || '请选择游戏题目'" 
-            @click="showPuzzleSelector = true"
+            :desc="difficultyText" 
+            @click="showDifficultySelector = true"
           >
             <template #link>
               <IconFont name="right" size="16"></IconFont>
@@ -59,12 +59,12 @@
           </nut-cell>
         </view>
         
-        <!-- 难度选择 -->
-        <view class="setting-item">
-          <view class="setting-label">游戏难度</view>
+        <!-- 3. 题目选择 (只有当主题和难度选择后才显示) -->
+        <view class="setting-item" v-if="selectedThemeId && selectedDifficulty">
+          <view class="setting-label">题目选择</view>
           <nut-cell 
-            :desc="difficultyText" 
-            @click="showDifficultySelector = true"
+            :desc="selectedPuzzle?.text || '请选择游戏题目'" 
+            @click="selectedThemeId ? showPuzzleSelector = true : null"
           >
             <template #link>
               <IconFont name="right" size="16"></IconFont>
@@ -73,7 +73,7 @@
         </view>
       </view>
       
-      <!-- 玩家列表 -->
+      <!-- 玩家列表 保持不变 -->
       <view class="player-list">
         <nut-divider 
           content-position="center"
@@ -103,7 +103,7 @@
         </view>
       </view>
       
-      <!-- 主持人操作按钮 -->
+      <!-- 主持人操作按钮 保持不变 -->
       <view class="action-buttons">
         <nut-button 
           block 
@@ -117,7 +117,7 @@
       </view>
     </view>
 
-    <!-- 玩家视图 -->
+    <!-- 玩家视图 保持不变 -->
     <view v-else class="player-view">
       <!-- 游戏信息展示 -->
       <view class="game-info">
@@ -206,49 +206,22 @@
               v-for="theme in themeOptions" 
               :key="theme.value" 
               class="theme-option"
-              :class="{ 'disabled': theme.disabled, 'selected': selectedThemeId === theme.value }"
-              @click="!theme.disabled && handleThemeSelect(theme)"
+              :class="{ 'disabled': theme.disabled && !canPurchase, 'selected': selectedThemeId === theme.value }"
+              @click="handleThemeClick(theme)"
             >
               <view class="option-content">
                 <view class="option-title">{{ theme.text }}</view>
                 <view v-if="theme.description" class="option-desc">{{ theme.description }}</view>
                 <view v-if="theme.locked" class="theme-locked">
                   <IconFont name="lock" size="12"></IconFont>
-                  <view class="lock-text">解锁条件: {{ theme.unlockRequirement }}</view>
+                  <view class="lock-text">{{ theme.unlockRequirement }}</view>
                 </view>
               </view>
-              <IconFont v-if="selectedThemeId === theme.value" name="check" color="#3C92FB" size="16"></IconFont>
-            </view>
-          </view>
-        </scroll-view>
-      </view>
-    </nut-popup>
-
-    <!-- 题目选择弹窗 -->
-    <nut-popup v-model:visible="showPuzzleSelector" position="bottom">
-      <view class="selector-container">
-        <view class="selector-header">
-          <view class="selector-title">选择游戏题目</view>
-          <nut-button size="small" @click="showPuzzleSelector = false">取消</nut-button>
-        </view>
-        <scroll-view 
-          scroll-y 
-          class="puzzle-scroll"
-          :style="{ maxHeight: puzzleScrollHeight + 'px' }"
-        >
-          <view class="puzzle-list">
-            <view 
-              v-for="puzzle in puzzleOptions" 
-              :key="puzzle.value" 
-              class="puzzle-option"
-              :class="{ 'disabled': puzzle.disabled, 'selected': selectedPuzzleId === puzzle.value }"
-              @click="!puzzle.disabled && handlePuzzleSelect(puzzle)"
-            >
-              <view class="option-content">
-                <view class="option-title">{{ puzzle.text }}</view>
-                <view v-if="puzzle.description" class="option-desc">{{ puzzle.description }}</view>
+              <!-- 添加解锁购买按钮 -->
+              <view v-if="theme.locked && canPurchase" class="unlock-button" @click.stop="purchaseTheme(theme)">
+                <nut-button size="small" type="primary">解锁 ({{ theme.price || '5' }}元)</nut-button>
               </view>
-              <IconFont v-if="selectedPuzzleId === puzzle.value" name="check" color="#3C92FB" size="16"></IconFont>
+              <IconFont v-else-if="selectedThemeId === theme.value" name="check" color="#3C92FB" size="16"></IconFont>
             </view>
           </view>
         </scroll-view>
@@ -299,6 +272,37 @@
         </view>
       </view>
     </nut-popup>
+
+    <!-- 题目选择弹窗 -->
+    <nut-popup v-model:visible="showPuzzleSelector" position="bottom">
+      <view class="selector-container">
+        <view class="selector-header">
+          <view class="selector-title">选择游戏题目</view>
+          <nut-button size="small" @click="showPuzzleSelector = false">取消</nut-button>
+        </view>
+        <scroll-view 
+          scroll-y 
+          class="puzzle-scroll"
+          :style="{ maxHeight: puzzleScrollHeight + 'px' }"
+        >
+          <view class="puzzle-list">
+            <view 
+              v-for="puzzle in puzzleOptions" 
+              :key="puzzle.value" 
+              class="puzzle-option"
+              :class="{ 'disabled': puzzle.disabled, 'selected': selectedPuzzleId === puzzle.value }"
+              @click="!puzzle.disabled && handlePuzzleSelect(puzzle)"
+            >
+              <view class="option-content">
+                <view class="option-title">{{ puzzle.text }}</view>
+                <view v-if="puzzle.description" class="option-desc">{{ puzzle.description }}</view>
+              </view>
+              <IconFont v-if="selectedPuzzleId === puzzle.value" name="check" color="#3C92FB" size="16"></IconFont>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+    </nut-popup>
   </view>
   <Tabbar></Tabbar>
 </template>
@@ -323,6 +327,7 @@ interface CascaderOption {
   disabled?: boolean;
   locked?: boolean;
   unlockRequirement?: string;
+  price?: string; // 添加价格字段
   children?: CascaderOption[];
 }
 
@@ -349,6 +354,9 @@ export default {
     // 房间ID
     const roomId = ref('')
     
+    // 是否可以购买
+    const canPurchase = ref(true) // 默认允许购买
+    
     // 获取当前房间
     const currentRoom = computed(() => roomStore.currentRoom)
     
@@ -517,7 +525,8 @@ export default {
             description: theme.description,
             disabled: theme.isLocked, // 根据是否解锁决定是否可选
             locked: theme.isLocked,
-            unlockRequirement: theme.unlockRequirement || '完成相关任务'
+            price: theme.price || '5', // 添加价格,默认5元
+            unlockRequirement: theme.isLocked ? (theme.unlockRequirement || '购买解锁') : ''
           }))
           console.log('已加载主题:', themeOptions.value)
         }
@@ -571,6 +580,27 @@ export default {
       }
     }
     
+    // 处理主题点击
+    const handleThemeClick = (theme: CascaderOption) => {
+      // 如果主题被锁定且不可购买,则不做任何操作
+      if (theme.disabled && !canPurchase) {
+        Taro.showToast({
+          title: '该主题暂未解锁',
+          icon: 'none'
+        })
+        return
+      }
+      
+      // 如果主题被锁定且可购买,弹出购买提示
+      if (theme.locked && canPurchase) {
+        // 购买流程在 purchaseTheme 中处理
+        return
+      }
+      
+      // 正常选择主题
+      handleThemeSelect(theme)
+    }
+    
     // 处理主题选择
     const handleThemeSelect = (theme: CascaderOption) => {
       if (theme.disabled) {
@@ -591,6 +621,73 @@ export default {
       }, 300)
     }
     
+    // 购买主题
+    const purchaseTheme = async (theme: CascaderOption) => {
+      try {
+        Taro.showLoading({ title: '准备支付...' })
+        
+        // 调用微信支付接口
+        Taro.requestPayment({
+          timeStamp: Date.now().toString(),
+          nonceStr: Math.random().toString(36).substring(2, 15),
+          package: `prepay_id=wx${Date.now()}`,
+          signType: 'MD5',
+          paySign: 'test_sign', // 实际项目中需要服务端生成真实的支付参数
+          success: async () => {
+            // 支付成功后,解锁主题
+            Taro.hideLoading()
+            Taro.showLoading({ title: '解锁中...' })
+            
+            try {
+              // 调用解锁主题API
+              const result = await turtleSoupStore.unlockTheme(theme.value)
+              
+              if (result.success) {
+                Taro.showToast({
+                  title: '解锁成功',
+                  icon: 'success'
+                })
+                
+                // 更新主题列表
+                await loadThemes()
+                
+                // 选择刚解锁的主题
+                const updatedTheme = themeOptions.value.find(t => t.value === theme.value)
+                if (updatedTheme && !updatedTheme.disabled) {
+                  handleThemeSelect(updatedTheme)
+                }
+              } else {
+                throw new Error('解锁主题失败')
+              }
+            } catch (error) {
+              console.error('解锁主题失败:', error)
+              Taro.showToast({
+                title: '解锁失败,请联系客服',
+                icon: 'none'
+              })
+            } finally {
+              Taro.hideLoading()
+            }
+          },
+          fail: (err) => {
+            Taro.hideLoading()
+            console.log('支付失败:', err)
+            Taro.showToast({
+              title: '支付已取消',
+              icon: 'none'
+            })
+          }
+        })
+      } catch (error) {
+        Taro.hideLoading()
+        console.error('发起支付失败:', error)
+        Taro.showToast({
+          title: '支付发起失败',
+          icon: 'none'
+        })
+      }
+    }
+    
     // 处理难度选择
     const handleDifficultySelect = (difficulty: TurtleSoupDifficulty) => {
       selectedDifficulty.value = difficulty
@@ -791,12 +888,15 @@ export default {
       startGame,
       handleCopy,
       handleShare,
+      handleThemeClick,
       handleThemeSelect,
       handleDifficultySelect,
       handlePuzzleSelect,
+      purchaseTheme,
       handleAvatarError,
       themeScrollHeight,
-      puzzleScrollHeight
+      puzzleScrollHeight,
+      canPurchase
     }
   },
   
@@ -1102,6 +1202,10 @@ export default {
             margin-left: $spacing-mini;
           }
         }
+        
+        .unlock-button {
+          margin-left: $spacing-mini;
+        }
       }
     }
   }