Просмотр исходного кода

fix(小程序): 孩子端任务页/首页我的任务补齐打卡联动跳转

- child/tasks.vue: handleTaskClick 原本仅小游戏类跳转,其它任务一律直接完成。
  新增 navigateByAction(与 tasks.vue 同款白名单:minigame/focus/survey/article/
  activity/cart/order/diet/sleep/exercise,外部商品跳 task-upload-purchase),
  联动类点击跳转对应功能页、提交后由后端回写;普通任务才直接完成。
  新增 getActionLabel 按钮文案分派(开始游戏/去打卡/去阅读/去购物等)。
- index-home/index.vue: 首页「我的任务」快捷完成按钮同样直接完成。loadTodayTasks
  标记 _isLinked,按钮改显「去完成」,quickCompleteTask 对联动类调用新增的
  navigateByLinkedAction 跳转,普通任务才直接完成。
zxyj-web 3 недель назад
Родитель
Сommit
5251a50c50
2 измененных файлов с 117 добавлено и 5 удалено
  1. 71 4
      cfc-frontend/pages/child/tasks.vue
  2. 46 1
      cfc-frontend/pages/index-home/index.vue

+ 71 - 4
cfc-frontend/pages/child/tasks.vue

@@ -83,8 +83,7 @@
                   :disabled="completingTaskId === task.id"
                   @click="handleTaskClick(task)"
                 >
-                  <text v-if="task.category === '小游戏类'">🎮 开始游戏</text>
-                  <text v-else>✅ 完成了!</text>
+                  <text>{{ getActionLabel(task) }}</text>
                 </PlayfulButton>
               </template>
 
@@ -270,6 +269,7 @@ export default {
     handleTaskClick(task) {
       var gamePages, gamePage
       if (this.completingTaskId === task.id) return
+      // 小游戏类保留原逻辑:按 minigameCode 跳转
       if (task.category === '小游戏类' && task.minigameCode) {
         gamePages = {
           'schulte': '/pages/games/schulte',
@@ -282,9 +282,76 @@ export default {
             url: gamePage + '?taskId=' + task.id + '&memberId=' + this.currentChildId
           })
         }
-      } else {
-        this.completeChildTask(task.id)
+        return
       }
+      // 打卡/联动类任务(饮食/运动/睡眠/阅读/购买/问卷/活动等):跳转对应功能页,
+      // 提交后由后端回写完成;其余普通任务才直接完成
+      if (this.navigateByAction(task)) return
+      this.completeChildTask(task.id)
+    },
+    /** 按 action_type 跳转联动页面(与家长端 pages/tasks/tasks.vue 的 navigateByAction 同款) */
+    navigateByAction(task) {
+      var at = task.actionType
+      var taskId = task.id
+      var cfg = {}
+      try { cfg = typeof task.actionConfig === 'string' ? JSON.parse(task.actionConfig || '{}') : (task.actionConfig || {}) } catch (e) {}
+      if (at === 'minigame') {
+        var gameId = cfg.gameId || ''
+        uni.navigateTo({ url: '/pages/games/index?taskId=' + taskId + (gameId ? '&gameId=' + gameId : '') })
+        return true
+      }
+      if (at === 'focus') {
+        uni.navigateTo({ url: '/pages/games/focus-timer?taskId=' + taskId })
+        return true
+      }
+      if (at === 'survey') {
+        uni.navigateTo({ url: '/pages/survey/index?taskId=' + taskId })
+        return true
+      }
+      if (at === 'article') {
+        var articleId = cfg.articleId || task.sourceId
+        uni.navigateTo({ url: '/pages/article-center/article-detail?taskId=' + taskId + (articleId ? '&id=' + articleId : '') })
+        return true
+      }
+      if (at === 'activity') {
+        uni.navigateTo({ url: '/pages/activity/list?taskId=' + taskId })
+        return true
+      }
+      if (at === 'cart' || at === 'order') {
+        var isExternal = cfg.isExternal === 1 || cfg.isExternal === '1'
+        if (isExternal) {
+          var tTitle = encodeURIComponent(task.title || '外部平台商品')
+          uni.navigateTo({ url: '/pages/tasks/task-upload-purchase?taskId=' + taskId + '&title=' + tTitle })
+        } else {
+          uni.navigateTo({ url: '/pages/shop/cart?taskId=' + taskId })
+        }
+        return true
+      }
+      if (at === 'diet') {
+        uni.navigateTo({ url: '/pages/growth/diet-checkin?taskId=' + taskId })
+        return true
+      }
+      if (at === 'sleep') {
+        uni.navigateTo({ url: '/pages/growth/sleep-checkin?taskId=' + taskId })
+        return true
+      }
+      if (at === 'exercise') {
+        uni.navigateTo({ url: '/pages/growth/exercise-checkin?taskId=' + taskId })
+        return true
+      }
+      return false
+    },
+    /** 按钮文案:按 action_type 分派(与家长端 getActionLabel 同款) */
+    getActionLabel(task) {
+      if (task.category === '小游戏类' || task.actionType === 'minigame') return '🎮 开始游戏'
+      var at = task.actionType
+      if (at === 'focus') return '⏱️ 开始专注'
+      if (at === 'survey') return '📝 去填写'
+      if (at === 'article') return '📖 去阅读'
+      if (at === 'activity') return '🎯 去报名'
+      if (at === 'cart' || at === 'order') return '🛒 去购物'
+      if (at === 'diet' || at === 'sleep' || at === 'exercise') return '✅ 去打卡'
+      return '✅ 完成了!'
     },
     getCategoryIcon(category) {
       var icons = {

+ 46 - 1
cfc-frontend/pages/index-home/index.vue

@@ -159,7 +159,7 @@
             <text class="task-quick-meta" v-if="!task._isOverdue">+{{ task.points }}CF</text>
             <text class="task-quick-meta task-quick-meta--overdue" v-else>已超期</text>
           </view>
-          <button v-if="!task._isOverdue" class="btn-quick-complete" @click="quickCompleteTask(task)">完成</button>
+          <button v-if="!task._isOverdue" class="btn-quick-complete" @click="quickCompleteTask(task)">{{ task._isLinked ? '去完成' : '完成' }}</button>
           <view v-else class="status-badge-overdue">⏰ 已超期</view>
         </view>
       </view>
@@ -1122,6 +1122,10 @@ title: '添加家庭成员',
                 t._isOverdue = true
               }
             }
