Przeglądaj źródła

refactor(frontend): 多页面 v-for :key 统一改为索引绑定,删除冗余 getXxxKey 方法

liaoxg 6 dni temu
rodzic
commit
0b2c71e3d1

+ 1 - 4
cfc-frontend/components/daily-task-card.vue

@@ -19,7 +19,7 @@
       <!-- 打卡类 -->
       <view class="daily-task-group" v-if="overview.checkins && overview.checkins.length > 0">
         <view class="daily-task-group-title">打卡</view>
-        <view class="daily-task-item" v-for="item in overview.checkins" :key="getCheckinKey(item)" @click="go(item.route)">
+        <view class="daily-task-item" v-for="(item, ci) in overview.checkins" :key="ci" @click="go(item.route)">
           <text class="daily-task-item-label">{{ item.label }}</text>
           <text v-if="item.done" class="daily-task-done">✓</text>
           <text v-else class="daily-task-go">去完成</text>
@@ -99,9 +99,6 @@ export default {
         self.error = true
       })
     },
-    getCheckinKey: function(item) {
-      return item.key || item.label || ''
-    },
     go: function(route) {
       if (!route) return
       uni.navigateTo({ url: route })

+ 8 - 16
cfc-frontend/components/report-blocks-renderer.vue

@@ -1,17 +1,17 @@
 <template>
   <view class="blocks-renderer">
-    <view v-for="(block, bi) in blocks" :key="getBlockKey(bi)" class="block-wrap">
+    <view v-for="(block, bi) in blocks" :key="bi" class="block-wrap">
       <!-- score: 评分圆环组 -->
       <view v-if="block.type === 'score'" class="block score-block">
         <view class="block-title">{{ block.title }}</view>
         <view class="score-grid">
-          <view class="score-item" v-for="(item, si) in block.items" :key="getScoreKey(si)">
+          <view class="score-item" v-for="(item, si) in block.items" :key="si">
             <view class="score-circle" :style="'border-color:' + (item.color || '#4A9BD7')">
               <text class="score-value">{{ item.value }}</text>
             </view>
             <text class="score-label">{{ item.label }}</text>
             <view class="sub-items" v-if="item.subItems && item.subItems.length" style="margin-top:8rpx; display:flex; flex-direction:column; align-items:center;">
-              <view class="sub-item" v-for="(sub, ssi) in item.subItems" :key="getSubKey(ssi)" style="display:flex; flex-direction:row; align-items:center;">
+              <view class="sub-item" v-for="(sub, ssi) in item.subItems" :key="ssi" style="display:flex; flex-direction:row; align-items:center;">
                 <text class="sub-label" style="font-size:20rpx; color:#666; margin-right:4rpx;">{{ sub.label }}:</text>
                 <text class="sub-value" style="font-size:20rpx; color:#333;">{{ sub.value }}</text>
               </view>
@@ -23,7 +23,7 @@
       <!-- risk_group: 风险分组(按等级徽章) -->
       <view v-else-if="block.type === 'risk_group'" class="block risk-block">
         <view class="block-title">{{ block.title }}</view>
-        <view class="risk-card" v-for="(item, ri) in block.items" :key="getRiskKey(ri)"
+        <view class="risk-card" v-for="(item, ri) in block.items" :key="ri"
               :class="'risk-level-' + riskCss(item.level)">
           <text class="risk-name">{{ item.name }}</text>
           <text class="risk-value">风险值: {{ item.value || '--' }}</text>
@@ -38,7 +38,7 @@
           <text>{{ block.title }}</text>
           <text class="title-abnormal" v-if="block.abnormalCount && block.abnormalCount > 0">,{{ block.abnormalCount }} 项异常</text>
         </view>
-        <view class="indicator-item" v-for="(item, ii) in block.items" :key="getIndicatorKey(ii)">
+        <view class="indicator-item" v-for="(item, ii) in block.items" :key="ii">
           <text class="ind-name">{{ item.name }}</text>
           <text class="ind-value">{{ item.value }} {{ item.unit }}</text>
           <text class="ind-status" :class="'status-' + statusCss(item.status)">{{ item.status }}</text>
@@ -50,13 +50,13 @@
       <view v-else-if="block.type === 'list'" class="block list-block">
         <view class="block-title">{{ block.title }}</view>
         <view class="list-head" v-if="block.columns">
-          <text class="list-cell head-cell" v-for="(colItem, ci) in block.columns" :key="getColumnKey(ci)"
+          <text class="list-cell head-cell" v-for="(colItem, ci) in block.columns" :key="ci"
                 :style="'flex:' + (ci === 0 ? 2 : 1)">{{ colItem.label }}</text>
           <text class="list-cell head-cell" style="flex:0 0 60rpx;">帮助</text>
         </view>
-        <view class="list-row" v-for="(item, li) in block.items" :key="getListItemKey(li)">
+        <view class="list-row" v-for="(item, li) in block.items" :key="li">
           <text class="list-cell" :style="'flex:' + (ci === 0 ? 2 : 1)"
-                v-for="(colItem, ci) in block.columns" :key="getListColumnKey(ci)">{{ item[colItem.key] || '--' }}</text>
+                v-for="(colItem, ci) in block.columns" :key="ci">{{ item[colItem.key] || '--' }}</text>
           <text class="list-cell ind-help-btn" style="flex:0 0 60rpx; text-align:center;" @tap.stop="onHelp(item, 'bacteria')">?</text>
         </view>
       </view>
@@ -83,14 +83,6 @@ export default {
     blocks: { type: Array, default: function() { return [] } }
   },
   methods: {
-    getBlockKey: function(bi) { return 'b' + bi },
-    getSubKey: function(ssi) { return 'sub' + ssi },
-    getScoreKey: function(si) { return 's' + si },
-    getRiskKey: function(ri) { return 'r' + ri },
-    getIndicatorKey: function(ii) { return 'i' + ii },
-    getColumnKey: function(ci) { return 'c' + ci },
-    getListItemKey: function(li) { return 'l' + li },
-    getListColumnKey: function(ci) { return 'lc' + ci },
     riskCss: function(level) {
       var important = ['需注意', '注意', '高风险', '异常']
       return important.indexOf(level) >= 0 ? 'warning' : 'low'

+ 1 - 5
cfc-frontend/pages/butler/select.vue

@@ -21,7 +21,7 @@
     <!-- 管家列表 -->
     <view class="list-section" id="butlerList">
       <view class="section-title">可选管家</view>
-      <view class="butler-card" v-for="(item, idx) in butlers" :key="getItemKey(idx)"
+      <view class="butler-card" v-for="(item, idx) in butlers" :key="idx"
         :class="{ active: myButler && myButler.butlerUserId === item.butlerUserId }">
         <image class="card-avatar" :src="item.avatar || '/static/logo.png'" mode="aspectFill" />
         <view class="card-body">
@@ -65,10 +65,6 @@ export default {
     }
   },
   methods: {
-    getItemKey(idx) {
-      var item = this.butlers[idx]
-      return item && item.butlerUserId ? 'b_' + item.butlerUserId : 'idx_' + idx
-    },
     formatDate(iso) {
       if (!iso) return ''
       return iso.substring(0, 19).replace('T', ' ').substring(0, 16)

+ 4 - 17
cfc-frontend/pages/cognitive-test/memory-test.vue

@@ -30,7 +30,7 @@
       <view class="show-area" v-if="phase === 'show'">
         <text class="show-hint">记住这串数字</text>
         <view class="digit-row">
-          <view class="show-digit" v-for="(d, i) in showDigits" :key="getKey(d, i)">
+          <view class="show-digit" v-for="(d, i) in showDigits" :key="i">
             <text>{{ d }}</text>
           </view>
         </view>
@@ -46,17 +46,17 @@
           <view
             class="input-cell"
             v-for="(c, i) in inputCells"
-            :key="getCellKey(i)"
+            :key="i"
             :class="{ filled: i < userInput.length }"
           ></view>
         </view>
         <view class="pad">
-          <view class="pad-row" v-for="(row, ri) in padRows" :key="getRowKey(ri)">
+          <view class="pad-row" v-for="(row, ri) in padRows" :key="ri">
             <view
               class="pad-key"
               :class="{ 'pad-key-ctrl': key.ctrl }"
               v-for="(key, ci) in row"
-              :key="getPadKey(key, ri, ci)"
+              :key="ci"
               @click="onPadPress(key)"
             >
               <text>{{ key.v }}</text>
@@ -154,19 +154,6 @@ export default {
     this.clearAllTimers()
   },
   methods: {
-    // key 一律用方法调用,禁止模板内表达式
-    getKey(d, i) {
-      return 'digit-' + i
-    },
-    getCellKey(i) {
-      return 'cell-' + i
-    },
-    getRowKey(ri) {
-      return 'row-' + ri
-    },
-    getPadKey(key, ri, ci) {
-      return 'pad-' + ri + '-' + ci
-    },
     clearAllTimers() {
       if (this.showTimer) {
         clearInterval(this.showTimer)

+ 2 - 5
cfc-frontend/pages/cognitive-test/spatial-test.vue

@@ -42,8 +42,8 @@
 
       <view class="options-wrap">
         <view
-          v-for="opt in currentQuestion.options"
-          :key="getOptKey(opt)"
+          v-for="(opt, oi) in currentQuestion.options"
+          :key="oi"
           class="option-btn"
           :class="selected === opt ? 'option-selected' : ''"
           @tap="handleOptionTap(opt)"
@@ -167,9 +167,6 @@ export default {
     }
   },
   methods: {
-    getOptKey(opt) {
-      return this.currentIndex + '_' + opt
-    },
     generateQuestions() {
       var shuffled = shuffleArray(QUESTION_BANK)
       return shuffled.slice(0, TOTAL_QUESTIONS)

+ 1 - 5
cfc-frontend/pages/cognitive-test/speed-test.vue

@@ -39,7 +39,7 @@
         <view class="choice-grid">
           <view
             v-for="(sym, idx) in currentOptions"
-            :key="getSymKey(sym)"
+            :key="idx"
             :class="'choice-cell' + (selIndex === idx ? (roundCorrect ? ' cell-hit' : ' cell-miss') : '')"
             :style="'width:' + cellSize + 'px;height:' + cellSize + 'px'"
             @click="handleChoice(sym)"
@@ -131,10 +131,6 @@ export default {
   },
 
   methods: {
-    getSymKey(sym) {
-      return sym
-    },
-
     clearTimers() {
       if (this.showTimer) {
         clearTimeout(this.showTimer)

+ 1 - 1
cfc-frontend/pages/diet/records.vue

@@ -2,7 +2,7 @@
   <view class="page">
     <view class="member-bar" v-if="memberList.length > 1">
       <view class="member-tab" :class="{'active': currentMemberId === (item.id || item.userId)}"
-        v-for="item in memberList" :key="item.id || item.userId"
+        v-for="(item, mi) in memberList" :key="mi"
         @tap="switchMember(item)">
         <text class="member-name">{{ item.nickname || item.roleLabel || '全部' }}</text>
       </view>

+ 2 - 5
cfc-frontend/pages/discover-detail/product-detail/product-detail.vue

@@ -30,8 +30,8 @@
           <text class="gift-select-title">选择赠品(可多选)</text>
           <view class="gift-select-list">
             <view
-              v-for="item in giftItems"
-              :key="getGiftItemKey(item)"
+              v-for="(item, gi) in giftItems"
+              :key="gi"
               :class="item._selected ? 'gift-select-item gift-select-item-active' : 'gift-select-item'"
               @click="toggleGift(item)"
             >
@@ -532,9 +532,6 @@ export default {
       var map = { 1: '快递配送', 2: '到店自提', 3: '快递/自提', 4: '线上服务', 5: '线上预约' }
       return map[method] || '快递配送'
     },
-    getGiftItemKey: function(item) {
-      return item.id
-    },
     toggleGift: function(item) {
       this.$set(item, '_selected', !item._selected)
     },

+ 6 - 12
cfc-frontend/pages/discover/circles.vue

@@ -30,8 +30,8 @@
         </view>
         <view class="circle-grid">
           <view
-            v-for="item in myCircles"
-            :key="getCircleKey(item)"
+            v-for="(item, ci) in myCircles"
+            :key="ci"
             class="circle-item">
             <view class="circle-card-wrap" @tap="onCircleClick(item)">
               <CircleCard :circle="item" compact />
@@ -51,8 +51,8 @@
         </view>
         <view class="pending-list">
           <view
-            v-for="pm in pendingMembers"
-            :key="getPendingKey(pm)"
+            v-for="(pm, pmi) in pendingMembers"
+            :key="pmi"
             class="pending-item">
             <image
               class="pending-avatar"
@@ -84,8 +84,8 @@
         </view>
         <view class="circle-grid" v-if="discoverList.length > 0">
           <view
-            v-for="item in discoverList"
-            :key="getCircleKey(item)"
+            v-for="(item, ci) in discoverList"
+            :key="ci"
             class="circle-item"
             @tap="onCircleClick(item)">
             <CircleCard :circle="item" />
@@ -178,12 +178,6 @@ export default {
     this.loadData()
   },
   methods: {
-    getCircleKey: function(item) {
-      return item.id || item.circleId
-    },
-    getPendingKey: function(item) {
-      return (item.circleId || '') + '_' + (item.memberId || '')
-    },
     formatAppliedTime: function(timeStr) {
       var d = parseDate(timeStr)
       if (!d) return ''

+ 5 - 5
cfc-frontend/pages/growth-main/index.vue

@@ -361,7 +361,7 @@ export default {
           if (self.formatDateKey(dr.createdAt || dr.recordDate) === self.todayDate) dietDone = true
         }
       } catch(e) {}
-      items.push({ key: 'diet', icon: '🍽️', label: '饮食记录', done: dietDone, actionText: '补录' })
+      items.push({ key: 'diet', icon: '🍽️', label: '饮食', done: dietDone, actionText: '补录' })
       var exerciseDone = false
       try {
         var eParams = self.memberId ? { memberId: self.memberId } : {}
@@ -373,7 +373,7 @@ export default {
           if (self.formatDateKey(er.startTime || er.createdAt) === self.todayDate) exerciseDone = true
         }
       } catch(e) {}
-      items.push({ key: 'exercise', icon: '🏃', label: '运动记录', done: exerciseDone, actionText: '记录' })
+      items.push({ key: 'exercise', icon: '🏃', label: '运动', done: exerciseDone, actionText: '记录' })
       var sleepDone = false
       try {
         var sParams = self.memberId ? { memberId: self.memberId } : {}
@@ -385,7 +385,7 @@ export default {
           if (self.formatDateKey(sr.sleepTime || sr.createdAt) === self.todayDate) sleepDone = true
         }
       } catch(e) {}
-      items.push({ key: 'sleep', icon: '😴', label: '睡眠记录', done: sleepDone, actionText: '记录' })
+      items.push({ key: 'sleep', icon: '😴', label: '睡眠', done: sleepDone, actionText: '记录' })
       var emotionDone = false
       try {
         var mParams = { page: 1, size: 10 }
@@ -398,7 +398,7 @@ export default {
           if (self.formatDateKey(mr.checkinDate || mr.createdAt) === self.todayDate) emotionDone = true
         }
       } catch(e) {}
-      items.push({ key: 'emotion', icon: '😊', label: '心情记录', done: emotionDone, actionText: '记录' })
+      items.push({ key: 'emotion', icon: '😊', label: '心情', done: emotionDone, actionText: '记录' })
       var checkinDone = false
       try {
         var cParams = self.memberId ? { memberId: self.memberId } : {}
@@ -411,7 +411,7 @@ export default {
           }
         }
       } catch(e) {}
-      items.push({ key: 'checkin', icon: '💰', label: '记账打卡', done: checkinDone, actionText: '去打卡' })
+      items.push({ key: 'checkin', icon: '💰', label: '记账', done: checkinDone, actionText: '去打卡' })
       self.progressItems = items.filter(function(it) { return it && it.key })
       self.incompleteCount = 0
       for (var pi = 0; pi < items.length; pi++) if (items[pi] && !items[pi].done) self.incompleteCount++

+ 2 - 4
cfc-frontend/pages/growth/caution-detail.vue

@@ -8,14 +8,14 @@
 
     <view class="member-bar" v-if="memberList.length > 1">
       <view class="member-tab" :class="{'active': currentMemberId === (item.id || item.userId)}"
-        v-for="item in memberList" :key="getMemberKey(item)"
+        v-for="(item, mi) in memberList" :key="mi"
         @tap="switchMember(item)">
         <text class="member-name">{{ item.nickname || item.roleLabel || '全部' }}</text>
       </view>
     </view>
 
     <scroll-view class="content" scroll-y :refresher-enabled="true" :refresher-triggered="refreshing" @refresherrefresh="onRefresh">
-      <view class="food-card" v-for="(item, idx) in foods" :key="getItemKey(item)">
+      <view class="food-card" v-for="(item, idx) in foods" :key="idx">
         <view class="food-header">
           <text class="food-name">{{ item.foodName }}</text>
           <view class="score-badge-group">
@@ -89,8 +89,6 @@ export default {
     this.loadFoods()
   },
   methods: {
-    getItemKey: function(item) { return item.foodId || item.id || Math.random() },
-    getMemberKey: function(item) { return item.id || item.userId || 'all' },
     goBack: function() {
       uni.navigateBack()
     },

+ 8 - 1
cfc-frontend/pages/growth/diet-checkin.vue

@@ -4,7 +4,7 @@
     <view class="nav-bar">
       <view class="nav-left" @tap="goBack">
         <text class="nav-icon">‹</text>
-        <text class="nav-title">饮食打卡</text>
+        <text class="nav-title">{{ todayText }}</text>
       </view>
     </view>
 
@@ -149,6 +149,7 @@ export default {
       recordTimer: null,
       recorderManager: null,
       history: [],
+      todayText: '',
       mealTypes: [
         { label: '早餐', value: 'breakfast' },
         { label: '午餐', value: 'lunch' },
@@ -176,6 +177,7 @@ export default {
     }
   },
     onLoad: function(options) {
+    this.todayText = this.getToday()
     var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
     if (role !== 'child') {
       this.memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || null
@@ -219,6 +221,11 @@ export default {
     if (this.recordTimer) clearInterval(this.recordTimer)
   },
   methods: {
+    getToday: function() {
+      var d = new Date()
+      var weekdays = ['日', '一', '二', '三', '四', '五', '六']
+      return d.getFullYear() + '年' + (d.getMonth() + 1) + '月' + d.getDate() + '日 周' + weekdays[d.getDay()]
+    },
     goBack: function() { uni.navigateBack() },
     mealTypeLabel: function(type) {
       var m = { breakfast: '早餐', lunch: '午餐', dinner: '晚餐', snack: '加餐' }

+ 8 - 1
cfc-frontend/pages/growth/exercise-checkin.vue

@@ -4,7 +4,7 @@
     <view class="nav-bar">
       <view class="nav-left" @tap="goBack">
         <text class="nav-icon">‹</text>
-        <text class="nav-title">运动打卡</text>
+        <text class="nav-title">{{ todayText }}</text>
       </view>
     </view>
 
@@ -157,6 +157,7 @@ export default {
       recordTimer: null,
       recorderManager: null,
       history: [],
+      todayText: '',
       exerciseTypes: [
         { label: '跑步', value: 'running' },
         { label: '游泳', value: 'swimming' },
@@ -191,6 +192,7 @@ export default {
     }
   },
   onLoad: function() {
+    this.todayText = this.getToday()
     var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
     if (role !== 'child') {
       this.memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || null
@@ -227,6 +229,11 @@ export default {
     if (this.recordTimer) clearInterval(this.recordTimer)
   },
   methods: {
+    getToday: function() {
+      var d = new Date()
+      var weekdays = ['日', '一', '二', '三', '四', '五', '六']
+      return d.getFullYear() + '年' + (d.getMonth() + 1) + '月' + d.getDate() + '日 周' + weekdays[d.getDay()]
+    },
     goBack: function() { uni.navigateBack() },
     exerciseTypeLabel: function(type) {
       var m = { running: '跑步', swimming: '游泳', jump_rope: '跳绳', ball: '球类', cycling: '骑行', gymnastics: '体操', other: '其他' }

+ 8 - 1
cfc-frontend/pages/growth/sleep-checkin.vue

@@ -4,7 +4,7 @@
     <view class="nav-bar">
       <view class="nav-left" @tap="goBack">
         <text class="nav-icon">‹</text>
-        <text class="nav-title">睡眠打卡</text>
+        <text class="nav-title">{{ todayText }}</text>
       </view>
     </view>
 
@@ -98,6 +98,7 @@ export default {
       memberId: null,
       submitting: false,
       history: [],
+      todayText: '',
       form: {
         sleepTime: '21:00',
         wakeTime: '07:00',
@@ -107,6 +108,7 @@ export default {
     }
   },
   onLoad: function() {
+    this.todayText = this.getToday()
     var role = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
     if (role !== 'child') {
       this.memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || null
@@ -114,6 +116,11 @@ export default {
     this.loadHistory()
   },
   methods: {
+    getToday: function() {
+      var d = new Date()
+      var weekdays = ['日', '一', '二', '三', '四', '五', '六']
+      return d.getFullYear() + '年' + (d.getMonth() + 1) + '月' + d.getDate() + '日 周' + weekdays[d.getDay()]
+    },
     goBack: function() { uni.navigateBack() },
     formatDate: function(dateStr) {
       if (!dateStr) return ''

+ 2 - 5
cfc-frontend/pages/growth/upload.vue

@@ -24,8 +24,8 @@
         <scroll-view class="purchase-scroll" scroll-x enable-flex show-scrollbar="false">
           <view
             class="purchase-card"
-            v-for="item in recommendedProducts"
-            :key="getProductKey(item)"
+            v-for="(item, pi) in recommendedProducts"
+            :key="pi"
             @click="goToProduct(item.id)"
           >
             <image class="purchase-cover" :src="getImageUrl(item.coverImage) || '/static/default-product.png'" mode="aspectFill" />
@@ -276,9 +276,6 @@ export default {
     },
     getImageUrl: function(path) {
       return config.API_BASE_URL + path
-    },
-    getProductKey: function(item) {
-      return item.id
     }
   }
 }

+ 1 - 2
cfc-frontend/pages/health-main/index.vue

@@ -20,7 +20,7 @@
         </view>
       </view>
       <!-- 本期待填调研(按周期计算,服务端返回 pending 列表) -->
-      <view v-for="item in pendingSurveys" :key="surveyKey(item)" class="self-test-card" @click="goSurveyFill(item)">
+      <view v-for="(item, si) in pendingSurveys" :key="si" class="self-test-card" @click="goSurveyFill(item)">
         <view class="self-test-head">
           <text class="self-test-icon">📋</text>
           <view class="self-test-info">
@@ -514,7 +514,6 @@ export default {
         self.pendingSurveys = pending
       }).catch(function() { self.pendingSurveys = [] })
     },
-    surveyKey: function(item) { return item && item.templateId ? item.templateId : Math.random() },
     cycleLabel: function(cycleType) {
       var map = { weekly: '每周调研', biweekly: '双周调研', monthly: '每月调研' }
       return map[cycleType] || '定期调研'

+ 2 - 4
cfc-frontend/pages/health/gut-flora-foods-detail.vue

@@ -3,7 +3,7 @@
 
     <view class="member-bar" v-if="memberList.length > 1">
       <view class="member-tab" :class="{'active': currentMemberId === (item.id || item.userId)}"
-        v-for="item in memberList" :key="getMemberKey(item)"
+        v-for="(item, mi) in memberList" :key="mi"
         @tap="switchMember(item)">
         <text class="member-name">{{ item.nickname || item.roleLabel || '全部' }}</text>
       </view>
@@ -48,7 +48,7 @@
         class="food-card"
         :class="item.bandClass"
         v-for="(item, idx) in filteredFoods"
-        :key="getItemKey(item)"
+        :key="idx"
         @tap="showFoodKnowledge(item)"
       >
         <view class="food-header">
@@ -201,8 +201,6 @@ export default {
     this.loadData()
   },
 methods: {
-  getMemberKey: function(item) { return item.id || item.userId || 'all' },
-  getItemKey: function(item) { return item.foodId || item.id || item.name || Math.random() },
     goBack: function() { uni.navigateBack() },
     switchMember: function(item) {
       this.currentMemberId = item.id || item.userId

+ 2 - 4
cfc-frontend/pages/health/survey-questionnaire.vue

@@ -5,13 +5,13 @@
       <text class="header-dimension" v-if="templateDimension">适用维度: {{ templateDimension }}</text>
       <text class="header-period" v-if="periodStart || periodEnd">周期: {{ periodStart && formatDate(periodStart) }} - {{ periodEnd && formatDate(periodEnd) }}</text>
     </view>
-    <view class="question-section" v-for="(q, qIdx) in questions" :key="getQuestionKey(q)">
+    <view class="question-section" v-for="(q, qIdx) in questions" :key="qIdx">
       <view class="question-header">
         <text class="question-num">Q{{ qIdx + 1 }}</text>
         <text class="question-text">{{ q.text }}</text>
       </view>
       <view class="options-list">
-        <view class="option-item" :class="answers[qIdx] === opt ? 'option-selected' : ''" v-for="(opt, oIdx) in q.options" :key="getOptionKey(q, opt)" @click="selectOption(qIdx, opt)">
+        <view class="option-item" :class="answers[qIdx] === opt ? 'option-selected' : ''" v-for="(opt, oIdx) in q.options" :key="oIdx" @click="selectOption(qIdx, opt)">
           <view class="option-radio" :class="answers[qIdx] === opt ? 'radio-checked' : ''"></view>
           <text class="option-text">{{ opt }}</text>
         </view>
@@ -38,8 +38,6 @@ export default {
     if (this.templateId || options.dim) this.loadTemplate()
   },
   methods: {
-    getQuestionKey(q) { return q && q.text ? q.text : 'q-' + Math.random() },
-    getOptionKey(q, opt) { return q && q.text ? q.text + '-' + opt : 'opt-' + Math.random() },
     formatDate(s) { return s ? s.substring(0, 10) : '' },
     selectOption(qIdx, opt) { this.$set(this.answers, qIdx, opt) },
     async loadTemplate() {

+ 1 - 4
cfc-frontend/pages/health/teacher-service.vue

@@ -38,7 +38,7 @@
 
     <!-- 规划师列表 -->
     <view class="ts-list" v-if="guides.length > 0">
-      <view v-for="(g, idx) in guides" :key="getGuideKey(g, idx)"
+      <view v-for="(g, idx) in guides" :key="idx"
             class="ts-card" :class="{ active: selectedGuideId === g.id }"
             @click="selectGuide(g)">
         <view class="ts-card-top">
@@ -115,9 +115,6 @@ export default {
     this.loadGuides()
   },
   methods: {
-    getGuideKey: function(g, idx) {
-      return g.id || ('g' + idx)
-    },
     loadGuides: function() {
       var self = this
       self.loading = true

+ 1 - 4
cfc-frontend/pages/notification/index.vue

@@ -8,7 +8,7 @@
     </view>
 
     <view class="nt-list" v-else>
-      <view class="nt-item" v-for="item in list" :key="getKey(item)" @click="onTap(item)">
+      <view class="nt-item" v-for="(item, ni) in list" :key="ni" @click="onTap(item)">
         <view class="nt-item-icon">
           <text class="nt-icon">{{ getIcon(item.type) }}</text>
           <view class="nt-unread" v-if="item.is_read === 0 || item.isRead === 0"></view>
@@ -52,9 +52,6 @@ export default {
     this.loadList()
   },
   methods: {
-    getKey(item) {
-      return item.id || Math.random()
-    },
     getIcon(type) {
       if (type === 'report_ready') return '📋'
       if (type === 'reward_granted') return '🎁'

+ 1 - 4
cfc-frontend/pages/profile-extra/coupons.vue

@@ -23,7 +23,7 @@
       <view v-if="tabIndex === 3" class="coupon-list">
         <view
           v-for="(item, index) in exchangeableList"
-          :key="getCouponKey(item, index)"
+          :key="index"
           class="coupon-card"
         >
           <view class="card-left">
@@ -196,9 +196,6 @@ export default {
         }
       })
     },
-    getCouponKey(item, index) {
-      return item.id + '-' + index
-    },
     getCouponDesc(item) {
       if (item.type === 'DISCOUNT') {
         return '折扣券 ' + (item.discountRate / 100).toFixed(1) + '折'

+ 4 - 4
cfc-frontend/pages/profile-extra/family-members.vue

@@ -17,7 +17,7 @@
         <text class="jr-title">📨 加入申请</text>
         <text class="jr-count">{{ joinRequests.length }} 条待确认</text>
       </view>
-      <view class="jr-item" v-for="r in joinRequests" :key="getJoinRequestKey(r)">
+      <view class="jr-item" v-for="r in joinRequests" :key="r.id">
         <image class="jr-avatar" :src="r.requesterAvatar || '/static/default-avatar.png'" mode="aspectFill"></image>
         <view class="jr-info">
           <text class="jr-name">{{ r.requesterNickname || '微信用户' }}</text>
@@ -545,11 +545,11 @@ export default {
         this.joinRequests = []
       }
     },
-    getJoinRequestKey(r) {
-      return r.id || r.requesterUserId
-    },
     onHandleJoinRequest(r, approve) {
       var self = this
+      if (!r) {
+        return
+      }
       var nickname = r.requesterNickname || '该用户'
       uni.showModal({
         title: approve ? '同意加入' : '拒绝申请',

+ 3 - 6
cfc-frontend/pages/profile-extra/gates.vue

@@ -3,7 +3,7 @@
   <view class="page-section" v-if="mainGates.length > 0">
     <text class="section-title">主线关卡</text>
     <view class="gate-list">
-      <view class="gate-card" v-for="(g, i) in mainGates" :key="getMainGateKey(g, i)"
+      <view class="gate-card" v-for="(g, i) in mainGates" :key="i"
         :class="'gate-card--' + (g.status === 'UNLOCKED' ? 'done' : (g.status === 'CURRENT' ? 'current' : 'locked'))">
         <view class="gate-status-icon">
           <text v-if="g.status === 'UNLOCKED'">✅</text>
@@ -32,7 +32,7 @@
     <text class="section-title">推荐挑战</text>
     <view class="gate-list">
       <view class="gate-card" :class="'gate-card--' + (g.status === 'UNLOCKED' ? 'done' : 'current')"
-        v-for="(g, i) in softGates" :key="getSoftGateKey(g, i)">
+        v-for="(g, i) in softGates" :key="i">
         <view class="gate-status-icon">
           <text v-if="g.status === 'UNLOCKED'">✅</text>
           <text v-else>🎯</text>
@@ -57,7 +57,7 @@
   <view class="page-section" v-if="valueGates.length > 0">
     <text class="section-title">增值关卡</text>
     <view class="gate-list">
-      <view class="gate-card gate-card--value" v-for="(g, i) in valueGates" :key="getValueGateKey(g, i)">
+      <view class="gate-card gate-card--value" v-for="(g, i) in valueGates" :key="i">
         <view class="gate-status-icon">
           <text v-if="g.status === 'UNLOCKED'">✅</text>
           <text v-else>💎</text>
@@ -122,9 +122,6 @@ export default {
         self.loading = false
       })
     },
-    getMainGateKey: function(g, i) { return g.id || i },
-    getSoftGateKey: function(g, i) { return 'soft' + (g.id || i) },
-    getValueGateKey: function(g, i) { return 'val' + (g.id || i) },
     goGateAction: function(g) {
       var type = g.gateType
       var url = ''

+ 1 - 4
cfc-frontend/pages/wealth-sub/checkin.vue

@@ -26,7 +26,7 @@
         <view class="day-cell empty" v-for="(e, i) in emptyDays" :key="getEmptyKey(i)"></view>
         <view
           v-for="(day, i) in daysInMonth"
-          :key="getDayKey(i)"
+          :key="i"
           class="day-cell"
           :class="{
             checked: checkinDates[day],
@@ -425,9 +425,6 @@ export default {
     },
     getEmptyKey: function(i) {
       return 'e' + i
-    },
-    getDayKey: function(i) {
-      return 'd' + i
     }
   }
 }

+ 1 - 1
cfc-frontend/pages/wealth/index.vue

@@ -291,7 +291,7 @@ export default {
       weekDays: ['一', '二', '三', '四', '五', '六', '日'],
       // 服务列表
       serviceList: [
-        { icon: '📝', label: '记账打卡', action: 'goToCheckin' },
+        { icon: '📝', label: '记账', action: 'goToCheckin' },
         { icon: '🛡️', label: '保单管理', action: 'goToInsurance' },
         { icon: '🎨', label: '创客中心', action: 'goToCreator' },
         { icon: '🏪', label: '商城首页', action: 'goToShop' },