Эх сурвалжийг харах

feat(frontend): 家庭挑战孩子端打卡入口 + cancelled 状态显示修复

Xiaogang Liao 1 сар өмнө
parent
commit
2d70990289

+ 34 - 2
cfc-frontend/components/FamilyChallengeCard.vue

@@ -3,7 +3,7 @@
     <view class="card-header">
       <text class="card-title">{{ challenge.title }}</text>
       <text class="card-status" :class="statusClass">
-        {{ challenge.status === 'active' ? '进行中' : '已完成' }}
+        {{ challenge.status === 'active' ? '进行中' : (challenge.status === 'cancelled' ? '已取消' : '已完成') }}
       </text>
     </view>
     <text class="card-desc">{{ challenge.description || '' }}</text>
@@ -43,13 +43,18 @@
       <text class="detail-label" v-if="challenge.endDate">· 截止 {{ formatDate(challenge.endDate) }}</text>
       <text class="detail-label reward" v-if="challenge.rewardPoints">· 🏆 +{{ challenge.rewardPoints }}分</text>
     </view>
+
+    <view class="card-action" v-if="showCheckin && challenge.status === 'active'">
+      <view class="checkin-btn" @click="onCheckin">✅ 打卡</view>
+    </view>
   </view>
 </template>
 
 <script>
 export default {
   props: {
-    challenge: { type: Object, default: null }
+    challenge: { type: Object, default: null },
+    showCheckin: { type: Boolean, default: false }
   },
   computed: {
     statusClass: function() {
@@ -82,6 +87,18 @@ export default {
       if (target <= 0) return 0
       var p = Math.round((value || 0) / target * 100)
       return Math.min(p, 100)
+    },
+    onCheckin: function() {
+      var self = this
+      uni.showModal({
+        title: '确认打卡',
+        content: '确认完成今日打卡吗?',
+        success: function(res) {
+          if (res.confirm) {
+            self.$emit('checkin', { challengeId: self.challenge.id })
+          }
+        }
+      })
     }
   }
 }
@@ -114,6 +131,7 @@ export default {
 }
 .status-active { background: #DCFCE7; color: #16A34A; }
 .status-completed { background: #F1F5F9; color: #64748B; }
+.status-cancelled { background: #FEE2E2; color: #DC2626; }
 .card-desc {
   font-size: 22rpx;
   color: #64748B;
@@ -219,4 +237,18 @@ export default {
 .detail-val { font-size: 20rpx; color: #334155; }
 .detail-val.reward { color: #F97316; font-weight: 600; }
 .detail-val.badge { color: #6366F1; }
+
+.card-action {
+  margin-top: 8rpx;
+  display: flex;
+  justify-content: flex-end;
+}
+.checkin-btn {
+  padding: 10rpx 18rpx;
+  border-radius: 999rpx;
+  background: #10B981;
+  color: #FFFFFF;
+  font-size: 22rpx;
+  font-weight: 600;
+}
 </style>

+ 18 - 2
cfc-frontend/pages/index-home/index.vue

@@ -107,7 +107,9 @@
       <FamilyChallengeCard
         v-for="ch in activeChallenges"
         :key="ch.id"
-        :challenge="ch" />
+        :challenge="ch"
+        :showCheckin="showCheckinBtn"
+        @checkin="onChallengeCheckin" />
     </view>
     <view v-else>
       <FamilyEmptyState type="card" />
@@ -376,7 +378,7 @@ import CircleCard from '../../components/CircleCard.vue'
 import CircleDetail from '../../components/CircleDetail.vue'
 import LoginGuideCard from '../../components/LoginGuideCard.vue'
 import FamilyChallengeCard from '../../components/FamilyChallengeCard.vue'
-import { acceptParentInvite, productList, getActivityList, getFeaturedArticles, getFamilyEnergySandbox, getVisibleFamilyMembers, getTodayTasksByCategory, getChildren, getEnergyOverview, getProductsByDomain, getChallengeList, getMyCircles, discoverCircles, joinGeneralCircle, leaveGeneralCircle, switchToFamilyMember, getMyMembership } from '../../utils/api.js'
+import { acceptParentInvite, productList, getActivityList, getFeaturedArticles, getFamilyEnergySandbox, getVisibleFamilyMembers, getTodayTasksByCategory, getChildren, getEnergyOverview, getProductsByDomain, getChallengeList, getMyCircles, discoverCircles, joinGeneralCircle, leaveGeneralCircle, switchToFamilyMember, getMyMembership, updateChallengeProgress } from '../../utils/api.js'
 import store from '../../store/index.js'
 import config from '@/config.js'
 
@@ -517,6 +519,9 @@ export default {
       if (!this.isMember) return true
       return this.membershipDays > 0 && this.membershipDays <= 30
     },
+    showCheckinBtn: function() {
+      return (uni.getStorageSync('role') || 'parent') === 'child'
+    },
     memberEntryTitle: function() {
       if (!this.isMember) return '开通会员,解锁全部特权'
       return '会员即将到期,续费恢复权益'
@@ -710,6 +715,17 @@ export default {
         self.activeChallenges = []
       })
     },
+    onChallengeCheckin: function(payload) {
+      var self = this
+      updateChallengeProgress({ challengeId: payload.challengeId, delta: 1 }).then(function(res) {
+        if (res && res.code === 200) {
+          uni.showToast({ title: '打卡成功', icon: 'success' })
+          self.loadChallenges()
+        } else {
+          uni.showToast({ title: (res && res.message) || '打卡失败', icon: 'none' })
+        }
+      })
+    },
     loadMyCircles: function() {
       var self = this
       if (!this.currentChildId) return