+            // 打卡/联动类任务在首页显示「去完成」,点击跳转对应功能页而非直接完成
+            if (t.actionType && ['minigame', 'focus', 'survey', 'article', 'activity', 'cart', 'order', 'diet', 'sleep', 'exercise'].indexOf(t.actionType) >= 0) {
+              t._isLinked = true
+            }
             return true
           })
         }
@@ -1500,6 +1504,15 @@ title: '添加家庭成员',
     },
     quickCompleteTask: function(task) {
       var self = this
+      // 打卡/联动类任务:跳转对应功能页(饮食/运动/睡眠/阅读/购买/问卷/活动等),
+      // 提交后由后端回写完成,不在首页直接完成
+      if (task && task.actionType) {
+        var linked = ['minigame', 'focus', 'survey', 'article', 'activity', 'cart', 'order', 'diet', 'sleep', 'exercise']
+        if (linked.indexOf(task.actionType) >= 0) {
+          self.navigateByLinkedAction(task)
+          return
+        }
+      }
       // 首页「我的任务」只展示本人任务:memberId 取任务归属成员,后端亦会按任务
       // 实际归属校验(不属于本人时返回空数据),不会替其他家庭成员完成任务
       var memberId = (task && task.familyMemberId) || null
@@ -1520,6 +1533,38 @@ title: '添加家庭成员',
         uni.showToast({ title: '完成失败,请重试', icon: 'none' })
       })
     },
+    /** 打卡/联动类任务:跳转对应功能页(与 pages/tasks/tasks.vue 的 navigateByAction 同款) */
+    navigateByLinkedAction: function(task) {
+      var cfg = {}
+      try { cfg = typeof task.actionConfig === 'string' ? JSON.parse(task.actionConfig || '{}') : (task.actionConfig || {}) } catch (e) {}
+      var at = task.actionType
+      var taskId = task.id
+      if (at === 'minigame') {
+        uni.navigateTo({ url: '/pages/games/index?taskId=' + taskId })
+      } else if (at === 'focus') {
+        uni.navigateTo({ url: '/pages/games/focus-timer?taskId=' + taskId })
+      } else if (at === 'survey') {
+        uni.navigateTo({ url: '/pages/survey/index?taskId=' + taskId })
+      } else if (at === 'article') {
+        var articleId = cfg.articleId || task.sourceId
+        uni.navigateTo({ url: '/pages/article-center/article-detail?taskId=' + taskId + (articleId ? '&id=' + articleId : '') })
+      } else if (at === 'activity') {
+        uni.navigateTo({ url: '/pages/activity/list?taskId=' + taskId })
+      } else if (at === 'cart' || at === 'order') {
+        var isExternal = cfg.isExternal === 1 || cfg.isExternal === '1'
+        if (isExternal) {
+          uni.navigateTo({ url: '/pages/tasks/task-upload-purchase?taskId=' + taskId })
+        } else {
+          uni.navigateTo({ url: '/pages/shop/cart?taskId=' + taskId })
+        }
+      } else if (at === 'diet') {
+        uni.navigateTo({ url: '/pages/growth/diet-checkin?taskId=' + taskId })
+      } else if (at === 'sleep') {
+        uni.navigateTo({ url: '/pages/growth/sleep-checkin?taskId=' + taskId })
+      } else if (at === 'exercise') {
+        uni.navigateTo({ url: '/pages/growth/exercise-checkin?taskId=' + taskId })
+      }
+    },
     onChallengeCheckin: function(payload) {
       var self = this
       updateChallengeProgress({ challengeId: payload.challengeId, delta: 1 }).then(function(res) {