Преглед на файлове

fix(health-plan-summary): member toggle + race condition + generate string data

- Normalize all memberIds to String consistently (executors, selectedMemberIds, isMemberSelected, toggleMember)
- Prevent onLoad + onShow double-load race with _executorsLoaded flag
- Only loadExecutors in onLoad when not in planId/viewMode path
- Handle generateHealthPlan API returning data as markdown string (typeof check)
- Reset selectedMemberIds and userGoal in reCreate()

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
liaoxg преди 2 месеца
родител
ревизия
6bcb2d2829
променени са 1 файла, в които са добавени 25 реда и са изтрити 9 реда
  1. 25 9
      cfc-frontend/pages/health/health-plan-summary.vue

+ 25 - 9
cfc-frontend/pages/health/health-plan-summary.vue

@@ -37,13 +37,13 @@
       <view class="hint-text">选择方案针对的家庭成员</view>
       <view class="hint-text">选择方案针对的家庭成员</view>
       <view class="member-list">
       <view class="member-list">
         <view v-for="(m, idx) in executors" :key="getMemberKey(m, idx)"
         <view v-for="(m, idx) in executors" :key="getMemberKey(m, idx)"
-              class="member-card" :class="{ active: selectedMemberIds.indexOf(m.memberId) >= 0 }"
+              class="member-card" :class="{ active: isMemberSelected(m.memberId) }"
               @click="toggleMember(m.memberId)">
               @click="toggleMember(m.memberId)">
           <view class="member-card-left">
           <view class="member-card-left">
             <view class="member-avatar">{{ m.name.charAt(0) }}</view>
             <view class="member-avatar">{{ m.name.charAt(0) }}</view>
             <text class="member-card-name">{{ m.name }}</text>
             <text class="member-card-name">{{ m.name }}</text>
           </view>
           </view>
-          <view class="member-check">{{ selectedMemberIds.indexOf(m.memberId) >= 0 ? '✓' : '' }}</view>
+          <view class="member-check">{{ isMemberSelected(m.memberId) ? '✓' : '' }}</view>
         </view>
         </view>
         <view v-if="executors.length === 0" class="empty-state">
         <view v-if="executors.length === 0" class="empty-state">
           <text class="empty-icon">👨‍👩‍👧</text>
           <text class="empty-icon">👨‍👩‍👧</text>
@@ -217,13 +217,15 @@ export default {
   },
   },
   onLoad: function(options) {
   onLoad: function(options) {
     if (options.initialMemberIds) {
     if (options.initialMemberIds) {
+      var self = this
       this.selectedMemberIds = options.initialMemberIds.split(',').filter(function(id) { return id })
       this.selectedMemberIds = options.initialMemberIds.split(',').filter(function(id) { return id })
     }
     }
     this.planId = options.planId ? parseInt(options.planId) : null
     this.planId = options.planId ? parseInt(options.planId) : null
-    this.loadExecutors()
     if (this.planId) {
     if (this.planId) {
       this.viewMode = true
       this.viewMode = true
       this.loadPlanDetail()
       this.loadPlanDetail()
+    } else {
+      this.loadExecutors()
     }
     }
     if (options.autoExecute === '1') {
     if (options.autoExecute === '1') {
       var self = this
       var self = this
@@ -231,7 +233,9 @@ export default {
     }
     }
   },
   },
   onShow: function() {
   onShow: function() {
-    this.loadExecutors()
+    if (!this._executorsLoaded) {
+      this.loadExecutors()
+    }
   },
   },
   methods: {
   methods: {
     getMemberKey: function(m, idx) { return m.memberId || idx },
     getMemberKey: function(m, idx) { return m.memberId || idx },
@@ -241,18 +245,25 @@ export default {
         if (res && res.code === 200 && res.data) {
         if (res && res.code === 200 && res.data) {
           var list = res.data instanceof Array ? res.data : []
           var list = res.data instanceof Array ? res.data : []
           self.executors = list.filter(function(m) { return m.memberId || m.id }).map(function(m) {
           self.executors = list.filter(function(m) { return m.memberId || m.id }).map(function(m) {
-            return { memberId: m.memberId || m.id, name: m.nickname || m.name || '成员' }
+            return { memberId: String(m.memberId || m.id), name: m.nickname || m.name || '成员' }
           })
           })
           if (self.selectedMemberIds.length === 0 && self.executors.length > 0) {
           if (self.selectedMemberIds.length === 0 && self.executors.length > 0) {
             self.selectedMemberIds = [self.executors[0].memberId]
             self.selectedMemberIds = [self.executors[0].memberId]
+          } else {
+            self.selectedMemberIds = self.selectedMemberIds.map(function(id) { return String(id) })
           }
           }
         }
         }
-      }).catch(function() { self.executors = [] })
+        self._executorsLoaded = true
+      }).catch(function() { self.executors = []; self._executorsLoaded = true })
+    },
+    isMemberSelected: function(memberId) {
+      return this.selectedMemberIds.indexOf(String(memberId)) >= 0
     },
     },
     toggleMember: function(memberId) {
     toggleMember: function(memberId) {
-      var idx = this.selectedMemberIds.indexOf(memberId)
+      var id = String(memberId)
+      var idx = this.selectedMemberIds.indexOf(id)
       if (idx >= 0) this.selectedMemberIds.splice(idx, 1)
       if (idx >= 0) this.selectedMemberIds.splice(idx, 1)
-      else this.selectedMemberIds.push(memberId)
+      else this.selectedMemberIds.push(id)
     },
     },
     formatTime: function(iso) {
     formatTime: function(iso) {
       if (!iso) return ''
       if (!iso) return ''
@@ -275,7 +286,10 @@ export default {
         self.loading = false
         self.loading = false
         if (res && res.code === 200 && res.data) {
         if (res && res.code === 200 && res.data) {
           var data = res.data
           var data = res.data
-          if (data.overview) {
+          if (typeof data === 'string') {
+            self.planData = { overview: data }
+            self.step = 2
+          } else if (data.overview) {
             self.planData = data
             self.planData = data
             if (data.sections && data.sections instanceof Array) {
             if (data.sections && data.sections instanceof Array) {
               data.sections.forEach(function(sec) {
               data.sections.forEach(function(sec) {
@@ -453,6 +467,8 @@ export default {
       this.taskSubmitted = false
       this.taskSubmitted = false
       this.editing = false
       this.editing = false
       this.sectionFeedback = ''
       this.sectionFeedback = ''
+      this.selectedMemberIds = []
+      this.userGoal = ''
       this.planData = {}
       this.planData = {}
       this.sections = {
       this.sections = {
         nutrition: { content: '', confirmed: false },
         nutrition: { content: '', confirmed: false },