Browse Source

fix(health-plan): ensure member IDs are always strings for WeChat mini-program event system

- In WeChat mini-programs, event handler args are converted to strings via dataset
- Template expressions pass raw numbers, causing type mismatch in indexOf
- Always convert to String() in isMemberSelected, toggleMember, loadExecutors
- Prevent double loadExecutors with synchronous _executorsLoading flag

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
liaoxg 3 tuần trước cách đây
mục cha
commit
f0c53a79ea
1 tập tin đã thay đổi với 47 bổ sung49 xóa
  1. 47 49
      cfc-frontend/pages/health/health-plan-summary.vue

+ 47 - 49
cfc-frontend/pages/health/health-plan-summary.vue

@@ -16,15 +16,13 @@
         <text class="selected-count-badge" v-if="selectedMemberIds.length > 0">{{ selectedMemberIds.length }} 人已选</text>
       </view>
       <view class="hint-text">选择方案针对的家庭成员</view>
-      <view class="member-list">
+      <view class="member-grid">
         <view v-for="(m, idx) in executors" :key="getMemberKey(m, idx)"
-              class="member-card" :class="{ active: isMemberSelected(m.id) }"
+              class="member-tile" :class="{ active: isMemberSelected(m.id) }"
               @click="toggleMember(m.id)">
-          <view class="member-card-left">
-            <view class="member-avatar">{{ (m.nickname || m.name || '成').charAt(0) }}</view>
-            <text class="member-card-name">{{ m.nickname || m.name || '成员' }}</text>
-          </view>
-          <view class="member-check">{{ isMemberSelected(m.id) ? '✓' : '' }}</view>
+          <view class="member-tile-avatar">{{ (m.nickname || m.name || '成').charAt(0) }}</view>
+          <text class="member-tile-name">{{ m.nickname || m.name || '成员' }}</text>
+          <view class="member-tile-check" v-if="isMemberSelected(m.id)">✓</view>
         </view>
         <view v-if="executors.length === 0" class="empty-state">
           <text class="empty-icon">👨‍👩‍👧</text>
@@ -460,7 +458,7 @@ export default {
           var list = res.data instanceof Array ? res.data : []
           self.executors = list.filter(function(m) { return m.id })
           if (self.selectedMemberIds.length === 0 && self.executors.length > 0) {
-            self.selectedMemberIds = [self.executors[0].id]
+            self.selectedMemberIds = [String(self.executors[0].id)]
           }
         }
         self._executorsLoaded = true
@@ -468,12 +466,13 @@ export default {
       }).catch(function() { self.executors = []; self._executorsLoaded = true; self._executorsLoading = false })
     },
     isMemberSelected: function(id) {
-      return this.selectedMemberIds.indexOf(id) >= 0
+      return this.selectedMemberIds.indexOf(String(id)) >= 0
     },
     toggleMember: function(id) {
-      var idx = this.selectedMemberIds.indexOf(id)
+      var strId = String(id)
+      var idx = this.selectedMemberIds.indexOf(strId)
       if (idx >= 0) this.selectedMemberIds.splice(idx, 1)
-      else this.selectedMemberIds.push(id)
+      else this.selectedMemberIds.push(strId)
     },
 
     // ===== 时间格式化 =====
@@ -983,73 +982,72 @@ export default {
 }
 
 /* 成员卡片 */
-.member-list {
+.member-grid {
   display: flex;
-  flex-direction: column;
-  gap: 12rpx;
+  flex-wrap: wrap;
+  gap: 20rpx;
   margin-bottom: 24rpx;
 }
-.member-card {
+.member-tile {
+  position: relative;
+  width: calc(33.333% - 14rpx);
   display: flex;
+  flex-direction: column;
   align-items: center;
-  justify-content: space-between;
-  padding: 20rpx 24rpx;
-  border-radius: 16rpx;
+  padding: 28rpx 12rpx 20rpx;
+  border-radius: 20rpx;
   border: 3rpx solid #e8e8e8;
-  border-left: 8rpx solid transparent;
   background: #FAFAFA;
   transition: all 0.2s;
 }
-.member-card.active {
+.member-tile.active {
   border-color: #F97316;
-  border-left: 8rpx solid #F97316;
   background: #FFFBF0;
+  box-shadow: 0 4rpx 16rpx rgba(249, 115, 22, 0.15);
 }
-.member-card-left {
-  display: flex;
-  align-items: center;
-  gap: 16rpx;
-}
-.member-avatar {
-  width: 60rpx;
-  height: 60rpx;
-  border-radius: 30rpx;
+.member-tile-avatar {
+  width: 96rpx;
+  height: 96rpx;
+  border-radius: 50%;
   background: #ececec;
-  border: 2rpx solid #ddd;
+  border: 3rpx solid #ddd;
   color: #888;
-  font-size: 28rpx;
+  font-size: 36rpx;
   font-weight: bold;
   display: flex;
   align-items: center;
   justify-content: center;
-  flex-shrink: 0;
+  margin-bottom: 12rpx;
 }
-.member-card.active .member-avatar {
+.member-tile.active .member-tile-avatar {
   background: #F97316;
   border-color: #F97316;
   color: #fff;
 }
-.member-card-name {
-  font-size: 28rpx;
+.member-tile-name {
+  font-size: 26rpx;
   font-weight: 600;
   color: #333;
+  max-width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
-.member-card.active .member-card-name { color: #F97316; }
-.member-check {
-  width: 44rpx;
-  height: 44rpx;
-  border-radius: 50%;
-  border: 3rpx solid #d5d5d5;
-  background: #fff;
-  color: transparent;
-  font-size: 24rpx;
-  line-height: 38rpx;
-  text-align: center;
+.member-tile.active .member-tile-name {
+  color: #F97316;
 }
-.member-card.active .member-check {
+.member-tile-check {
+  position: absolute;
+  top: 8rpx;
+  right: 8rpx;
+  width: 36rpx;
+  height: 36rpx;
+  border-radius: 50%;
   background: #F97316;
-  border-color: #F97316;
   color: #fff;
+  font-size: 20rpx;
+  line-height: 36rpx;
+  text-align: center;
 }
 
 /* 目标输入 */