Explorar o código

fix: cognitive-hall.vue goReport routes to /pages/wisdom-detail/cognitive-report (was referencing now-deleted /pages/wisdom/cognitive-report); deleted 13 duplicate orphan files in mind/ wisdom/ body/

Sisyphus Agent hai 2 días
pai
achega
3c2d4f621a

+ 0 - 245
cfc-frontend/pages/body/member-body-detail.vue

@@ -1,245 +0,0 @@
-<template>
-  <view class="detail-container">
-    <!-- 顶部导航 -->
-
-    <!-- 五维能量条 -->
-    <FamilyEnergyBar
-      dimensionCode="body"
-      :sandboxData="sandboxData"
-      :dualDimension="dualDimension" />
-
-    <!-- 成员信息卡 -->
-    <view class="member-card" v-if="memberInfo">
-      <view class="member-avatar">
-        <text class="avatar-text">{{ memberInfo.name && memberInfo.name.charAt(0) || '孩' }}</text>
-      </view>
-      <view class="member-info">
-        <text class="member-name">{{ memberInfo.name || '孩子' }}</text>
-        <text class="member-role">身体维度</text>
-      </view>
-      <view class="member-score" v-if="dualDimension">
-        <text class="score-value">{{ dualDimension.energy || 0 }}</text>
-        <text class="score-label">能量值</text>
-      </view>
-    </view>
-
-    <!-- 家庭成员关系图谱(只读) -->
-    <FamilyRelationGraph
-      dimensionCode="body"
-      :selfId="selfId"
-      :members="graphMembers"
-      :energyMap="energyMapForGraph"
-      :intimacyMap="intimacyMapForGraph"
-      :interactive="false" />
-
-    <!-- 今日任务 -->
-    <DimensionTasks
-      :memberId="memberId"
-      @taskClick="onTaskClick"
-      @moreTasks="goTasks" />
-
-    <!-- 活动 -->
-    <DimensionActivities
-      :isLoggedIn="true"
-      @activityClick="goActivityDetail"
-      @moreActivities="goMoreActivities" />
-
-    <!-- 商品 -->
-    <DimensionProducts
-      :isLoggedIn="true"
-      @productClick="goProductDetail"
-      @moreProducts="goMoreProducts" />
-
-    <!-- 底部占位 -->
-    <view class="bottom-spacer"></view>
-  </view>
-</template>
-
-<script>
-import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
-import FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
-import DimensionTasks from '../../components/DimensionTasks.vue'
-import DimensionActivities from '../../components/DimensionActivities.vue'
-import DimensionProducts from '../../components/DimensionProducts.vue'
-import { getEnergyOverview, getFamilyEnergySandbox, getChildren } from '../../utils/api.js'
-
-export default {
-  components: { FamilyEnergyBar, FamilyRelationGraph, DimensionTasks, DimensionActivities, DimensionProducts },
-  data() {
-    return {
-      memberId: null,
-      selfId: null,
-      memberInfo: null,
-      sandboxData: null,
-      dualDimension: null
-    }
-  },
-  computed: {
-    graphMembers: function() {
-      if (!this.sandboxData || !this.sandboxData.members) return []
-      return this.sandboxData.members.map(function(m) {
-        return {
-          id: m.memberId || m.id,
-          nickname: m.name || m.nickname || '成员',
-          memberType: m.memberType || 'child'
-        }
-      })
-    },
-    energyMapForGraph: function() {
-      var map = {}
-      if (this.sandboxData && this.sandboxData.members) {
-        for (var i = 0; i < this.sandboxData.members.length; i++) {
-          var m = this.sandboxData.members[i]
-          map[m.memberId || m.id] = {
-            bodyScore: m.bodyScore || 0,
-            mindScore: m.mindScore || 0,
-            actionScore: m.actionScore || 0
-          }
-        }
-      }
-      return map
-    },
-    intimacyMapForGraph: function() {
-      return {}
-    }
-  },
-  onLoad: function(options) {
-    this.memberId = options.memberId || uni.getStorageSync('currentChildId') || null
-  },
-  onShow: function() {
-    this.selfId = uni.getStorageSync('userId') || null
-    this.loadChildren()
-    this.loadSandboxData()
-    if (this.memberId) {
-      this.loadEnergyData()
-    }
-  },
-  methods: {
-    loadChildren: function() {
-      var self = this
-      getChildren().then(function(res) {
-        if (res.code === 200 && res.data) {
-          for (var i = 0; i < res.data.length; i++) {
-            if (res.data[i].memberId === self.memberId) {
-              self.memberInfo = res.data[i]
-              break
-            }
-          }
-          if (!self.memberInfo && res.data.length > 0) {
-            self.memberInfo = res.data[0]
-            self.memberId = res.data[0].memberId
-          }
-        }
-      }).catch(function() {})
-    },
-    loadSandboxData: function() {
-      var self = this
-      getFamilyEnergySandbox().then(function(res) {
-        if (res && res.data) self.sandboxData = res.data
-      }).catch(function() {})
-    },
-    loadEnergyData: function() {
-      var self = this
-      getEnergyOverview(this.memberId).then(function(res) {
-        if (res && res.data) {
-          var dims = res.data.dimensions
-          if (dims && dims.length > 0) {
-            for (var i = 0; i < dims.length; i++) {
-              if (dims[i].code === 'body') {
-                self.dualDimension = dims[i]
-                break
-              }
-            }
-          }
-        }
-      }).catch(function() {})
-    },
-    onTaskClick: function(task) {
-      uni.navigateTo({ url: '/pages/tasks/tasks' })
-    },
-    goActivityDetail: function(act) {
-      if (act && act.id) uni.navigateTo({ url: '/pages/activity/activity-detail/activity-detail?id=' + act.id })
-    },
-    goMoreActivities: function() {
-      uni.navigateTo({ url: '/pages/activity/index' })
-    },
-    goProductDetail: function(prod) {
-      if (prod && prod.id) uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + prod.id })
-    },
-    goMoreProducts: function() {
-      uni.navigateTo({ url: '/pages/shop/index/index' })
-    },
-    goTasks: function() { uni.navigateTo({ url: '/pages/tasks/tasks' }) },
-    goBack: function() { uni.navigateBack() }
-  }
-}
-</script>
-
-<style scoped>
-.detail-container {
-  min-height: 100vh;
-  background: #f5f7fa;
-  padding-bottom: 40rpx;
-}
-.nav-back-icon {
-  font-size: 36rpx;
-  color: #333;
-}
-.member-card {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  margin: 20rpx 30rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 24rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-.member-avatar {
-  width: 80rpx;
-  height: 80rpx;
-  border-radius: 40rpx;
-  background: linear-gradient(135deg, #4CAF50, #81C784);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 20rpx;
-}
-.avatar-text {
-  font-size: 32rpx;
-  color: #fff;
-  font-weight: bold;
-}
-.member-info {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-}
-.member-name {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #333;
-}
-.member-role {
-  font-size: 24rpx;
-  color: #999;
-  margin-top: 4rpx;
-}
-.member-score {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-}
-.score-value {
-  font-size: 40rpx;
-  font-weight: bold;
-  color: #4CAF50;
-}
-.score-label {
-  font-size: 22rpx;
-  color: #999;
-}
-.bottom-spacer {
-  height: 40rpx;
-}
-</style>

+ 1 - 1
cfc-frontend/pages/cognitive-hall.vue

@@ -150,7 +150,7 @@ export default {
       uni.navigateTo({ url: url })
     },
     goReport: function() {
-      uni.navigateTo({ url: '/pages/wisdom/cognitive-report?memberId=' + (this.selectedMemberId || '') })
+      uni.navigateTo({ url: '/pages/wisdom-detail/cognitive-report?memberId=' + (this.selectedMemberId || '') })
     }
   }
 }

+ 0 - 1059
cfc-frontend/pages/mind/emotion-checkin.vue

@@ -1,1059 +0,0 @@
-<template>
-  <view class="checkin-container">
-    <!-- 顶部:日期 + 连续打卡 -->
-    <view class="checkin-header">
-      <text class="checkin-date">{{ todayDisplay }}</text>
-      <view class="streak-badge" v-if="stats && stats.currentStreak > 0">
-        <text class="streak-icon">&#x1F525;</text>
-        <text class="streak-text">连续{{ stats.currentStreak }}天</text>
-      </view>
-    </view>
-
-    <!-- ===== Phase 1.1: 2D 效价-激活度网格 ===== -->
-    <view class="section">
-      <text class="section-title">在方格中点击你的情绪位置</text>
-      <view class="affect-grid" @tap="onAffectGridTap">
-        <!-- 象限标签 -->
-        <text class="grid-label label-nw">烦躁/紧张</text>
-        <text class="grid-label label-ne">兴奋/愉快</text>
-        <text class="grid-label label-sw">低落/疲惫</text>
-        <text class="grid-label label-se">平静/放松</text>
-        <!-- 坐标轴 -->
-        <text class="axis-label axis-bottom">消极 ← 情绪效价 → 积极</text>
-        <text class="axis-label axis-left">激<br/>活<br/>度</text>
-        <!-- 选中的圆点 -->
-        <view class="affect-dot" :style="{ left: affectDotX + 'rpx', top: affectDotY + 'rpx' }">
-          <view class="affect-dot-inner"></view>
-        </view>
-      </view>
-      <view class="affect-values">
-        <text class="affect-val">效价: {{ affectValence }}/10</text>
-        <text class="affect-val">激活度: {{ affectArousal }}/10</text>
-      </view>
-    </view>
-
-    <!-- ===== Plutchik 情绪轮 ===== -->
-    <view class="section">
-      <text class="section-title">选择你的核心情绪</text>
-      <view class="emotion-wheel">
-        <view
-          v-for="em in plutchikEmotions"
-          :key="em.type"
-          :class="['emotion-btn', selectedEmotionType === em.type ? 'emotion-active' : '']"
-          :style="{ borderColor: selectedEmotionType === em.type ? em.color : '#eee' }"
-          @click="selectedEmotionType = em.type">
-          <text class="emotion-icon">{{ em.icon }}</text>
-          <text class="emotion-label">{{ em.label }}</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- ===== 强度滑条 ===== -->
-    <view class="section">
-      <text class="section-title">情绪强度</text>
-      <view class="slider-row">
-        <text class="slider-label">弱</text>
-        <slider class="custom-slider" min="1" max="10" :value="intensityLevel" @change="onIntensityChange" activeColor="#FF6B9D" block-size="20" />
-        <text class="slider-value">{{ intensityLevel }}</text>
-      </view>
-      <view class="intensity-hint">{{ intensityHint }}</view>
-    </view>
-
-    <!-- ===== 精力水平 ===== -->
-    <view class="section">
-      <text class="section-title">精力水平</text>
-      <view class="slider-row">
-        <text class="slider-label">低</text>
-        <slider class="custom-slider" min="1" max="10" :value="energyLevel" @change="onEnergyChange" activeColor="#10B981" block-size="20" />
-        <text class="slider-value">{{ energyLevel }}</text>
-      </view>
-    </view>
-
-    <!-- ===== 压力水平 ===== -->
-    <view class="section">
-      <text class="section-title">压力水平</text>
-      <view class="slider-row">
-        <text class="slider-label">无压力</text>
-        <slider class="custom-slider" min="1" max="10" :value="stressLevel" @change="onStressChange" activeColor="#F59E0B" block-size="20" />
-        <text class="slider-value">{{ stressLevel }}</text>
-      </view>
-    </view>
-
-    <!-- ===== 情绪标签(精细标签) ===== -->
-    <view class="section">
-      <text class="section-title">情绪标签(可多选)</text>
-      <view class="tag-grid">
-        <view
-          v-for="tag in emotionTags"
-          :key="tag"
-          :class="['tag-btn', selectedTags.indexOf(tag) !== -1 ? 'tag-active' : '']"
-          @click="toggleTag(tag)">
-          <text>{{ tag }}</text>
-        </view>
-      </view>
-      <view class="custom-tag-row" v-if="showCustomTagInput">
-        <input class="custom-tag-input" v-model="customTag" placeholder="输入自定义情绪" />
-        <text class="custom-tag-add" @click="addCustomTag">添加</text>
-      </view>
-      <text class="custom-tag-trigger" v-if="!showCustomTagInput" @click="showCustomTagInput = true">+ 自定义标签</text>
-    </view>
-
-    <!-- 拍照 -->
-    <view class="section">
-      <text class="section-title">拍一张照片记录心情</text>
-      <view class="photo-area" @click="takePhoto">
-        <image v-if="photoPath" :src="photoPath" mode="aspectFill" class="photo-preview" />
-        <view v-else class="photo-placeholder">
-          <text class="photo-icon">&#x1F4F7;</text>
-          <text class="photo-hint">点击拍照</text>
-        </view>
-      </view>
-      <text class="photo-note">照片仅对自己可见</text>
-    </view>
-
-    <!-- 情绪识别 -->
-    <view class="section" v-if="photoPath">
-      <text class="section-title">\uD83E\uDD16 情绪识别</text>
-      <view class="ai-recognize-btn" v-if="!aiResult && !aiAnalyzing" @click="analyzeEmotion">
-        <text class="ai-btn-icon">\uD83D\uDD0D</text>
-        <text class="ai-btn-text">识别照片中的情绪</text>
-      </view>
-      <view class="ai-analyzing" v-if="aiAnalyzing">
-        <text class="loading-text">分析中...</text>
-      </view>
-      <view class="ai-result" v-if="aiResult">
-        <text class="ai-result-title">识别结果</text>
-        <view class="ai-emotion-list">
-          <view class="ai-emotion-item" v-for="em in aiResult" :key="em.label">
-            <text class="ai-emotion-label">{{ em.label }}</text>
-            <view class="ai-emotion-bar-track">
-              <view class="ai-emotion-bar-fill" :style="{ width: em.confidence + '%', background: em.color }"></view>
-            </view>
-            <text class="ai-emotion-value">{{ em.confidence }}%</text>
-          </view>
-        </view>
-        <view class="ai-apply-tags" @click="applyAiTags">
-          <text class="ai-apply-text">应用为情绪标签</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 文字记录 -->
-    <view class="section">
-      <text class="section-title">记录一下(选填)</text>
-      <textarea
-        class="note-input"
-        v-model="note"
-        placeholder="今天发生了什么?"
-        maxlength="500" />
-    </view>
-
-    <!-- 提交按钮 -->
-    <view class="submit-area">
-      <button
-        class="submit-btn"
-        :class="{ 'submit-disabled': !canSubmit }"
-        :disabled="!canSubmit"
-        @click="submitCheckin">
-        <text class="submit-icon">&#x2764;</text>
-        <text>记录心情</text>
-      </button>
-    </view>
-
-    <!-- 提交中 loading -->
-    <view class="loading-mask" v-if="submitting">
-      <view class="loading-box">
-        <text class="loading-text">记录中...</text>
-      </view>
-    </view>
-  </view>
-</template>
-
-<script>
-import config from '../../config'
-
-export default {
-  data() {
-    return {
-      // --- 2D 效价-激活度网格 ---
-      affectValence: 5,       // 1-10
-      affectArousal: 5,       // 1-10
-      gridWidth: 360,         // rpx, 会在 onReady 中动态计算
-      gridHeight: 360,
-
-      // --- Plutchik 情绪轮 ---
-      selectedEmotionType: '',
-
-      // --- 滑条 ---
-      intensityLevel: 5,
-      energyLevel: 6,
-      stressLevel: 4,
-
-      // --- 旧字段保留兼容 ---
-      selectedWeather: 'sunny',   // 自动从效价映射
-      selectedTags: [],
-      customTag: '',
-      showCustomTagInput: false,
-      photoPath: '',
-      photoUrl: '',
-      note: '',
-      submitting: false,
-      aiAnalyzing: false,
-      aiResult: null,
-      aiEmotionColors: {
-        '开心': '#10B981',
-        '平静': '#3B82F6',
-        '惊喜': '#8B5CF6',
-        '难过': '#6B9BD2',
-        '生气': '#EF4444',
-        '紧张': '#F59E0B',
-        '疲惫': '#9CA3AF',
-        '害怕': '#7C3AED'
-      },
-      stats: null,
-
-      // Plutchik 8 基本情绪
-      plutchikEmotions: [
-        { type: 'joy',         icon: '\uD83D\uDE04', label: '开心',   color: '#FFD93D' },
-        { type: 'trust',       icon: '\uD83E\uDD1D', label: '信任',   color: '#6BCB77' },
-        { type: 'fear',        icon: '\uD83D\uDE31', label: '恐惧',   color: '#9B59B6' },
-        { type: 'surprise',    icon: '\uD83D\uDE32', label: '惊讶',   color: '#FF9FF3' },
-        { type: 'sadness',     icon: '\uD83D\uDE22', label: '悲伤',   color: '#6B9BD2' },
-        { type: 'disgust',     icon: '\uD83E\uDD2E', label: '厌恶',   color: '#A0A0A0' },
-        { type: 'anger',       icon: '\uD83D\uDE21', label: '愤怒',   color: '#FF6B6B' },
-        { type: 'anticipation',icon: '\uD83E\uDD29', label: '期待',   color: '#FF8C42' }
-      ],
-
-      emotionTags: ['开心', '期待', '平静', '惊喜', '难过', '生气', '紧张', '无聊', '疲惫', '害怕'],
-
-      // 强度提示文案
-      intensityHints: {
-        1: '几乎感觉不到',
-        2: '很微弱',
-        3: '有一点',
-        4: '稍微明显',
-        5: '中等强度',
-        6: '比较明显',
-        7: '相当强烈',
-        8: '很强烈',
-        9: '非常强烈',
-        10: '前所未有的强烈'
-      }
-    }
-  },
-
-  computed: {
-    todayDisplay() {
-      const now = new Date()
-      const y = now.getFullYear()
-      const m = now.getMonth() + 1
-      const d = now.getDate()
-      const weekdays = ['日', '一', '二', '三', '四', '五', '六']
-      return y + '年' + m + '月' + d + '日 周' + weekdays[now.getDay()]
-    },
-
-    canSubmit() {
-      return this.affectValence > 0 && this.selectedEmotionType
-    },
-
-    // 圆点位置(rpx)
-    affectDotX() {
-      // valence 1-10 → 0 to gridWidth
-      // 留出边距: 从 20 到 gridWidth-20
-      const margin = 20
-      const usable = this.gridWidth - 2 * margin
-      return margin + ((this.affectValence - 1) / 9) * usable
-    },
-
-    affectDotY() {
-      // arousal 1-10 → gridHeight to 0 (Y轴反向)
-      const margin = 20
-      const usable = this.gridHeight - 2 * margin
-      return margin + (1 - (this.affectArousal - 1) / 9) * usable
-    },
-
-    intensityHint() {
-      return this.intensityHints[this.intensityLevel] || ''
-    }
-  },
-
-  onLoad() {
-    this.loadStats()
-  },
-
-  onReady() {
-    // 估算 grid 尺寸(基于屏幕宽度)
-    var sysInfo = uni.getWindowInfo()
-    // 页面左右margin=32rpx, section padding=28rpx
-    // gridWidth ≈ screenWidth - (32+28)*2 = screenWidth - 120
-    var screenWidth = sysInfo.windowWidth
-    var gridRpx = screenWidth - 120  // 估算 rpx
-    if (gridRpx < 300) gridRpx = 300
-    this.gridWidth = gridRpx
-    this.gridHeight = gridRpx
-  },
-
-  methods: {
-    loadStats() {
-      const memberId = uni.getStorageSync('currentChildId')
-      if (!memberId) return
-      uni.request({
-        url: config.API_BASE_URL + '/api/mind/checkin/stats',
-        method: 'POST',
-        data: { memberId },
-        success: (res) => {
-          if (res.data && res.data.code === 200 && res.data.data) {
-            this.stats = res.data.data
-          }
-        }
-      })
-    },
-
-    // ===== 2D 效价-激活度网格 =====
-    onAffectGridTap(e) {
-      // 从触点获取页面坐标(clientX/clientY,px)
-      var touch = (e.touches && e.touches[0]) || (e.changedTouches && e.changedTouches[0]) || e.detail
-      var clientX = touch ? (touch.clientX !== undefined ? touch.clientX : touch.x) : 0
-      var clientY = touch ? (touch.clientY !== undefined ? touch.clientY : touch.y) : 0
-
-      var self = this
-      var query = uni.createSelectorQuery().in(this)
-      query.select('.affect-grid').boundingClientRect(function(rect) {
-        if (!rect) return
-
-        // 相对网格的偏移(px)
-        var pxX = clientX - rect.left
-        var pxY = clientY - rect.top
-        if (pxX < 0) pxX = 0
-        if (pxY < 0) pxY = 0
-        if (pxX > rect.width) pxX = rect.width
-        if (pxY > rect.height) pxY = rect.height
-
-        // px → rpx 转换(网格宽高单位为 rpx,通过比例换算)
-        var scale = self.gridWidth / rect.width
-        var rpxX = pxX * scale
-        var rpxY = pxY * scale
-
-        // 约束在有效范围内(跟 affectDotX/Y 的 margin 对齐)
-        var margin = 20
-        var maxRpx = self.gridWidth - margin
-        if (rpxX < margin) rpxX = margin
-        if (rpxX > maxRpx) rpxX = maxRpx
-        if (rpxY < margin) rpxY = margin
-        if (rpxY > self.gridHeight - margin) rpxY = self.gridHeight - margin
-
-        // 映射到 1-10
-        var usable = self.gridWidth - 2 * margin
-        var val = Math.round(1 + ((rpxX - margin) / usable) * 9)
-        var aro = Math.round(10 - ((rpxY - margin) / usable) * 9)
-
-        self.affectValence = Math.max(1, Math.min(10, val))
-        self.affectArousal = Math.max(1, Math.min(10, aro))
-
-        // 自动映射 moodWeather 用于向后兼容
-        self.updateWeatherFromAffect()
-      }).exec()
-    },
-
-    updateWeatherFromAffect() {
-      // 效价高 + 激活度高 = rainbow
-      if (this.affectValence >= 7 && this.affectArousal >= 7) {
-        this.selectedWeather = 'rainbow'
-      } else if (this.affectValence >= 6) {
-        // 效价高 = sunny
-        this.selectedWeather = 'sunny'
-      } else if (this.affectValence <= 4 && this.affectArousal >= 7) {
-        // 效价低 + 激活度高 = stormy
-        this.selectedWeather = 'stormy'
-      } else if (this.affectValence <= 4 && this.affectArousal <= 4) {
-        // 效价低 + 激活度低 = rainy
-        this.selectedWeather = 'rainy'
-      } else {
-        this.selectedWeather = 'cloudy'
-      }
-    },
-
-    // ===== 滑条变化 =====
-    onIntensityChange(e) {
-      this.intensityLevel = e.detail.value
-    },
-
-    onEnergyChange(e) {
-      this.energyLevel = e.detail.value
-    },
-
-    onStressChange(e) {
-      this.stressLevel = e.detail.value
-    },
-
-    // ===== 情绪标签 =====
-    toggleTag(tag) {
-      const idx = this.selectedTags.indexOf(tag)
-      if (idx !== -1) {
-        this.selectedTags.splice(idx, 1)
-      } else {
-        this.selectedTags.push(tag)
-      }
-    },
-
-    addCustomTag() {
-      const tag = this.customTag.trim()
-      if (tag && this.emotionTags.indexOf(tag) === -1 && this.selectedTags.indexOf(tag) === -1) {
-        this.selectedTags.push(tag)
-      }
-      this.customTag = ''
-      this.showCustomTagInput = false
-    },
-
-    // ===== 拍照 =====
-    takePhoto() {
-      uni.chooseImage({
-        count: 1,
-        sourceType: ['camera', 'album'],
-        success: (res) => {
-          this.photoPath = res.tempFilePaths[0]
-          this.aiResult = null
-        }
-      })
-    },
-
-    analyzeEmotion() {
-      var self = this
-      if (!self.photoPath) {
-        uni.showToast({ title: '请先拍照', icon: 'none' })
-        return
-      }
-      self.aiAnalyzing = true
-      self.aiResult = null
-      uni.uploadFile({
-        url: config.API_BASE_URL + '/api/media/upload',
-        filePath: self.photoPath,
-        name: 'file',
-        success: function(uploadRes) {
-          var photoUrl = ''
-          try {
-            var data = JSON.parse(uploadRes.data)
-            photoUrl = data.data || data.url || ''
-            self.photoUrl = photoUrl
-          } catch (e) {
-            photoUrl = uploadRes.data || ''
-            self.photoUrl = photoUrl
-          }
-          if (!photoUrl) {
-            self.aiAnalyzing = false
-            uni.showToast({ title: '照片上传失败', icon: 'none' })
-            return
-          }
-          uni.request({
-            url: config.API_BASE_URL + '/api/mind/checkin/emotion/recognize',
-            method: 'POST',
-            data: { image_url: photoUrl },
-            header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + uni.getStorageSync('token') },
-            success: function(res) {
-              self.aiAnalyzing = false
-              if (res.data && res.data.code === 200 && res.data.data && res.data.data.length > 0) {
-                self.aiResult = res.data.data
-              } else {
-                self.aiResult = []
-                uni.showToast({ title: '识别失败,请重试', icon: 'none' })
-              }
-            },
-            fail: function() {
-              self.aiAnalyzing = false
-              uni.showToast({ title: '网络错误', icon: 'none' })
-            }
-          })
-        },
-        fail: function() {
-          self.aiAnalyzing = false
-          uni.showToast({ title: '照片上传失败', icon: 'none' })
-        }
-      })
-    },
-
-    applyAiTags() {
-      if (!this.aiResult) return
-      var detected = []
-      for (var i = 0; i < this.aiResult.length; i++) {
-        var em = this.aiResult[i]
-        if (em.confidence >= 20 && this.emotionTags.indexOf(em.label) !== -1) {
-          detected.push(em.label)
-        }
-      }
-      var merged = this.selectedTags.slice()
-      for (var j = 0; j < detected.length; j++) {
-        if (merged.indexOf(detected[j]) === -1) {
-          merged.push(detected[j])
-        }
-      }
-      this.selectedTags = merged
-      uni.showToast({ title: '已应用情绪标签', icon: 'success' })
-    },
-
-    // ===== 提交 =====
-    submitCheckin() {
-      if (!this.canSubmit || this.submitting) return
-      this.submitting = true
-
-      const memberId = uni.getStorageSync('currentChildId')
-
-      const doSubmit = (photoUrl) => {
-        uni.request({
-          url: config.API_BASE_URL + '/api/mind/checkin/create',
-          method: 'POST',
-          data: {
-            memberId: memberId,
-            // 旧字段向后兼容
-            moodWeather: this.selectedWeather,
-            emotionTags: JSON.stringify(this.selectedTags),
-            photoUrl: photoUrl || '',
-            note: this.note,
-            // Phase 1.1 精细情绪字段
-            moodScore: this.affectValence,
-            emotionType: this.selectedEmotionType,
-            stressLevel: this.stressLevel,
-            arousalLevel: this.affectArousal,
-            energyLevel: this.energyLevel
-          },
-          success: (res) => {
-            this.submitting = false
-            if (res.data && res.data.code === 200) {
-              uni.showToast({
-                title: '+5 心能量',
-                icon: 'none',
-                duration: 2000
-              })
-              setTimeout(() => {
-                uni.navigateTo({ url: '/pages/mind/emotion-trend' })
-              }, 1500)
-            } else {
-              uni.showToast({ title: res.data && res.data.message || '提交失败', icon: 'none' })
-            }
-          },
-          fail: () => {
-            this.submitting = false
-            uni.showToast({ title: '网络错误', icon: 'none' })
-          }
-        })
-      }
-
-      if (this.photoUrl) {
-        doSubmit(this.photoUrl)
-      } else if (this.photoPath) {
-        uni.uploadFile({
-          url: config.API_BASE_URL + '/api/media/upload',
-          filePath: this.photoPath,
-          name: 'file',
-          success: (uploadRes) => {
-            let photoUrl = ''
-            try {
-              const data = JSON.parse(uploadRes.data)
-              photoUrl = data.data || data.url || ''
-            } catch (e) {
-              photoUrl = uploadRes.data
-            }
-            this.photoUrl = photoUrl
-            doSubmit(photoUrl)
-          },
-          fail: () => doSubmit('')
-        })
-      } else {
-        doSubmit('')
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.checkin-container {
-  min-height: 100vh;
-  background: linear-gradient(180deg, #FFF0F5 0%, #FFFFFF 100%);
-  padding-bottom: 40rpx;
-}
-
-.checkin-header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 30rpx 32rpx;
-}
-
-.checkin-date {
-  font-size: 32rpx;
-  font-weight: 600;
-  color: #333;
-}
-
-.streak-badge {
-  display: flex;
-  align-items: center;
-  background: linear-gradient(135deg, #FF6B9D, #FF8CB3);
-  border-radius: 30rpx;
-  padding: 8rpx 24rpx;
-}
-
-.streak-icon {
-  font-size: 28rpx;
-  margin-right: 8rpx;
-}
-
-.streak-text {
-  font-size: 24rpx;
-  color: #fff;
-  font-weight: 500;
-}
-
-.section {
-  margin: 20rpx 32rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 28rpx;
-  box-shadow: 0 2rpx 12rpx rgba(255, 107, 157, 0.08);
-}
-
-.section-title {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: #333;
-  margin-bottom: 20rpx;
-  display: block;
-}
-
-/* ===== 2D 效价-激活度网格 ===== */
-.affect-grid {
-  position: relative;
-  width: 100%;
-  /* height is set dynamically equal to width via aspect-ratio or inline style */
-  aspect-ratio: 1;
-  background: linear-gradient(135deg, #FFF5F5 0%, #FFF0FF 25%, #F0F0FF 50%, #F0FFF4 75%, #FFFFF0 100%);
-  border-radius: 16rpx;
-  border: 2rpx solid #E8E8E8;
-  margin-bottom: 16rpx;
-  /* Ensure the grid has a defined height — fallback */
-  min-height: 300rpx;
-  /* Overlap grid lines for visual clarity */
-  overflow: hidden;
-}
-
-/* 网格线:用伪元素或背景渐变 */
-.affect-grid::before {
-  content: '';
-  position: absolute;
-  left: 50%;
-  top: 0;
-  bottom: 0;
-  width: 2rpx;
-  background: rgba(0,0,0,0.06);
-  transform: translateX(-50%);
-  pointer-events: none;
-}
-
-.affect-grid::after {
-  content: '';
-  position: absolute;
-  top: 50%;
-  left: 0;
-  right: 0;
-  height: 2rpx;
-  background: rgba(0,0,0,0.06);
-  transform: translateY(-50%);
-  pointer-events: none;
-}
-
-.grid-label {
-  position: absolute;
-  font-size: 22rpx;
-  color: rgba(0,0,0,0.25);
-  pointer-events: none;
-}
-
-.label-nw { top: 20rpx; left: 20rpx; }
-.label-ne { top: 20rpx; right: 20rpx; }
-.label-sw { bottom: 20rpx; left: 20rpx; }
-.label-se { bottom: 20rpx; right: 20rpx; }
-
-.axis-label {
-  position: absolute;
-  font-size: 20rpx;
-  color: rgba(0,0,0,0.2);
-  pointer-events: none;
-}
-
-.axis-bottom {
-  bottom: 4rpx;
-  left: 50%;
-  transform: translateX(-50%);
-  white-space: nowrap;
-}
-
-.axis-left {
-  left: 4rpx;
-  top: 50%;
-  transform: translateY(-50%);
-  font-size: 18rpx;
-  line-height: 1.4;
-  text-align: center;
-}
-
-.affect-dot {
-  position: absolute;
-  width: 44rpx;
-  height: 44rpx;
-  margin-left: -22rpx;
-  margin-top: -22rpx;
-  pointer-events: none;
-  z-index: 2;
-}
-
-.affect-dot-inner {
-  width: 100%;
-  height: 100%;
-  border-radius: 50%;
-  background: #FF6B9D;
-  box-shadow: 0 0 12rpx rgba(255, 107, 157, 0.5);
-  border: 4rpx solid #fff;
-  box-sizing: border-box;
-}
-
-.affect-values {
-  display: flex;
-  justify-content: center;
-  gap: 40rpx;
-}
-
-.affect-val {
-  font-size: 24rpx;
-  color: #888;
-}
-
-/* ===== Plutchik 情绪轮 ===== */
-.emotion-wheel {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 16rpx;
-  justify-content: center;
-}
-
-.emotion-btn {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  width: 130rpx;
-  padding: 16rpx 8rpx;
-  border-radius: 20rpx;
-  border: 3rpx solid #eee;
-  background: #FAFAFA;
-  transition: all 0.2s;
-}
-
-.emotion-active {
-  background: #FFF0F5;
-  transform: scale(1.05);
-}
-
-.emotion-icon {
-  font-size: 48rpx;
-  margin-bottom: 6rpx;
-}
-
-.emotion-label {
-  font-size: 24rpx;
-  color: #555;
-}
-
-/* ===== 滑条 ===== */
-.slider-row {
-  display: flex;
-  align-items: center;
-  gap: 16rpx;
-}
-
-.slider-label {
-  font-size: 24rpx;
-  color: #999;
-  white-space: nowrap;
-  width: 64rpx;
-}
-
-.custom-slider {
-  flex: 1;
-}
-
-.slider-value {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: #FF6B9D;
-  width: 40rpx;
-  text-align: center;
-}
-
-.intensity-hint {
-  font-size: 22rpx;
-  color: #aaa;
-  text-align: center;
-  margin-top: 4rpx;
-}
-
-/* ===== 情绪标签 ===== */
-.tag-grid {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 16rpx;
-}
-
-.tag-btn {
-  padding: 12rpx 28rpx;
-  border-radius: 30rpx;
-  background: #F5F5F5;
-  font-size: 26rpx;
-  color: #666;
-  border: 2rpx solid transparent;
-}
-
-.tag-active {
-  background: #FFF0F5;
-  color: #FF6B9D;
-  border-color: #FF6B9D;
-}
-
-.custom-tag-row {
-  display: flex;
-  align-items: center;
-  margin-top: 16rpx;
-  gap: 16rpx;
-}
-
-.custom-tag-input {
-  flex: 1;
-  height: 60rpx;
-  border: 2rpx solid #E0E0E0;
-  border-radius: 12rpx;
-  padding: 0 16rpx;
-  font-size: 26rpx;
-}
-
-.custom-tag-add {
-  padding: 12rpx 24rpx;
-  background: #FF6B9D;
-  color: #fff;
-  border-radius: 12rpx;
-  font-size: 26rpx;
-}
-
-.custom-tag-trigger {
-  display: inline-block;
-  margin-top: 16rpx;
-  color: #FF6B9D;
-  font-size: 26rpx;
-}
-
-/* ===== 拍照 ===== */
-.photo-area {
-  width: 200rpx;
-  height: 200rpx;
-  border-radius: 16rpx;
-  overflow: hidden;
-  background: #F5F5F5;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.photo-preview {
-  width: 100%;
-  height: 100%;
-}
-
-.photo-placeholder {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-}
-
-.photo-icon {
-  font-size: 60rpx;
-}
-
-.photo-hint {
-  font-size: 24rpx;
-  color: #999;
-  margin-top: 8rpx;
-}
-
-.photo-note {
-  display: block;
-  font-size: 22rpx;
-  color: #999;
-  margin-top: 12rpx;
-}
-
-/* ===== 情绪识别 ===== */
-.ai-recognize-btn {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 20rpx;
-  background: linear-gradient(135deg, #8B5CF6, #A78BFA);
-  border-radius: 16rpx;
-  cursor: pointer;
-}
-
-.ai-btn-icon {
-  font-size: 32rpx;
-  margin-right: 10rpx;
-}
-
-.ai-btn-text {
-  font-size: 26rpx;
-  color: #fff;
-  font-weight: 500;
-}
-
-.ai-recognize-btn:active {
-  opacity: 0.8;
-}
-
-.ai-analyzing {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 30rpx 0;
-}
-
-.loading-text {
-  font-size: 26rpx;
-  color: #8B5CF6;
-}
-
-.ai-result {
-  margin-top: 16rpx;
-}
-
-.ai-result-title {
-  font-size: 24rpx;
-  color: #666;
-  margin-bottom: 12rpx;
-  display: block;
-}
-
-.ai-emotion-list {
-  display: flex;
-  flex-direction: column;
-  gap: 12rpx;
-}
-
-.ai-emotion-item {
-  display: flex;
-  align-items: center;
-  gap: 12rpx;
-}
-
-.ai-emotion-label {
-  font-size: 24rpx;
-  color: #333;
-  width: 64rpx;
-  flex-shrink: 0;
-}
-
-.ai-emotion-bar-track {
-  flex: 1;
-  height: 16rpx;
-  background: #F0F0F0;
-  border-radius: 8rpx;
-  overflow: hidden;
-}
-
-.ai-emotion-bar-fill {
-  height: 100%;
-  border-radius: 8rpx;
-  transition: width 0.5s;
-}
-
-.ai-emotion-value {
-  font-size: 22rpx;
-  color: #999;
-  width: 56rpx;
-  text-align: right;
-}
-
-.ai-apply-tags {
-  margin-top: 20rpx;
-  padding: 16rpx;
-  background: #F5F0FF;
-  border-radius: 12rpx;
-  text-align: center;
-  cursor: pointer;
-}
-
-.ai-apply-text {
-  font-size: 26rpx;
-  color: #8B5CF6;
-  font-weight: 500;
-}
-
-.ai-apply-tags:active {
-  opacity: 0.7;
-}
-
-.note-input {
-  width: 100%;
-  min-height: 140rpx;
-  background: #F9F9F9;
-  border-radius: 12rpx;
-  padding: 20rpx;
-  font-size: 26rpx;
-  color: #333;
-  box-sizing: border-box;
-}
-
-.submit-area {
-  margin: 40rpx 32rpx;
-}
-
-.submit-btn {
-  width: 100%;
-  height: 88rpx;
-  line-height: 88rpx;
-  background: linear-gradient(135deg, #FF6B9D, #FF8CB3);
-  color: #fff;
-  border-radius: 44rpx;
-  font-size: 30rpx;
-  font-weight: 600;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.submit-disabled {
-  opacity: 0.5;
-}
-
-.submit-icon {
-  margin-right: 8rpx;
-}
-
-.loading-mask {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: rgba(0, 0, 0, 0.3);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  z-index: 999;
-}
-
-.loading-box {
-  background: #fff;
-  padding: 40rpx 60rpx;
-  border-radius: 20rpx;
-}
-
-.loading-text {
-  font-size: 28rpx;
-  color: #666;
-}
-</style>

+ 0 - 417
cfc-frontend/pages/mind/emotion-report.vue

@@ -1,417 +0,0 @@
-<template>
-  <view class="container">
-    <!-- 报告数据 -->
-    <view class="report-card" v-if="report">
-      <view class="report-header">
-        <text class="report-title">EMI 心理测评报告</text>
-        <text class="report-date">测评日期: {{ report.assessmentDate || '-' }}</text>
-      </view>
-
-      <!-- 综合EQ分 -->
-      <view class="overall-wrap">
-        <text class="overall-label">综合EQ分</text>
-        <text class="overall-value">{{ report.overallScore || 0 }}</text>
-        <text :class="['overall-level', 'level-' + getScoreLevel(report.overallScore || 0)]">{{ getScoreLevelText(report.overallScore || 0) }}</text>
-      </view>
-
-      <!-- 四维条形图 -->
-      <view class="dimension-list">
-        <view class="dimension-item" v-for="dim in dimensions" :key="dim.key">
-          <view class="dimension-header">
-            <view class="dimension-label-row">
-              <text class="dimension-icon">{{ dim.icon }}</text>
-              <text class="dimension-name">{{ dim.label }}</text>
-            </view>
-            <view class="dimension-score-row">
-              <text class="dimension-score">{{ report[dim.key] || 0 }}</text>
-              <text :class="['dimension-level', 'level-' + getScoreLevel(report[dim.key] || 0)]">{{ getScoreLevelText(report[dim.key] || 0) }}</text>
-            </view>
-          </view>
-          <view class="dimension-bar">
-            <view class="dimension-bar-fill" :style="{ width: (report[dim.key] || 0) + '%', background: dim.color }"></view>
-          </view>
-          <text class="dimension-desc">{{ dim.desc }}</text>
-        </view>
-      </view>
-
-      <!-- 分析报告 -->
-      <view class="section-card" v-if="report.analysisReport">
-        <view class="section-header-row">
-          <text class="section-icon">&#x1F4DD;</text>
-          <text class="section-title">分析报告</text>
-        </view>
-        <text class="section-content">{{ report.analysisReport }}</text>
-      </view>
-
-      <!-- 成长建议 -->
-      <view class="section-card suggestion-card" v-if="report.growthSuggestions">
-        <view class="section-header-row">
-          <text class="section-icon">&#x1F331;</text>
-          <text class="section-title">成长建议</text>
-        </view>
-        <text class="section-content">{{ report.growthSuggestions }}</text>
-      </view>
-
-      <!-- 底部操作 -->
-      <view class="action-row">
-        <view class="action-btn secondary" @click="goAssessment">
-          <text class="action-btn-text">再次测评</text>
-        </view>
-        <view class="action-btn primary" @click="goBack">
-          <text class="action-btn-text">返回心智</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 加载中 -->
-    <view class="loading-state" v-if="loading">
-      <text class="loading-text">加载中...</text>
-    </view>
-
-    <!-- 无数据 -->
-    <view class="empty-state" v-if="!report && !loading">
-      <text class="empty-icon">&#x1F9E0;</text>
-      <text class="empty-title">暂无心理测评数据</text>
-      <text class="empty-desc">完成 EMI 心理测评后,可在此查看详细的心理分析报告</text>
-      <view class="empty-btn" @click="goAssessment">预约测评</view>
-    </view>
-  </view>
-</template>
-
-<script>
-import { getEmiReport } from '../../utils/api.js'
-
-export default {
-  components: { },
-  data: function() {
-    return {
-      memberId: null,
-      report: null,
-      loading: true,
-      dimensions: [
-        { key: 'emotionScore', label: '情绪商数', icon: '\u2764\uFE0F', color: '#FF6B35', desc: '识别、理解和管理自身及他人情绪的能力' },
-        { key: 'resilienceScore', label: '心理韧性', icon: '\u{1F4AA}', color: '#F97316', desc: '面对压力和挫折时的适应与恢复能力' },
-        { key: 'stressCopingScore', label: '压力应对', icon: '\u{1F64F}', color: '#FB923C', desc: '有效应对生活压力和心理负担的能力' },
-        { key: 'selfAwarenessScore', label: '自我认知', icon: '\u{1F9EC}', color: '#FBBF24', desc: '对自身情绪、优势、弱点的深度理解' }
-      ]
-    }
-  },
-  onLoad: function(options) {
-    if (options && options.memberId) {
-      this.memberId = options.memberId
-    } else {
-      this.memberId = uni.getStorageSync('currentChildId') || null
-    }
-    this.loadReport()
-  },
-  methods: {
-    loadReport: function() {
-      var self = this
-      var memberId = self.memberId
-      if (!memberId) {
-        self.loading = false
-        return
-      }
-      self.loading = true
-      getEmiReport(memberId).then(function(res) {
-        if (res.code === 200 && res.data) {
-          self.report = res.data
-        }
-        self.loading = false
-      }).catch(function() {
-        self.loading = false
-      })
-    },
-    getScoreLevel: function(score) {
-      if (score >= 85) return 'excellent'
-      if (score >= 70) return 'good'
-      if (score >= 55) return 'average'
-      return 'low'
-    },
-    getScoreLevelText: function(score) {
-      if (score >= 85) return '优秀'
-      if (score >= 70) return '良好'
-      if (score >= 55) return '一般'
-      return '需提升'
-    },
-    goAssessment: function() {
-      uni.navigateTo({ url: '/pages/assessment/apply' })
-    },
-    goBack: function() {
-      uni.navigateBack({ delta: 1 })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.container {
-  min-height: 100vh;
-  background: #f5f7fa;
-  padding-bottom: 120rpx;
-}
-
-/* ===== 报告卡 ===== */
-.report-card {
-  margin: 20rpx 30rpx;
-  background: #fff;
-  border-radius: 24rpx;
-  padding: 40rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
-}
-
-.report-header {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 30rpx;
-}
-
-.report-title {
-  font-size: 36rpx;
-  font-weight: bold;
-  color: #333;
-}
-
-.report-date {
-  font-size: 24rpx;
-  color: #999;
-}
-
-/* ===== 综合EQ ===== */
-.overall-wrap {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 40rpx 0;
-  background: linear-gradient(135deg, #FF6B35, #FFD700);
-  border-radius: 20rpx;
-  margin-bottom: 40rpx;
-  color: #fff;
-}
-
-.overall-label {
-  font-size: 28rpx;
-  opacity: 0.9;
-  margin-bottom: 8rpx;
-}
-
-.overall-value {
-  font-size: 80rpx;
-  font-weight: bold;
-  margin-bottom: 8rpx;
-}
-
-.overall-level {
-  font-size: 26rpx;
-  padding: 4rpx 24rpx;
-  border-radius: 20rpx;
-  background: rgba(255,255,255,0.3);
-}
-
-/* ===== 四维条形图 ===== */
-.dimension-list {
-  margin-bottom: 30rpx;
-}
-
-.dimension-item {
-  margin-bottom: 28rpx;
-}
-
-.dimension-header {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 10rpx;
-}
-
-.dimension-label-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-}
-
-.dimension-icon {
-  font-size: 28rpx;
-  margin-right: 10rpx;
-}
-
-.dimension-name {
-  font-size: 28rpx;
-  color: #333;
-  font-weight: 500;
-}
-
-.dimension-score-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-}
-
-.dimension-score {
-  font-size: 32rpx;
-  font-weight: bold;
-  color: #FF6B35;
-  margin-right: 12rpx;
-}
-
-.dimension-level {
-  font-size: 22rpx;
-  padding: 2rpx 14rpx;
-  border-radius: 12rpx;
-}
-
-.level-excellent { color: #10B981; background: #ECFDF5; }
-.level-good { color: #3B82F6; background: #EFF6FF; }
-.level-average { color: #F97316; background: #FFF7ED; }
-.level-low { color: #EF4444; background: #FEF2F2; }
-
-.dimension-bar {
-  height: 18rpx;
-  background: #f0f0f0;
-  border-radius: 9rpx;
-  overflow: hidden;
-  margin-bottom: 8rpx;
-}
-
-.dimension-bar-fill {
-  height: 100%;
-  border-radius: 9rpx;
-  transition: width 0.5s;
-}
-
-.dimension-desc {
-  font-size: 22rpx;
-  color: #999;
-  line-height: 1.4;
-}
-
-/* ===== 分析/建议卡 ===== */
-.section-card {
-  margin-top: 30rpx;
-  padding: 28rpx;
-  background: #f9f9f9;
-  border-radius: 16rpx;
-}
-
-.suggestion-card {
-  background: #FFFBEB;
-}
-
-.section-header-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  margin-bottom: 16rpx;
-}
-
-.section-icon {
-  font-size: 30rpx;
-  margin-right: 10rpx;
-}
-
-.section-title {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #333;
-}
-
-.section-content {
-  font-size: 26rpx;
-  color: #666;
-  line-height: 1.7;
-  display: block;
-}
-
-/* ===== 底部操作 ===== */
-.action-row {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  margin-top: 40rpx;
-}
-
-.action-btn {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 20rpx 0;
-  border-radius: 40rpx;
-}
-
-.action-btn.primary {
-  background: linear-gradient(135deg, #FF6B35, #FFD700);
-  margin-left: 16rpx;
-  box-shadow: 0 4rpx 16rpx rgba(255,107,53,0.3);
-}
-
-.action-btn.secondary {
-  background: #fff;
-  border: 2rpx solid #ddd;
-  margin-right: 16rpx;
-}
-
-.action-btn-text {
-  font-size: 28rpx;
-  font-weight: 500;
-  color: #fff;
-}
-
-.action-btn.secondary .action-btn-text {
-  color: #666;
-}
-
-/* ===== 加载中 ===== */
-.loading-state {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 120rpx 0;
-}
-
-.loading-text {
-  font-size: 28rpx;
-  color: #999;
-}
-
-/* ===== 空状态 ===== */
-.empty-state {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 120rpx 40rpx 80rpx;
-}
-
-.empty-icon {
-  font-size: 100rpx;
-  margin-bottom: 20rpx;
-}
-
-.empty-title {
-  font-size: 32rpx;
-  color: #333;
-  font-weight: bold;
-  margin-bottom: 16rpx;
-}
-
-.empty-desc {
-  font-size: 26rpx;
-  color: #999;
-  text-align: center;
-  line-height: 1.6;
-  margin-bottom: 40rpx;
-}
-
-.empty-btn {
-  background: linear-gradient(135deg, #FF6B35, #FFD700);
-  color: #fff;
-  font-size: 28rpx;
-  font-weight: bold;
-  padding: 16rpx 64rpx;
-  border-radius: 40rpx;
-  box-shadow: 0 4rpx 16rpx rgba(255,107,53,0.3);
-}
-</style>

+ 0 - 839
cfc-frontend/pages/mind/emotion-trend.vue

@@ -1,839 +0,0 @@
-<template>
-  <view class="trend-container">
-    <!-- 统计概览卡片 -->
-    <view class="stats-overview">
-      <view class="stat-item">
-        <text class="stat-value">{{ stats.totalCheckins || 0 }}</text>
-        <text class="stat-label">总打卡</text>
-      </view>
-      <view class="stat-item">
-        <text class="stat-value">{{ stats.currentStreak || 0 }}</text>
-        <text class="stat-label">连续天数</text>
-      </view>
-      <view class="stat-item">
-        <text class="stat-value">{{ stats.thisMonthCheckins || 0 }}</text>
-        <text class="stat-label">本月打卡</text>
-      </view>
-      <view class="stat-item">
-        <text class="stat-value">{{ stats.totalEnergy || 0 }}</text>
-        <text class="stat-label">心能量</text>
-      </view>
-    </view>
-
-    <!-- 周期切换 -->
-    <view class="period-tabs">
-      <view
-        :class="['period-tab', period === 'week' ? 'period-active' : '']"
-        @click="switchPeriod('week')">
-        <text>周</text>
-      </view>
-      <view
-        :class="['period-tab', period === 'month' ? 'period-active' : '']"
-        @click="switchPeriod('month')">
-        <text>月</text>
-      </view>
-    </view>
-
-    <!-- ===== Phase 1.1: 情绪评分折线图 ===== -->
-    <view class="chart-card" v-if="moodTrendData.length > 0">
-      <text class="card-title">情绪评分趋势</text>
-      <text class="card-subtitle" v-if="trend && trend.moodScoreAvg">周均分: {{ trend.moodScoreAvg }}</text>
-      <canvas class="line-chart-canvas" canvas-id="moodTrendCanvas" @ready="drawMoodTrend"></canvas>
-    </view>
-
-    <!-- ===== Phase 1.1: 效价-激活度散点图 ===== -->
-    <view class="chart-card" v-if="scatterData.length > 0">
-      <text class="card-title">情绪空间分布</text>
-      <text class="card-subtitle">每个点代表一次打卡</text>
-      <canvas class="scatter-canvas" canvas-id="scatterCanvas" @ready="drawScatter"></canvas>
-    </view>
-
-    <!-- ===== 情绪日历(升级为热力版) ===== -->
-    <view class="calendar-card">
-      <text class="card-title">情绪日历</text>
-      <view class="calendar-grid">
-        <view class="calendar-weekday" v-for="wd in weekdays" :key="wd">
-          <text>{{ wd }}</text>
-        </view>
-        <view
-          v-for="(day, idx) in calendarDays"
-          :key="idx"
-          :class="['calendar-day', day.isToday ? 'is-today' : '', day.moodScore ? 'has-data' : '']"
-          :style="day.bgStyle"
-          @click="day.moodScore && showDayDetail(day)">
-          <text class="day-num">{{ day.dayNum }}</text>
-          <text v-if="day.weather" class="day-emoji-small">{{ weatherEmoji(day.weather) }}</text>
-          <text v-if="day.moodScore" class="day-score">{{ day.moodScore }}</text>
-        </view>
-      </view>
-      <!-- 热力图例 -->
-      <view class="heatmap-legend">
-        <text class="legend-label">低</text>
-        <view class="legend-bar">
-          <view class="legend-step" style="background:#FFE0E0"></view>
-          <view class="legend-step" style="background:#FFB0B0"></view>
-          <view class="legend-step" style="background:#FFD9A0"></view>
-          <view class="legend-step" style="background:#C8E6C9"></view>
-          <view class="legend-step" style="background:#81C784"></view>
-        </view>
-        <text class="legend-label">高</text>
-      </view>
-    </view>
-
-    <!-- 情绪分布 -->
-    <view class="distribution-card">
-      <text class="card-title">情绪分布</text>
-      <view class="distribution-list">
-        <view class="dist-item" v-for="dist in distributionData" :key="dist.weather">
-          <text class="dist-emoji">{{ dist.icon }}</text>
-          <text class="dist-label">{{ dist.label }}</text>
-          <view class="dist-bar-track">
-            <view class="dist-bar-fill" :style="{ width: dist.percent + '%', background: dist.color }"></view>
-          </view>
-          <text class="dist-count">{{ dist.count }}次</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 按天详情(含情绪精细数据) -->
-    <view class="daily-list-card" v-if="dailyData.length > 0">
-      <text class="card-title">每日记录</text>
-      <view class="daily-item" v-for="(day, idx) in dailyData" :key="idx" @click="viewDayCheckin(day)">
-        <text class="daily-date">{{ formatDate(day.date) }}</text>
-        <text class="daily-emoji">{{ weatherEmoji(day.weather) }}</text>
-        <text class="daily-weather">{{ weatherLabel(day.weather) }}</text>
-        <!-- Phase 1.1 精细数据标签 -->
-        <view class="daily-tags" v-if="day.moodScore">
-          <text class="daily-tag score-tag">{{ day.moodScore }}分</text>
-          <text class="daily-tag type-tag" v-if="day.emotionType">{{ emotionTypeLabel(day.emotionType) }}</text>
-          <text class="daily-tag arousal-tag" v-if="day.arousalLevel">激活{{ day.arousalLevel }}</text>
-        </view>
-      </view>
-    </view>
-
-    <view class="empty-state" v-if="!stats || stats.totalCheckins === 0">
-      <text class="empty-icon">&#x1F4AD;</text>
-      <text class="empty-text">还没有情绪打卡记录</text>
-      <button class="empty-btn" @click="goCheckin">去打卡</button>
-    </view>
-  </view>
-</template>
-
-<script>
-import config from '../../config'
-import { parseDate } from '../../utils/format.js'
-
-export default {
-  data() {
-    return {
-      period: 'week',
-      stats: null,
-      trend: null,
-      weekdays: ['日', '一', '二', '三', '四', '五', '六'],
-      weatherConfig: {
-        sunny: { icon: '\u2600\uFE0F', label: '心情很好', color: '#FFB347' },
-        cloudy: { icon: '\u26C5', label: '一般般', color: '#A0A0A0' },
-        rainy: { icon: '\uD83C\uDF27\uFE0F', label: '有点难过', color: '#6B9BD2' },
-        stormy: { icon: '\u26A1', label: '很烦躁', color: '#8B5CF6' },
-        rainbow: { icon: '\uD83C\uDF08', label: '特别开心', color: '#FF6B9D' }
-      },
-      // Plutchik 情绪中文映射
-      emotionTypeLabels: {
-        'joy': '开心', 'trust': '信任', 'fear': '恐惧', 'surprise': '惊讶',
-        'sadness': '悲伤', 'disgust': '厌恶', 'anger': '愤怒', 'anticipation': '期待'
-      },
-
-      // 散点/折线图数据
-      scatterData: [],
-      moodTrendData: [],
-
-      // Canvas 上下文
-      scatterCtx: null,
-      trendCtx: null
-    }
-  },
-
-  computed: {
-    calendarDays() {
-      const days = []
-      const now = new Date()
-      const year = now.getFullYear()
-      const month = now.getMonth()
-      const firstDay = new Date(year, month, 1).getDay()
-      const daysInMonth = new Date(year, month + 1, 0).getDate()
-      const today = now.getDate()
-
-      // Build data map from dailyData
-      const dataMap = {}
-      if (this.trend && this.trend.dailyData) {
-        for (const d of this.trend.dailyData) {
-          if (d.date) {
-            const dateObj = parseDate(d.date)
-            if (!dateObj) continue
-            dataMap[dateObj.getDate()] = d
-          }
-        }
-      }
-
-      // Empty leading cells
-      for (let i = 0; i < firstDay; i++) {
-        days.push({ dayNum: '', weather: null, moodScore: null, isToday: false, bgStyle: '' })
-      }
-
-      for (let d = 1; d <= daysInMonth; d++) {
-        const data = dataMap[d]
-        var bgStyle = ''
-        if (data && data.moodScore) {
-          // Heatmap intensity: moodScore 1-10 mapped to color
-          var score = data.moodScore
-          var r, g, b
-          if (score <= 3) {
-            // 1-3: red tint
-            var intensity1 = (score - 1) / 2
-            r = 255
-            g = Math.round(180 + 75 * intensity1)
-            b = Math.round(180 + 75 * intensity1)
-          } else if (score <= 6) {
-            // 4-6: orange/yellow tint
-            var intensity2 = (score - 4) / 2
-            r = 255
-            g = Math.round(200 + 55 * intensity2)
-            b = Math.round(160 - 60 * intensity2)
-          } else {
-            // 7-10: green tint
-            var intensity3 = (score - 7) / 3
-            r = Math.round(200 - 120 * intensity3)
-            g = Math.round(230 - 20 * intensity3)
-            b = Math.round(200 - 120 * intensity3)
-          }
-          bgStyle = 'background: rgba(' + r + ',' + g + ',' + b + ',0.4)'
-        }
-
-        days.push({
-          dayNum: d,
-          weather: data ? data.weather : null,
-          moodScore: data ? data.moodScore : null,
-          emotionType: data ? data.emotionType : null,
-          arousalLevel: data ? data.arousalLevel : null,
-          isToday: d === today,
-          bgStyle: bgStyle
-        })
-      }
-
-      return days
-    },
-
-    distributionData() {
-      const dist = this.trend && this.trend.weatherDistribution
-      if (!dist) return []
-      const total = Object.values(dist).reduce((a, b) => a + b, 0)
-      if (total === 0) return []
-
-      return Object.keys(dist).map(w => {
-        const cfg = this.weatherConfig[w] || { icon: '?', label: w, color: '#ccc' }
-        return {
-          weather: w,
-          icon: cfg.icon,
-          label: cfg.label,
-          color: cfg.color,
-          count: dist[w],
-          percent: Math.round(dist[w] / total * 100)
-        }
-      })
-    },
-
-    dailyData() {
-      return this.trend && this.trend.dailyData || []
-    }
-  },
-
-  onLoad() {
-    this.loadData()
-  },
-
-  methods: {
-    loadData() {
-      const memberId = uni.getStorageSync('currentChildId')
-      if (!memberId) return
-
-      uni.request({
-        url: config.API_BASE_URL + '/api/mind/checkin/stats',
-        method: 'POST',
-        data: { memberId },
-        success: (res) => {
-          if (res.data && res.data.code === 200) {
-            this.stats = res.data.data
-          }
-        }
-      })
-
-      uni.request({
-        url: config.API_BASE_URL + '/api/mind/checkin/trend',
-        method: 'POST',
-        data: { memberId, period: this.period },
-        success: (res) => {
-          if (res.data && res.data.code === 200) {
-            this.trend = res.data.data
-            // Extract scatter & trend data
-            this.scatterData = this.trend && this.trend.scatterData || []
-            this.moodTrendData = this.trend && this.trend.moodTrend || []
-            // Draw charts after data is ready
-            this.$nextTick(function() {
-              this.drawMoodTrend()
-              this.drawScatter()
-            })
-          }
-        }
-      })
-    },
-
-    switchPeriod(p) {
-      this.period = p
-      this.loadData()
-    },
-
-    weatherEmoji(weather) {
-      const cfg = this.weatherConfig[weather]
-      return cfg ? cfg.icon : ''
-    },
-
-    weatherLabel(weather) {
-      const cfg = this.weatherConfig[weather]
-      return cfg ? cfg.label : weather
-    },
-
-    emotionTypeLabel(type) {
-      return this.emotionTypeLabels[type] || type
-    },
-
-    formatDate(dateStr) {
-      if (!dateStr) return ''
-      const d = parseDate(dateStr)
-      if (!d) return ''
-      return (d.getMonth() + 1) + '/' + d.getDate()
-    },
-
-    goCheckin() {
-      uni.navigateTo({ url: '/pages/mind/emotion-checkin' })
-    },
-
-    showDayDetail(day) {
-      // 点击日历格子可以跳转到当天的打卡详情
-      // 简单 toast 显示评分
-      uni.showToast({ title: '情绪评分: ' + day.moodScore + '/10', icon: 'none' })
-    },
-
-    viewDayCheckin(day) {
-      // 查看单条打卡详情(当前简单响应)
-      if (day.moodScore) {
-        uni.showToast({ title: '情绪评分: ' + day.moodScore + '/10', icon: 'none' })
-      }
-    },
-
-    // ===== Canvas 绘图 =====
-
-    drawMoodTrend() {
-      if (this.moodTrendData.length < 1) return
-
-      var canvasId = 'moodTrendCanvas'
-      var query = uni.createSelectorQuery()
-      var self = this
-
-      query.select('#' + canvasId).fields({ node: true, size: true }).exec(function(res) {
-        // Fallback: use context API for older uni-app
-        var ctx = uni.createCanvasContext(canvasId, self)
-        var dpr = 2
-        // Get canvas dimensions from the element
-        query.select('#' + canvasId).boundingClientRect(function(rect) {
-          if (!rect) return
-          var width = rect.width || 300
-          var height = rect.height || 200
-          var canvasWidth = width * dpr
-          var canvasHeight = height * dpr
-
-          // Clear
-          ctx.clearRect(0, 0, canvasWidth, canvasHeight)
-
-          var data = self.moodTrendData
-          var count = data.length
-          if (count === 0) return
-
-          var padding = { top: 20 * dpr, right: 20 * dpr, bottom: 30 * dpr, left: 30 * dpr }
-          var plotW = canvasWidth - padding.left - padding.right
-          var plotH = canvasHeight - padding.top - padding.bottom
-
-          // Draw axes
-          ctx.setStrokeStyle('#E0E0E0')
-          ctx.setLineWidth(1 * dpr)
-          ctx.beginPath()
-          ctx.moveTo(padding.left, padding.top)
-          ctx.lineTo(padding.left, padding.top + plotH)
-          ctx.lineTo(padding.left + plotW, padding.top + plotH)
-          ctx.stroke()
-
-          // Draw Y axis labels
-          ctx.setFontSize(10 * dpr)
-          ctx.setFillStyle('#999')
-          ctx.setTextAlign('right')
-          for (var yVal = 2; yVal <= 10; yVal += 2) {
-            var yPos = padding.top + plotH - (yVal / 10) * plotH
-            ctx.fillText(yVal + '', padding.left - 6 * dpr, yPos + 3 * dpr)
-            // Grid line
-            ctx.setStrokeStyle('#F0F0F0')
-            ctx.setLineWidth(1 * dpr)
-            ctx.beginPath()
-            ctx.moveTo(padding.left, yPos)
-            ctx.lineTo(padding.left + plotW, yPos)
-            ctx.stroke()
-          }
-
-          // Determine trend line color based on trend direction
-          var avg = 0
-          for (var si = 0; si < count; si++) {
-            avg += data[si].moodScore
-          }
-          avg /= count
-          var lineColor = avg >= 5 ? '#10B981' : '#FF6B9D'
-
-          // Draw data points and lines
-          ctx.setStrokeStyle(lineColor)
-          ctx.setLineWidth(3 * dpr)
-          ctx.setFillStyle(lineColor)
-
-          for (var i = 0; i < count; i++) {
-            var x = padding.left + (i / Math.max(count - 1, 1)) * plotW
-            var y = padding.top + plotH - (data[i].moodScore / 10) * plotH
-
-            if (i === 0) {
-              ctx.beginPath()
-              ctx.moveTo(x, y)
-            } else {
-              ctx.lineTo(x, y)
-            }
-
-            // Draw point circle
-            ctx.beginPath()
-            ctx.arc(x, y, 4 * dpr, 0, 2 * Math.PI)
-            ctx.setFillStyle(lineColor)
-            ctx.fill()
-          }
-          ctx.stroke()
-
-          // Draw X axis labels (dates)
-          ctx.setFontSize(9 * dpr)
-          ctx.setFillStyle('#999')
-          ctx.setTextAlign('center')
-          var step = Math.max(1, Math.floor(count / 6))
-          for (var di = 0; di < count; di += step) {
-            var labelX = padding.left + (di / Math.max(count - 1, 1)) * plotW
-            var labelText = self.formatDate(data[di].date)
-            ctx.fillText(labelText, labelX, padding.top + plotH + 18 * dpr)
-          }
-
-          ctx.draw()
-        }).exec()
-      })
-    },
-
-    drawScatter() {
-      if (this.scatterData.length < 3) return
-
-      var canvasId = 'scatterCanvas'
-      var query = uni.createSelectorQuery()
-      var self = this
-
-      query.select('#' + canvasId).boundingClientRect(function(rect) {
-        if (!rect) return
-        var ctx = uni.createCanvasContext(canvasId, self)
-        var dpr = 2
-        var width = rect.width || 300
-        var height = rect.height || 200
-        var canvasWidth = width * dpr
-        var canvasHeight = height * dpr
-
-        ctx.clearRect(0, 0, canvasWidth, canvasHeight)
-
-        var padding = { top: 20 * dpr, right: 20 * dpr, bottom: 30 * dpr, left: 30 * dpr }
-        var plotW = canvasWidth - padding.left - padding.right
-        var plotH = canvasHeight - padding.top - padding.bottom
-
-        // Draw background with 4 quadrants
-        // Q1 (high arousal, high valence): top-right
-        var midX = padding.left + plotW / 2
-        var midY = padding.top + plotH / 2
-
-        // Quadrant backgrounds
-        ctx.setFillStyle('rgba(255,240,240,0.3)')
-        ctx.fillRect(midX, padding.top, plotW / 2, plotH / 2)  // high arousal, high valence
-        ctx.setFillStyle('rgba(240,240,255,0.3)')
-        ctx.fillRect(padding.left, padding.top, plotW / 2, plotH / 2)  // high arousal, low valence
-        ctx.setFillStyle('rgba(255,248,240,0.3)')
-        ctx.fillRect(midX, midY, plotW / 2, plotH / 2)  // low arousal, high valence
-        ctx.setFillStyle('rgba(245,245,245,0.3)')
-        ctx.fillRect(padding.left, midY, plotW / 2, plotH / 2)  // low arousal, low valence
-
-        // Quadrant labels
-        ctx.setFontSize(10 * dpr)
-        ctx.setFillStyle('rgba(0,0,0,0.15)')
-        ctx.setTextAlign('center')
-        ctx.fillText('兴奋', padding.left + plotW * 0.75, padding.top + plotH * 0.3)
-        ctx.fillText('烦躁', padding.left + plotW * 0.25, padding.top + plotH * 0.3)
-        ctx.fillText('平静', padding.left + plotW * 0.75, padding.top + plotH * 0.8)
-        ctx.fillText('低落', padding.left + plotW * 0.25, padding.top + plotH * 0.8)
-
-        // Axes
-        ctx.setStrokeStyle('#E0E0E0')
-        ctx.setLineWidth(1 * dpr)
-        ctx.beginPath()
-        ctx.moveTo(padding.left, padding.top + plotH / 2)
-        ctx.lineTo(padding.left + plotW, padding.top + plotH / 2)
-        ctx.stroke()
-        ctx.beginPath()
-        ctx.moveTo(padding.left + plotW / 2, padding.top)
-        ctx.lineTo(padding.left + plotW / 2, padding.top + plotH)
-        ctx.stroke()
-
-        // Axis labels
-        ctx.setFontSize(9 * dpr)
-        ctx.setFillStyle('#999')
-        ctx.setTextAlign('center')
-        ctx.fillText('效价 →', padding.left + plotW / 2, padding.top + plotH + 18 * dpr)
-        ctx.save()
-        ctx.translate(padding.left - 4 * dpr, padding.top + plotH / 2)
-        ctx.rotate(-Math.PI / 2)
-        ctx.setTextAlign('center')
-        ctx.fillText('激活度', 0, 0)
-        ctx.restore()
-
-        // Draw scatter points
-        for (var i = 0; i < self.scatterData.length; i++) {
-          var pt = self.scatterData[i]
-          var px = padding.left + (pt.valence / 10) * plotW
-          var py = padding.top + plotH - (pt.arousal / 10) * plotH
-
-          // Color based on valence
-          var color
-          if (pt.valence >= 7) {
-            color = '#10B981'
-          } else if (pt.valence >= 5) {
-            color = '#FFD93D'
-          } else {
-            color = '#FF6B9D'
-          }
-
-          ctx.beginPath()
-          ctx.arc(px, py, 5 * dpr, 0, 2 * Math.PI)
-          ctx.setFillStyle(color)
-          ctx.setGlobalAlpha(0.7)
-          ctx.fill()
-          ctx.setGlobalAlpha(1)
-        }
-
-        ctx.draw()
-      }).exec()
-    }
-  }
-}
-</script>
-
-<style scoped>
-.trend-container {
-  min-height: 100vh;
-  background: #F8F8F8;
-  padding-bottom: 40rpx;
-}
-
-.stats-overview {
-  display: flex;
-  margin: 24rpx 32rpx;
-  background: linear-gradient(135deg, #FF6B9D, #FF8CB3);
-  border-radius: 20rpx;
-  padding: 28rpx 16rpx;
-}
-
-.stat-item {
-  flex: 1;
-  text-align: center;
-}
-
-.stat-value {
-  font-size: 36rpx;
-  font-weight: 700;
-  color: #fff;
-  display: block;
-}
-
-.stat-label {
-  font-size: 22rpx;
-  color: rgba(255, 255, 255, 0.8);
-  margin-top: 6rpx;
-  display: block;
-}
-
-.period-tabs {
-  display: flex;
-  margin: 0 32rpx;
-  background: #fff;
-  border-radius: 12rpx;
-  overflow: hidden;
-}
-
-.period-tab {
-  flex: 1;
-  text-align: center;
-  padding: 16rpx 0;
-  font-size: 26rpx;
-  color: #666;
-}
-
-.period-active {
-  background: #FF6B9D;
-  color: #fff;
-  font-weight: 600;
-}
-
-/* ===== 图表卡片 ===== */
-.chart-card {
-  margin: 24rpx 32rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 28rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
-}
-
-.chart-card .card-title {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: #333;
-  display: block;
-  margin-bottom: 4rpx;
-}
-
-.chart-card .card-subtitle {
-  font-size: 22rpx;
-  color: #999;
-  display: block;
-  margin-bottom: 16rpx;
-}
-
-.line-chart-canvas,
-.scatter-canvas {
-  width: 100%;
-  height: 360rpx;
-  border-radius: 12rpx;
-  background: #FCFCFC;
-}
-
-.calendar-card,
-.distribution-card,
-.daily-list-card {
-  margin: 24rpx 32rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 28rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
-}
-
-.card-title {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: #333;
-  display: block;
-  margin-bottom: 20rpx;
-}
-
-.calendar-grid {
-  display: flex;
-  flex-wrap: wrap;
-}
-
-.calendar-weekday {
-  width: 14.28%;
-  text-align: center;
-  font-size: 22rpx;
-  color: #999;
-  padding: 10rpx 0;
-}
-
-.calendar-day {
-  width: 14.28%;
-  text-align: center;
-  padding: 8rpx 0;
-  min-height: 80rpx;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  border-radius: 8rpx;
-}
-
-.calendar-day.has-data {
-  cursor: pointer;
-}
-
-.is-today {
-  border: 2rpx solid #FF6B9D;
-  box-sizing: border-box;
-}
-
-.day-num {
-  font-size: 24rpx;
-  color: #333;
-}
-
-.day-emoji-small {
-  font-size: 20rpx;
-  margin-top: 2rpx;
-}
-
-.day-score {
-  font-size: 18rpx;
-  color: #888;
-  margin-top: 2rpx;
-}
-
-/* 热力图例 */
-.heatmap-legend {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 12rpx;
-  margin-top: 16rpx;
-}
-
-.legend-label {
-  font-size: 20rpx;
-  color: #999;
-}
-
-.legend-bar {
-  display: flex;
-  gap: 2rpx;
-  border-radius: 6rpx;
-  overflow: hidden;
-}
-
-.legend-step {
-  width: 24rpx;
-  height: 16rpx;
-}
-
-/* ===== 情绪分布 ===== */
-.distribution-list {
-  display: flex;
-  flex-direction: column;
-  gap: 16rpx;
-}
-
-.dist-item {
-  display: flex;
-  align-items: center;
-  gap: 16rpx;
-}
-
-.dist-emoji {
-  font-size: 32rpx;
-  width: 48rpx;
-  text-align: center;
-}
-
-.dist-label {
-  font-size: 24rpx;
-  color: #666;
-  width: 120rpx;
-}
-
-.dist-bar-track {
-  flex: 1;
-  height: 20rpx;
-  background: #F0F0F0;
-  border-radius: 10rpx;
-  overflow: hidden;
-}
-
-.dist-bar-fill {
-  height: 100%;
-  border-radius: 10rpx;
-  transition: width 0.3s;
-}
-
-.dist-count {
-  font-size: 24rpx;
-  color: #999;
-  width: 60rpx;
-  text-align: right;
-}
-
-/* ===== 每日记录 ===== */
-.daily-item {
-  display: flex;
-  align-items: center;
-  padding: 16rpx 0;
-  border-bottom: 2rpx solid #F5F5F5;
-  gap: 16rpx;
-}
-
-.daily-date {
-  font-size: 24rpx;
-  color: #999;
-  width: 100rpx;
-  flex-shrink: 0;
-}
-
-.daily-emoji {
-  font-size: 32rpx;
-  width: 40rpx;
-  text-align: center;
-  flex-shrink: 0;
-}
-
-.daily-weather {
-  font-size: 26rpx;
-  color: #666;
-  width: 120rpx;
-  flex-shrink: 0;
-}
-
-.daily-tags {
-  display: flex;
-  gap: 8rpx;
-  flex-wrap: wrap;
-}
-
-.daily-tag {
-  font-size: 20rpx;
-  padding: 4rpx 12rpx;
-  border-radius: 12rpx;
-  color: #fff;
-}
-
-.score-tag { background: #FF6B9D; }
-.type-tag { background: #8B5CF6; }
-.arousal-tag { background: #3B82F6; }
-
-.empty-state {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 120rpx 32rpx;
-}
-
-.empty-icon {
-  font-size: 80rpx;
-  margin-bottom: 20rpx;
-}
-
-.empty-text {
-  font-size: 28rpx;
-  color: #999;
-  margin-bottom: 40rpx;
-}
-
-.empty-btn {
-  background: #FF6B9D;
-  color: #fff;
-  padding: 16rpx 60rpx;
-  border-radius: 40rpx;
-  font-size: 28rpx;
-}
-</style>

+ 0 - 251
cfc-frontend/pages/mind/member-mind-detail.vue

@@ -1,251 +0,0 @@
-<template>
-  <view class="detail-container">
-    <!-- 顶部导航 -->
-
-    <!-- 五维能量条 -->
-    <FamilyEnergyBar
-      dimensionCode="mind"
-      :sandboxData="sandboxData"
-      :dualDimension="dualDimension" />
-
-    <!-- 成员信息卡 -->
-    <view class="member-card" v-if="memberInfo">
-      <view class="member-avatar mind-avatar">
-        <text class="avatar-text">{{ memberInfo.name && memberInfo.name.charAt(0) || '孩' }}</text>
-      </view>
-      <view class="member-info">
-        <text class="member-name">{{ memberInfo.name || '孩子' }}</text>
-        <text class="member-role">心智维度</text>
-      </view>
-      <view class="member-score" v-if="dualDimension">
-        <text class="score-value mind-score">{{ dualDimension.energy || 0 }}</text>
-        <text class="score-label">能量值</text>
-      </view>
-    </view>
-
-    <!-- 家庭成员关系图谱(只读) -->
-    <FamilyRelationGraph
-      dimensionCode="mind"
-      :selfId="selfId"
-      :members="graphMembers"
-      :energyMap="energyMapForGraph"
-      :intimacyMap="intimacyMapForGraph"
-      :interactive="false" />
-
-    <!-- 今日任务 -->
-    <DimensionTasks
-      :memberId="memberId"
-      @taskClick="onTaskClick"
-      @moreTasks="goTasks" />
-
-    <!-- 活动 -->
-    <DimensionActivities
-      :isLoggedIn="true"
-      @activityClick="goActivityDetail"
-      @moreActivities="goMoreActivities" />
-
-    <!-- 商品 -->
-    <DimensionProducts
-      :isLoggedIn="true"
-      @productClick="goProductDetail"
-      @moreProducts="goMoreProducts" />
-
-    <!-- 底部占位 -->
-    <view class="bottom-spacer"></view>
-  </view>
-</template>
-
-<script>
-import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
-import FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
-import DimensionTasks from '../../components/DimensionTasks.vue'
-import DimensionActivities from '../../components/DimensionActivities.vue'
-import DimensionProducts from '../../components/DimensionProducts.vue'
-import { getEnergyOverview, getFamilyEnergySandbox, getChildren } from '../../utils/api.js'
-
-export default {
-  components: { FamilyEnergyBar, FamilyRelationGraph, DimensionTasks, DimensionActivities, DimensionProducts },
-  data() {
-    return {
-      memberId: null,
-      selfId: null,
-      memberInfo: null,
-      sandboxData: null,
-      dualDimension: null
-    }
-  },
-  computed: {
-    graphMembers: function() {
-      if (!this.sandboxData || !this.sandboxData.members) return []
-      return this.sandboxData.members.map(function(m) {
-        return {
-          id: m.memberId || m.id,
-          nickname: m.name || m.nickname || '成员',
-          memberType: m.memberType || 'child'
-        }
-      })
-    },
-    energyMapForGraph: function() {
-      var map = {}
-      if (this.sandboxData && this.sandboxData.members) {
-        for (var i = 0; i < this.sandboxData.members.length; i++) {
-          var m = this.sandboxData.members[i]
-          map[m.memberId || m.id] = {
-            bodyScore: m.bodyScore || 0,
-            mindScore: m.mindScore || 0,
-            actionScore: m.actionScore || 0
-          }
-        }
-      }
-      return map
-    },
-    intimacyMapForGraph: function() {
-      return {}
-    }
-  },
-  onLoad: function(options) {
-    this.memberId = options.memberId || uni.getStorageSync('currentChildId') || null
-  },
-  onShow: function() {
-    this.selfId = uni.getStorageSync('userId') || null
-    this.loadChildren()
-    this.loadSandboxData()
-    if (this.memberId) {
-      this.loadEnergyData()
-    }
-  },
-  methods: {
-    loadChildren: function() {
-      var self = this
-      getChildren().then(function(res) {
-        if (res.code === 200 && res.data) {
-          for (var i = 0; i < res.data.length; i++) {
-            if (res.data[i].memberId === self.memberId) {
-              self.memberInfo = res.data[i]
-              break
-            }
-          }
-          if (!self.memberInfo && res.data.length > 0) {
-            self.memberInfo = res.data[0]
-            self.memberId = res.data[0].memberId
-          }
-        }
-      }).catch(function() {})
-    },
-    loadSandboxData: function() {
-      var self = this
-      getFamilyEnergySandbox().then(function(res) {
-        if (res && res.data) self.sandboxData = res.data
-      }).catch(function() {})
-    },
-    loadEnergyData: function() {
-      var self = this
-      getEnergyOverview(this.memberId).then(function(res) {
-        if (res && res.data) {
-          var dims = res.data.dimensions
-          if (dims && dims.length > 0) {
-            for (var i = 0; i < dims.length; i++) {
-              if (dims[i].code === 'mind') {
-                self.dualDimension = dims[i]
-                break
-              }
-            }
-          }
-        }
-      }).catch(function() {})
-    },
-    onTaskClick: function(task) {
-      uni.navigateTo({ url: '/pages/tasks/tasks' })
-    },
-    goActivityDetail: function(act) {
-      if (act && act.id) uni.navigateTo({ url: '/pages/activity/activity-detail/activity-detail?id=' + act.id })
-    },
-    goMoreActivities: function() {
-      uni.navigateTo({ url: '/pages/activity/index' })
-    },
-    goProductDetail: function(prod) {
-      if (prod && prod.id) uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + prod.id })
-    },
-    goMoreProducts: function() {
-      uni.navigateTo({ url: '/pages/shop/index/index' })
-    },
-    goTasks: function() { uni.navigateTo({ url: '/pages/tasks/tasks' }) },
-    goBack: function() { uni.navigateBack() }
-  }
-}
-</script>
-
-<style scoped>
-.detail-container {
-  min-height: 100vh;
-  background: #f5f7fa;
-  padding-bottom: 40rpx;
-}
-.nav-back-icon {
-  font-size: 36rpx;
-  color: #333;
-}
-.member-card {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  margin: 20rpx 30rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 24rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-.member-avatar {
-  width: 80rpx;
-  height: 80rpx;
-  border-radius: 40rpx;
-  background: linear-gradient(135deg, #FF6B35, #FBBF24);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 20rpx;
-}
-.mind-avatar {
-  background: linear-gradient(135deg, #8B5CF6, #A78BFA);
-}
-.avatar-text {
-  font-size: 32rpx;
-  color: #fff;
-  font-weight: bold;
-}
-.member-info {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-}
-.member-name {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #333;
-}
-.member-role {
-  font-size: 24rpx;
-  color: #999;
-  margin-top: 4rpx;
-}
-.member-score {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-}
-.score-value {
-  font-size: 40rpx;
-  font-weight: bold;
-  color: #FF6B35;
-}
-.mind-score {
-  color: #8B5CF6;
-}
-.score-label {
-  font-size: 22rpx;
-  color: #999;
-}
-.bottom-spacer {
-  height: 40rpx;
-}
-</style>

+ 0 - 703
cfc-frontend/pages/mind/screening.vue

@@ -1,703 +0,0 @@
-<template>
-  <view class="screen-container">
-    <!-- 头部 -->
-    <view class="screen-header">
-      <text class="screen-title">心理状态自评</text>
-      <text class="screen-subtitle">定期评估,了解自己的心理状态</text>
-    </view>
-
-    <!-- 量表选择 -->
-    <view class="section" v-if="!currentScreen">
-      <view class="scale-card" @click="startScreen('PHQ9')">
-        <view class="scale-icon-wrap" style="background:#FFF0F5">
-          <text class="scale-icon">&#x1F9E0;</text>
-        </view>
-        <view class="scale-info">
-          <text class="scale-name">PHQ-9 抑郁筛查</text>
-          <text class="scale-desc">过去两周,您是否被以下问题困扰?</text>
-        </view>
-        <text class="scale-arrow">&gt;</text>
-      </view>
-      <view class="scale-card" @click="startScreen('GAD7')">
-        <view class="scale-icon-wrap" style="background:#F0F4FF">
-          <text class="scale-icon">&#x1F4A8;</text>
-        </view>
-        <view class="scale-info">
-          <text class="scale-name">GAD-7 焦虑筛查</text>
-          <text class="scale-desc">过去两周,您是否被以下问题困扰?</text>
-        </view>
-        <text class="scale-arrow">&gt;</text>
-      </view>
-    </view>
-
-    <!-- 问卷页面 -->
-    <view class="section" v-if="currentScreen">
-      <view class="quiz-header">
-        <text class="quiz-title">{{ currentScreen === 'PHQ9' ? 'PHQ-9 抑郁筛查' : 'GAD-7 焦虑筛查' }}</text>
-        <text class="quiz-progress">{{ currentStep + 1 }}/{{ questions.length }}</text>
-      </view>
-      <view class="progress-track">
-        <view class="progress-fill" :style="{ width: ((currentStep + 1) / questions.length * 100) + '%' }"></view>
-      </view>
-
-      <!-- 当前问题 -->
-      <view class="question-card">
-        <text class="question-num">Q{{ currentStep + 1 }}</text>
-        <text class="question-text">{{ questions[currentStep] }}</text>
-      </view>
-
-      <!-- 选项 -->
-      <view class="options-list">
-        <view
-          v-for="(opt, oi) in answerOptions"
-          :key="oi"
-          :class="['option-btn', answers[currentStep] === oi ? 'option-active' : '']"
-          @click="selectAnswer(oi)">
-          <view :class="['option-radio', answers[currentStep] === oi ? 'radio-checked' : '']">
-            <view v-if="answers[currentStep] === oi" class="radio-dot"></view>
-          </view>
-          <text class="option-text">{{ opt }}</text>
-        </view>
-      </view>
-
-      <!-- 导航按钮 -->
-      <view class="quiz-nav">
-        <button class="nav-btn prev-btn" v-if="currentStep > 0" @click="prevStep">上一题</button>
-        <button class="nav-btn next-btn" v-if="currentStep < questions.length - 1" @click="nextStep">下一题</button>
-        <button class="nav-btn submit-btn" v-if="currentStep === questions.length - 1" @click="submitScreen">提交问卷</button>
-      </view>
-    </view>
-
-    <!-- 结果页面 -->
-    <view class="section" v-if="result">
-      <view class="result-header">
-        <text class="result-type">{{ currentScreen === 'PHQ9' ? 'PHQ-9 抑郁筛查' : 'GAD-7 焦虑筛查' }}</text>
-        <view :class="['result-score-circle', severityClass]">
-          <text class="result-score">{{ result.totalScore }}</text>
-          <text class="result-score-label">分</text>
-        </view>
-        <text :class="['result-severity', severityClass]">{{ severityLabel }}</text>
-      </view>
-
-      <!-- 自动建议 -->
-      <view class="suggestion-card">
-        <text class="suggestion-title">评估建议</text>
-        <text class="suggestion-text">{{ result.suggestions }}</text>
-      </view>
-
-      <!-- 风险标志 -->
-      <view class="risk-section" v-if="result.riskFlags && result.riskFlags.length > 0">
-        <view class="risk-item" v-for="flag in result.riskFlags" :key="flag">
-          <text class="risk-icon">&#x26A0;&#xFE0F;</text>
-          <text class="risk-text">{{ riskFlagLabel(flag) }}</text>
-        </view>
-      </view>
-
-      <!-- 危机热线 -->
-      <view class="crisis-banner" v-if="showCrisisBanner">
-        <text class="crisis-title">&#x1F6A8; 需要帮助?</text>
-        <text class="crisis-phone">全国心理危机干预热线</text>
-        <text class="crisis-number">400-161-9995</text>
-        <text class="crisis-hint">24小时 · 免费 · 专业</text>
-      </view>
-
-      <view class="result-actions">
-        <button class="action-btn primary-btn" @click="goTrend">查看情绪趋势</button>
-        <button class="action-btn secondary-btn" @click="resetScreen">再测一次</button>
-      </view>
-    </view>
-
-    <!-- 提交中 / 历史记录  -->
-    <view class="loading-mask" v-if="submitting">
-      <view class="loading-box">
-        <text class="loading-text">提交中...</text>
-      </view>
-    </view>
-  </view>
-</template>
-
-<script>
-import config from '../../config'
-
-export default {
-  data() {
-    return {
-      currentScreen: null,    // null / 'PHQ9' / 'GAD7'
-      currentStep: 0,
-      answers: [],
-      submitting: false,
-      result: null,
-
-      // PHQ-9 题目
-      phq9Questions: [
-        '做事时提不起劲或没有兴趣',
-        '感到心情低落、沮丧或绝望',
-        '入睡困难、睡不安稳或睡眠过多',
-        '感觉疲倦或没有活力',
-        '食欲不振或吃太多',
-        '觉得自己很糟——或觉得自己很失败,或让自己或家人失望',
-        '对事物专注有困难,例如阅读报纸或看电视时',
-        '行动或说话速度缓慢到别人已经觉察?或正好相反——比平常更多话',
-        '有不如死掉或用某种方式伤害自己的念头'
-      ],
-
-      // GAD-7 题目
-      gad7Questions: [
-        '感到紧张、焦虑或烦躁',
-        '无法停止或控制担忧',
-        '对各种各样的事情担忧过多',
-        '很难放松下来',
-        '由于不安而无法静坐',
-        '变得容易烦恼或急躁',
-        '感到害怕,好像可能发生可怕的事情'
-      ],
-
-      answerOptions: [
-        '完全不会',
-        '好几天',
-        '一半以上的天数',
-        '几乎每天'
-      ],
-
-      riskFlagLabels: {
-        'suicide_ideation': '存在自杀/自伤相关念头,请立即寻求帮助',
-        'core_depression_symptoms': '核心抑郁症状明显,建议关注',
-        'core_anxiety_symptoms': '核心焦虑症状明显,建议关注'
-      }
-    }
-  },
-
-  computed: {
-    questions() {
-      if (this.currentScreen === 'PHQ9') return this.phq9Questions
-      if (this.currentScreen === 'GAD7') return this.gad7Questions
-      return []
-    },
-
-    severityLabel() {
-      if (!this.result) return ''
-      var map = {
-        'none': '状态良好',
-        'mild': '轻度',
-        'moderate': '中度',
-        'moderately_severe': '中重度',
-        'severe': '重度'
-      }
-      return map[this.result.severityLevel] || this.result.severityLevel
-    },
-
-    severityClass() {
-      if (!this.result) return ''
-      var map = {
-        'none': 'severity-good',
-        'mild': 'severity-mild',
-        'moderate': 'severity-moderate',
-        'moderately_severe': 'severity-moderate',
-        'severe': 'severity-severe'
-      }
-      return map[this.result.severityLevel] || ''
-    },
-
-    showCrisisBanner() {
-      if (!this.result || !this.result.riskFlags) return false
-      return this.result.riskFlags.indexOf('suicide_ideation') !== -1
-    }
-  },
-
-  onLoad() {
-    // 加载历史记录(可选)
-  },
-
-  methods: {
-    startScreen(type) {
-      this.currentScreen = type
-      this.currentStep = 0
-      this.answers = []
-      this.result = null
-      var count = type === 'PHQ9' ? 9 : 7
-      for (var i = 0; i < count; i++) {
-        this.answers.push(-1)
-      }
-    },
-
-    selectAnswer(oi) {
-      var newAnswers = this.answers.slice()
-      newAnswers[this.currentStep] = oi
-      this.answers = newAnswers
-    },
-
-    nextStep() {
-      if (this.answers[this.currentStep] === -1) {
-        uni.showToast({ title: '请先选择一项', icon: 'none' })
-        return
-      }
-      this.currentStep++
-    },
-
-    prevStep() {
-      if (this.currentStep > 0) this.currentStep--
-    },
-
-    submitScreen() {
-      // 检查所有题目是否已回答
-      for (var i = 0; i < this.answers.length; i++) {
-        if (this.answers[i] === -1) {
-          uni.showToast({ title: '请回答所有问题', icon: 'none' })
-          this.currentStep = i
-          return
-        }
-      }
-
-      this.submitting = true
-
-      const memberId = uni.getStorageSync('currentChildId')
-
-      uni.request({
-        url: config.API_BASE_URL + '/api/mind/screening/submit',
-        method: 'POST',
-        data: {
-          memberId: memberId,
-          screenType: this.currentScreen,
-          answers: this.answers
-        },
-        success: (res) => {
-          this.submitting = false
-          if (res.data && res.data.code === 200) {
-            this.result = res.data.data
-          } else {
-            uni.showToast({ title: res.data && res.data.message || '提交失败', icon: 'none' })
-          }
-        },
-        fail: () => {
-          this.submitting = false
-          uni.showToast({ title: '网络错误', icon: 'none' })
-        }
-      })
-    },
-
-    resetScreen() {
-      this.currentScreen = null
-      this.currentStep = 0
-      this.answers = []
-      this.result = null
-    },
-
-    goTrend() {
-      uni.navigateTo({ url: '/pages/mind/emotion-trend' })
-    },
-
-    riskFlagLabel(flag) {
-      return this.riskFlagLabels[flag] || flag
-    }
-  }
-}
-</script>
-
-<style scoped>
-.screen-container {
-  min-height: 100vh;
-  background: linear-gradient(180deg, #F0F4FF 0%, #FFFFFF 100%);
-  padding-bottom: 40rpx;
-}
-
-.screen-header {
-  padding: 40rpx 32rpx 20rpx;
-}
-
-.screen-title {
-  font-size: 36rpx;
-  font-weight: 700;
-  color: #333;
-  display: block;
-}
-
-.screen-subtitle {
-  font-size: 26rpx;
-  color: #999;
-  margin-top: 8rpx;
-  display: block;
-}
-
-.section {
-  margin: 20rpx 32rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 28rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
-}
-
-/* ===== 量表选择 ===== */
-.scale-card {
-  display: flex;
-  align-items: center;
-  padding: 24rpx;
-  background: #FAFAFA;
-  border-radius: 16rpx;
-  margin-bottom: 16rpx;
-  gap: 20rpx;
-}
-
-.scale-card:active {
-  background: #F0F0F0;
-}
-
-.scale-icon-wrap {
-  width: 72rpx;
-  height: 72rpx;
-  border-radius: 36rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-}
-
-.scale-icon {
-  font-size: 36rpx;
-}
-
-.scale-info {
-  flex: 1;
-}
-
-.scale-name {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: #333;
-  display: block;
-}
-
-.scale-desc {
-  font-size: 22rpx;
-  color: #999;
-  margin-top: 4rpx;
-  display: block;
-}
-
-.scale-arrow {
-  font-size: 28rpx;
-  color: #ccc;
-}
-
-/* ===== 问卷 ===== */
-.quiz-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 16rpx;
-}
-
-.quiz-title {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: #333;
-}
-
-.quiz-progress {
-  font-size: 24rpx;
-  color: #999;
-}
-
-.progress-track {
-  height: 6rpx;
-  background: #F0F0F0;
-  border-radius: 3rpx;
-  margin-bottom: 24rpx;
-  overflow: hidden;
-}
-
-.progress-fill {
-  height: 100%;
-  background: linear-gradient(90deg, #6366F1, #8B5CF6);
-  border-radius: 3rpx;
-  transition: width 0.3s;
-}
-
-.question-card {
-  background: #F8F6FF;
-  border-radius: 16rpx;
-  padding: 24rpx;
-  margin-bottom: 24rpx;
-}
-
-.question-num {
-  font-size: 22rpx;
-  color: #8B5CF6;
-  font-weight: 600;
-  display: block;
-  margin-bottom: 8rpx;
-}
-
-.question-text {
-  font-size: 30rpx;
-  color: #333;
-  line-height: 1.5;
-}
-
-.options-list {
-  display: flex;
-  flex-direction: column;
-  gap: 12rpx;
-}
-
-.option-btn {
-  display: flex;
-  align-items: center;
-  padding: 20rpx 24rpx;
-  border-radius: 14rpx;
-  border: 2rpx solid #F0F0F0;
-  gap: 16rpx;
-}
-
-.option-active {
-  border-color: #8B5CF6;
-  background: #F8F6FF;
-}
-
-.option-radio {
-  width: 40rpx;
-  height: 40rpx;
-  border-radius: 20rpx;
-  border: 3rpx solid #D0D0D0;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-}
-
-.radio-checked {
-  border-color: #8B5CF6;
-}
-
-.radio-dot {
-  width: 22rpx;
-  height: 22rpx;
-  border-radius: 11rpx;
-  background: #8B5CF6;
-}
-
-.option-text {
-  font-size: 28rpx;
-  color: #444;
-}
-
-.option-active .option-text {
-  color: #333;
-  font-weight: 500;
-}
-
-.quiz-nav {
-  display: flex;
-  gap: 16rpx;
-  margin-top: 32rpx;
-}
-
-.nav-btn {
-  flex: 1;
-  height: 80rpx;
-  line-height: 80rpx;
-  border-radius: 40rpx;
-  font-size: 28rpx;
-  text-align: center;
-}
-
-.prev-btn {
-  background: #F0F0F0;
-  color: #666;
-}
-
-.next-btn {
-  background: #6366F1;
-  color: #fff;
-}
-
-.submit-btn {
-  background: linear-gradient(135deg, #6366F1, #8B5CF6);
-  color: #fff;
-}
-
-/* ===== 结果 ===== */
-.result-header {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 20rpx 0;
-}
-
-.result-type {
-  font-size: 24rpx;
-  color: #999;
-  margin-bottom: 16rpx;
-}
-
-.result-score-circle {
-  width: 120rpx;
-  height: 120rpx;
-  border-radius: 60rpx;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  margin-bottom: 12rpx;
-}
-
-.result-score {
-  font-size: 48rpx;
-  font-weight: 700;
-  color: #fff;
-  line-height: 1;
-}
-
-.result-score-label {
-  font-size: 20rpx;
-  color: rgba(255,255,255,0.8);
-  margin-top: 2rpx;
-}
-
-.result-severity {
-  font-size: 30rpx;
-  font-weight: 600;
-}
-
-.severity-good { background: #10B981; color: #10B981; }
-.severity-mild { background: #F59E0B; color: #F59E0B; }
-.severity-moderate { background: #FF8C42; color: #FF8C42; }
-.severity-severe { background: #EF4444; color: #EF4444; }
-
-.result-score-circle.severity-good { background: #10B981; }
-.result-score-circle.severity-mild { background: #F59E0B; }
-.result-score-circle.severity-moderate { background: #FF8C42; }
-.result-score-circle.severity-severe { background: #EF4444; }
-
-.suggestion-card {
-  background: #F0FFF4;
-  border-radius: 14rpx;
-  padding: 20rpx;
-  margin-top: 16rpx;
-}
-
-.suggestion-title {
-  font-size: 24rpx;
-  color: #10B981;
-  font-weight: 600;
-  display: block;
-  margin-bottom: 8rpx;
-}
-
-.suggestion-text {
-  font-size: 26rpx;
-  color: #555;
-  line-height: 1.6;
-}
-
-.risk-section {
-  margin-top: 16rpx;
-}
-
-.risk-item {
-  display: flex;
-  align-items: flex-start;
-  gap: 8rpx;
-  padding: 12rpx 16rpx;
-  background: #FFF5F5;
-  border-radius: 12rpx;
-  margin-bottom: 8rpx;
-}
-
-.risk-icon {
-  font-size: 24rpx;
-  flex-shrink: 0;
-}
-
-.risk-text {
-  font-size: 24rpx;
-  color: #E53E3E;
-  line-height: 1.5;
-}
-
-.crisis-banner {
-  margin-top: 16rpx;
-  padding: 24rpx;
-  background: linear-gradient(135deg, #FF6B6B, #E53E3E);
-  border-radius: 16rpx;
-  text-align: center;
-}
-
-.crisis-title {
-  font-size: 28rpx;
-  color: #fff;
-  font-weight: 700;
-  display: block;
-}
-
-.crisis-phone {
-  font-size: 22rpx;
-  color: rgba(255,255,255,0.8);
-  margin-top: 12rpx;
-  display: block;
-}
-
-.crisis-number {
-  font-size: 40rpx;
-  color: #fff;
-  font-weight: 700;
-  margin-top: 4rpx;
-  display: block;
-  letter-spacing: 2rpx;
-}
-
-.crisis-hint {
-  font-size: 20rpx;
-  color: rgba(255,255,255,0.7);
-  margin-top: 4rpx;
-  display: block;
-}
-
-.result-actions {
-  display: flex;
-  gap: 16rpx;
-  margin-top: 28rpx;
-}
-
-.action-btn {
-  flex: 1;
-  height: 76rpx;
-  line-height: 76rpx;
-  border-radius: 38rpx;
-  font-size: 26rpx;
-  text-align: center;
-}
-
-.primary-btn {
-  background: #FF6B9D;
-  color: #fff;
-}
-
-.secondary-btn {
-  background: #F0F0F0;
-  color: #666;
-}
-
-.loading-mask {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  background: rgba(0, 0, 0, 0.3);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  z-index: 999;
-}
-
-.loading-box {
-  background: #fff;
-  padding: 40rpx 60rpx;
-  border-radius: 20rpx;
-}
-
-.loading-text {
-  font-size: 28rpx;
-  color: #666;
-}
-</style>

+ 0 - 396
cfc-frontend/pages/mind/soothe-toolbox.vue

@@ -1,396 +0,0 @@
-<template>
-  <view class="toolbox-container">
-    <!-- ===== 4-7-8 呼吸练习 ===== -->
-    <view class="tool-section">
-      <view class="section-header">
-        <text class="section-emoji">&#x1F9D8;</text>
-        <text class="section-title">4-7-8 呼吸练习</text>
-      </view>
-      <text class="section-desc">用深呼吸让自己平静下来</text>
-
-      <view class="breathing-area" @click="toggleBreathing">
-        <view :class="['breath-circle', breathPhase]">
-          <text class="breath-text">{{ breathInstruction }}</text>
-        </view>
-      </view>
-      <text class="breath-hint" v-if="!breathing">点击开始呼吸练习</text>
-      <text class="breath-count" v-if="breathing">第 {{ breathRound }} 轮</text>
-    </view>
-
-    <!-- ===== 正念练习 ===== -->
-    <view class="tool-section">
-      <view class="section-header">
-        <text class="section-emoji">&#x1F4A1;</text>
-        <text class="section-title">正念练习</text>
-      </view>
-      <view class="mindfulness-cards">
-        <view
-          :class="['mind-card', activeMindCard === idx ? 'mind-card-active' : '']"
-          v-for="(item, idx) in mindfulnessItems"
-          :key="idx"
-          @click="activeMindCard = idx">
-          <text class="mind-card-title">{{ item.title }}</text>
-          <text class="mind-card-desc">{{ item.desc }}</text>
-        </view>
-      </view>
-      <view class="mindfulness-content" v-if="activeMindCard !== -1">
-        <text class="mindfulness-text">{{ mindfulnessItems[activeMindCard].content }}</text>
-      </view>
-    </view>
-
-    <!-- ===== 情绪涂鸦 ===== -->
-    <view class="tool-section">
-      <view class="section-header">
-        <text class="section-emoji">&#x1F3A8;</text>
-        <text class="section-title">情绪涂鸦</text>
-      </view>
-      <text class="section-desc">在纸上自由画下你现在的情绪</text>
-      <view class="doodle-suggestions">
-        <view class="doodle-item" v-for="(item, idx) in doodlePrompts" :key="idx">
-          <text class="doodle-icon">{{ item.icon }}</text>
-          <text class="doodle-text">{{ item.text }}</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- ===== 运动释压 ===== -->
-    <view class="tool-section">
-      <view class="section-header">
-        <text class="section-emoji">&#x1F3CB;</text>
-        <text class="section-title">运动释压</text>
-      </view>
-      <text class="section-desc">简单的伸展动作,释放身体紧张</text>
-      <view class="exercise-list">
-        <view class="exercise-item" v-for="(ex, idx) in exercises" :key="idx">
-          <text class="exercise-name">{{ ex.name }}</text>
-          <text class="exercise-desc">{{ ex.desc }}</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 底部提示 -->
-    <view class="disclaimer">
-      <text class="disclaimer-text">以上内容仅供参考,不能替代专业心理咨询或医疗建议。如有需要,请寻求专业帮助。</text>
-    </view>
-  </view>
-</template>
-
-<script>
-export default {
-  data() {
-    return {
-      breathing: false,
-      breathPhase: 'inhal',
-      breathInstruction: '吸气',
-      breathRound: 0,
-      breathTimer: null,
-      activeMindCard: -1,
-      mindfulnessItems: [
-        {
-          title: '5-4-3-2-1 感官练习',
-          desc: '快速回归当下',
-          content: '找一个舒服的姿势坐下,深呼吸三次。然后:\n\n看:说出你看到的 5 样东西\n听:说出你听到的 4 种声音\n触:感受你身体的 3 处触觉\n闻:闻到的 2 种气味\n味:品尝到的 1 种味道\n\n这个练习能帮你快速从焦虑中回到当下。'
-        },
-        {
-          title: '身体扫描',
-          desc: '放松全身',
-          content: '闭上眼睛,把注意力带到脚趾。感受脚趾的温度和触感。\n\n慢慢向上移动注意力:脚掌 → 脚踝 → 小腿 → 膝盖 → 大腿 → 腹部 → 胸部 → 手指 → 手臂 → 肩膀 → 脖子 → 面部 → 头顶。\n\n在每个部位停留 3-5 秒,感受那里的感觉。'
-        },
-        {
-          title: '感恩三件事',
-          desc: '培养积极心态',
-          content: '想一想今天让你感到感恩的三件事。可以是很小的事:\n\n1. 今天吃到了一顿美味的饭\n2. 有人对你说了一句温暖的话\n3. 今天的天气很好\n4. 你学会了一样新东西\n5. 帮助了别人\n\n在心里默默感谢这些美好。'
-        }
-      ],
-      doodlePrompts: [
-        { icon: '\u{1F308}', text: '画出你今天的情绪彩虹' },
-        { icon: '\u{1F33F}', text: '画一棵代表你内心的小树' },
-        { icon: '\u{1F30A}', text: '画出心情波浪' },
-        { icon: '\u{1F31F}', text: '画出你心中的星星' }
-      ],
-      exercises: [
-        { name: '耸肩放松', desc: '双肩用力向上耸起(靠近耳朵),保持 5 秒,然后突然放松。重复 5 次。' },
-        { name: '颈部伸展', desc: '慢慢将头向右侧倾斜,右手轻压头部,保持 15 秒。换左侧重复。' },
-        { name: '猫牛式', desc: '双手双膝着地,吸气时塌腰抬头(牛式),呼气时弓背低头(猫式)。重复 10 次。' },
-        { name: '蝴蝶式', desc: '坐姿,双脚掌相对,双手握住脚踝,膝盖向两侧打开轻轻上下抖动,像蝴蝶扇动翅膀。' }
-      ]
-    }
-  },
-
-  methods: {
-    toggleBreathing() {
-      if (this.breathing) {
-        this.stopBreathing()
-      } else {
-        this.startBreathing()
-      }
-    },
-
-    startBreathing() {
-      this.breathing = true
-      this.breathRound = 1
-      this.doBreathPhase('inhal', 4)
-    },
-
-    doBreathPhase(phase, seconds) {
-      var self = this
-      if (!self.breathing) return
-
-      self.breathPhase = phase
-      var instructions = {
-        inhal: '吸气...',
-        hold: '屏息...',
-        exhale: '呼气...'
-      }
-      self.breathInstruction = instructions[phase] || ''
-
-      self.breathTimer = setTimeout(function() {
-        if (!self.breathing) return
-        if (phase === 'inhal') {
-          self.doBreathPhase('hold', 7)
-        } else if (phase === 'hold') {
-          self.doBreathPhase('exhale', 8)
-        } else {
-          // exhale done, start next round
-          var nextRound = self.breathRound + 1
-          self.breathRound = nextRound
-          if (nextRound <= 5) {
-            self.doBreathPhase('inhal', 4)
-          } else {
-            self.stopBreathing()
-            uni.showToast({ title: '完成一组呼吸', icon: 'none' })
-          }
-        }
-      }, seconds * 1000)
-    },
-
-    stopBreathing() {
-      this.breathing = false
-      if (this.breathTimer) {
-        clearTimeout(this.breathTimer)
-        this.breathTimer = null
-      }
-      this.breathPhase = 'inhal'
-      this.breathInstruction = '吸气'
-      this.breathRound = 0
-    }
-  },
-
-  onUnload() {
-    this.stopBreathing()
-  }
-}
-</script>
-
-<style scoped>
-.toolbox-container {
-  min-height: 100vh;
-  background: linear-gradient(180deg, #F0FDF4 0%, #FFFFFF 50%, #FFF7ED 100%);
-  padding-bottom: 40rpx;
-}
-
-.tool-section {
-  margin: 24rpx 32rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 28rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
-}
-
-.section-header {
-  display: flex;
-  align-items: center;
-  margin-bottom: 8rpx;
-}
-
-.section-emoji {
-  font-size: 36rpx;
-  margin-right: 12rpx;
-}
-
-.section-title {
-  font-size: 30rpx;
-  font-weight: 600;
-  color: #333;
-}
-
-.section-desc {
-  font-size: 24rpx;
-  color: #999;
-  display: block;
-  margin-bottom: 20rpx;
-}
-
-/* ===== 呼吸练习 ===== */
-.breathing-area {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 40rpx 0;
-}
-
-.breath-circle {
-  width: 280rpx;
-  height: 280rpx;
-  border-radius: 50%;
-  background: linear-gradient(135deg, #A7F3D0, #6EE7B7);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  transition: all 0.3s;
-  box-shadow: 0 8rpx 40rpx rgba(16, 185, 129, 0.2);
-}
-
-.breath-circle.inhal {
-  animation: breatheIn 4s ease-in-out;
-}
-
-.breath-circle.hold {
-  background: linear-gradient(135deg, #FDE68A, #F59E0B);
-  box-shadow: 0 8rpx 40rpx rgba(245, 158, 11, 0.3);
-}
-
-.breath-circle.exhale {
-  animation: breatheOut 8s ease-in-out;
-}
-
-.breath-text {
-  font-size: 36rpx;
-  font-weight: 600;
-  color: #fff;
-}
-
-.breath-hint {
-  display: block;
-  text-align: center;
-  font-size: 24rpx;
-  color: #999;
-}
-
-.breath-count {
-  display: block;
-  text-align: center;
-  font-size: 24rpx;
-  color: #10B981;
-  margin-top: 8rpx;
-}
-
-/* ===== 正念练习 ===== */
-.mindfulness-cards {
-  display: flex;
-  flex-direction: column;
-  gap: 16rpx;
-}
-
-.mind-card {
-  padding: 20rpx;
-  background: #F9F9F9;
-  border-radius: 12rpx;
-  border: 2rpx solid transparent;
-}
-
-.mind-card-active {
-  background: #F0FDF4;
-  border-color: #10B981;
-}
-
-.mind-card-title {
-  font-size: 26rpx;
-  font-weight: 600;
-  color: #333;
-  display: block;
-}
-
-.mind-card-desc {
-  font-size: 22rpx;
-  color: #999;
-  display: block;
-  margin-top: 4rpx;
-}
-
-.mindfulness-content {
-  margin-top: 16rpx;
-  padding: 20rpx;
-  background: #F0FDF4;
-  border-radius: 12rpx;
-  border-left: 6rpx solid #10B981;
-}
-
-.mindfulness-text {
-  font-size: 24rpx;
-  color: #555;
-  line-height: 1.8;
-  white-space: pre-wrap;
-}
-
-/* ===== 情绪涂鸦 ===== */
-.doodle-suggestions {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 16rpx;
-}
-
-.doodle-item {
-  width: calc(50% - 8rpx);
-  display: flex;
-  align-items: center;
-  gap: 12rpx;
-  padding: 20rpx;
-  background: #FFFBEB;
-  border-radius: 12rpx;
-  box-sizing: border-box;
-}
-
-.doodle-icon {
-  font-size: 36rpx;
-}
-
-.doodle-text {
-  font-size: 24rpx;
-  color: #666;
-  flex: 1;
-}
-
-/* ===== 运动释压 ===== */
-.exercise-list {
-  display: flex;
-  flex-direction: column;
-  gap: 16rpx;
-}
-
-.exercise-item {
-  padding: 20rpx;
-  background: #F0F9FF;
-  border-radius: 12rpx;
-  border-left: 6rpx solid #3B82F6;
-}
-
-.exercise-name {
-  font-size: 26rpx;
-  font-weight: 600;
-  color: #2563EB;
-  display: block;
-  margin-bottom: 6rpx;
-}
-
-.exercise-desc {
-  font-size: 24rpx;
-  color: #666;
-  line-height: 1.6;
-  display: block;
-}
-
-/* ===== 免责声明 ===== */
-.disclaimer {
-  margin: 32rpx;
-  padding: 20rpx;
-  background: #FEF3C7;
-  border-radius: 12rpx;
-}
-
-.disclaimer-text {
-  font-size: 22rpx;
-  color: #92400E;
-  line-height: 1.6;
-}
-</style>

+ 0 - 501
cfc-frontend/pages/mind/traditional-mirror.vue

@@ -1,501 +0,0 @@
-<template>
-  <view class="mirror-container">
-    <!-- 加载状态 -->
-    <view class="loading-state" v-if="loading">
-      <text>加载中...</text>
-    </view>
-
-    <template v-if="!loading && dataLoaded">
-      <!-- 八字四柱 -->
-      <view class="mirror-section">
-        <view class="section-header">
-          <text class="section-icon">&#x2637;</text>
-          <text class="section-title">八字四柱</text>
-        </view>
-        <view class="bazi-grid" v-if="eightChars">
-          <view class="bazi-column" v-for="(pillar, key) in eightChars" :key="key">
-            <text class="bazi-pillar-label">{{ pillarLabel(key) }}</text>
-            <text class="bazi-pillar-value">{{ pillar }}</text>
-          </view>
-        </view>
-        <view class="empty-data" v-else>
-          <text>暂未设置出生信息,无法排八字</text>
-        </view>
-      </view>
-
-      <!-- 五行能量 -->
-      <view class="mirror-section">
-        <view class="section-header">
-          <text class="section-icon">&#x267E;</text>
-          <text class="section-title">五行能量</text>
-        </view>
-        <view class="wuxing-list" v-if="wuxingData">
-          <view class="wuxing-item" v-for="wx in wuxingData" :key="wx.key">
-            <text class="wuxing-label">{{ wx.label }}</text>
-            <view class="wuxing-bar-track">
-              <view class="wuxing-bar-fill" :style="{ width: wx.value + '%', background: wx.color }"></view>
-            </view>
-            <text class="wuxing-value">{{ wx.value }}%</text>
-          </view>
-        </view>
-
-        <!-- 今日能量文 -->
-        <view class="energy-card" v-if="wuxingReading">
-          <view class="energy-header">
-            <text class="energy-icon">&#x2728;</text>
-            <text class="energy-label">今日能量文</text>
-          </view>
-          <view class="energy-divider"></view>
-          <text class="energy-quote" v-if="wuxingReading.personalTrait">{{ wuxingReading.personalTrait }}</text>
-          <text class="energy-advice" v-if="wuxingReading.mindGrowthAdvice">{{ wuxingReading.mindGrowthAdvice }}</text>
-          <view class="energy-footer">
-            <text class="energy-footer-dot"></text>
-            <text class="energy-footer-dot"></text>
-            <text class="energy-footer-dot"></text>
-          </view>
-        </view>
-      </view>
-
-      <!-- 太阳星座 -->
-      <view class="mirror-section" v-if="zodiac">
-        <view class="section-header">
-          <text class="section-icon">&#x2600;</text>
-          <text class="section-title">太阳星座</text>
-        </view>
-        <view class="zodiac-display">
-          <text class="zodiac-name">{{ zodiac }}</text>
-        </view>
-      </view>
-
-      <!-- 生命灵数 -->
-      <view class="mirror-section" v-if="numSoulData">
-        <view class="section-header">
-          <text class="section-icon">&#x1F52E;</text>
-          <text class="section-title">生命灵数</text>
-        </view>
-        <view class="numsoul-display">
-          <view class="numsoul-number" :style="{ background: numSoulData.colorHex || '#FF6B9D' }">
-            <text class="numsoul-num">{{ lifeNumber }}</text>
-          </view>
-          <view class="numsoul-info">
-            <text class="numsoul-title">{{ numSoulData.title || '' }}</text>
-            <text class="numsoul-keywords" v-if="numSoulData.keywords">{{ numSoulData.keywords }}</text>
-            <text class="numsoul-advice" v-if="numSoulData.mindGrowthAdvice">{{ numSoulData.mindGrowthAdvice }}</text>
-          </view>
-        </view>
-      </view>
-    </template>
-
-    <!-- 无数据 -->
-    <view class="empty-state" v-if="!loading && !dataLoaded">
-      <text class="empty-icon">&#x1F4AD;</text>
-      <text class="empty-text">暂无传统文化数据</text>
-    </view>
-  </view>
-</template>
-
-<script>
-import config from '../../config'
-
-export default {
-  data() {
-    return {
-      loading: true,
-      dataLoaded: false,
-      eightChars: null,
-      wuxingElements: null,
-      zodiac: '',
-      lifeNumber: null,
-      wuxingReading: null,
-      numSoulData: null,
-      wuxingConfig: {
-        wood: { label: '木', color: '#10B981' },
-        fire: { label: '火', color: '#FF6B9D' },
-        earth: { label: '土', color: '#FF8C42' },
-        metal: { label: '金', color: '#6366F1' },
-        water: { label: '水', color: '#3B82F6' }
-      },
-      pillarLabels: { year: '年柱', month: '月柱', day: '日柱', hour: '时柱' }
-    }
-  },
-
-  computed: {
-    wuxingData() {
-      if (!this.wuxingElements) return null
-      var keys = ['wood', 'fire', 'earth', 'metal', 'water']
-      var result = []
-      for (var i = 0; i < keys.length; i++) {
-        var k = keys[i]
-        if (this.wuxingElements[k] !== undefined) {
-          result.push({
-            key: k,
-            label: this.wuxingConfig[k].label,
-            color: this.wuxingConfig[k].color,
-            value: this.wuxingElements[k]
-          })
-        }
-      }
-      return result.sort(function(a, b) { return b.value - a.value })
-    }
-  },
-
-  onLoad() {
-    this.loadData()
-  },
-
-  methods: {
-    loadData() {
-      var self = this
-      var memberId = this.$mp.query.memberId || uni.getStorageSync('currentChildId')
-      if (!memberId) {
-        // Try to get from storage
-        memberId = uni.getStorageSync('currentChildId')
-      }
-      if (!memberId) {
-        self.loading = false
-        return
-      }
-
-      uni.request({
-        url: config.API_BASE_URL + '/api/mind/traditional/mirror',
-        method: 'POST',
-        data: { memberId: memberId, memberType: 'child' },
-        success: function(res) {
-          self.loading = false
-          if (res.data && res.data.code === 200 && res.data.data) {
-            var d = res.data.data
-            self.eightChars = d.eightCharacters || null
-            self.wuxingElements = d.wuxingElements || null
-            self.zodiac = d.zodiac || ''
-            self.lifeNumber = d.lifeNumber || null
-            self.wuxingReading = d.wuxingReading || null
-            self.numSoulData = d.numSoul || null
-            self.dataLoaded = !!(self.eightChars || self.wuxingElements || self.zodiac || self.numSoulData)
-          } else {
-            self.dataLoaded = false
-          }
-        },
-        fail: function() {
-          self.loading = false
-        }
-      })
-    },
-
-    pillarLabel(key) {
-      return this.pillarLabels[key] || key
-    }
-  }
-}
-</script>
-
-<style scoped>
-.mirror-container {
-  min-height: 100vh;
-  background: #F8F6F0;
-  padding-bottom: 40rpx;
-}
-
-.loading-state {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 120rpx 0;
-  color: #999;
-  font-size: 28rpx;
-}
-
-.mirror-section {
-  margin: 24rpx 32rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 28rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
-}
-
-.section-header {
-  display: flex;
-  align-items: center;
-  margin-bottom: 20rpx;
-}
-
-.section-icon {
-  font-size: 36rpx;
-  margin-right: 12rpx;
-}
-
-.section-title {
-  font-size: 30rpx;
-  font-weight: 600;
-  color: #333;
-}
-
-.bazi-grid {
-  display: flex;
-  justify-content: space-around;
-  padding: 16rpx 0;
-}
-
-.bazi-column {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-}
-
-.bazi-pillar-label {
-  font-size: 22rpx;
-  color: #999;
-  margin-bottom: 8rpx;
-}
-
-.bazi-pillar-value {
-  font-size: 36rpx;
-  font-weight: 700;
-  color: #8B5CF6;
-  background: #F5F0FF;
-  padding: 12rpx 16rpx;
-  border-radius: 12rpx;
-}
-
-.wuxing-list {
-  display: flex;
-  flex-direction: column;
-  gap: 16rpx;
-}
-
-.wuxing-item {
-  display: flex;
-  align-items: center;
-  gap: 16rpx;
-}
-
-.wuxing-label {
-  font-size: 28rpx;
-  font-weight: 600;
-  width: 48rpx;
-  text-align: center;
-}
-
-.wuxing-bar-track {
-  flex: 1;
-  height: 24rpx;
-  background: #F0F0F0;
-  border-radius: 12rpx;
-  overflow: hidden;
-}
-
-.wuxing-bar-fill {
-  height: 100%;
-  border-radius: 12rpx;
-}
-
-.wuxing-value {
-  font-size: 24rpx;
-  color: #666;
-  width: 60rpx;
-  text-align: right;
-}
-
-.energy-card {
-  margin-top: 24rpx;
-  padding: 28rpx 24rpx;
-  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
-  border-radius: 20rpx;
-  position: relative;
-  overflow: hidden;
-}
-
-.energy-card::before {
-  content: '';
-  position: absolute;
-  top: -60rpx;
-  right: -60rpx;
-  width: 200rpx;
-  height: 200rpx;
-  background: radial-gradient(circle, rgba(255,215,0,0.15) 0%, transparent 70%);
-  border-radius: 50%;
-}
-
-.energy-card::after {
-  content: '';
-  position: absolute;
-  bottom: -40rpx;
-  left: -40rpx;
-  width: 160rpx;
-  height: 160rpx;
-  background: radial-gradient(circle, rgba(99,102,241,0.12) 0%, transparent 70%);
-  border-radius: 50%;
-}
-
-.energy-header {
-  display: flex;
-  align-items: center;
-  margin-bottom: 16rpx;
-  position: relative;
-  z-index: 1;
-}
-
-.energy-icon {
-  font-size: 36rpx;
-  margin-right: 12rpx;
-}
-
-.energy-label {
-  font-size: 30rpx;
-  font-weight: 700;
-  background: linear-gradient(135deg, #F59E0B, #FF6B9D);
-  -webkit-background-clip: text;
-  -webkit-text-fill-color: transparent;
-  background-clip: text;
-}
-
-.energy-divider {
-  height: 2rpx;
-  background: linear-gradient(90deg, rgba(255,215,0,0.5), rgba(255,107,157,0.3), transparent);
-  margin-bottom: 20rpx;
-  position: relative;
-  z-index: 1;
-}
-
-.energy-quote {
-  font-size: 28rpx;
-  color: #E2E8F0;
-  line-height: 1.8;
-  display: block;
-  margin-bottom: 16rpx;
-  position: relative;
-  z-index: 1;
-  padding-left: 24rpx;
-  letter-spacing: 1rpx;
-}
-
-.energy-quote::before {
-  content: '';
-  position: absolute;
-  left: 0;
-  top: 4rpx;
-  bottom: 4rpx;
-  width: 4rpx;
-  background: linear-gradient(180deg, #F59E0B, #FF6B9D);
-  border-radius: 2rpx;
-}
-
-.energy-advice {
-  font-size: 26rpx;
-  color: #94A3B8;
-  line-height: 1.7;
-  display: block;
-  position: relative;
-  z-index: 1;
-  padding: 16rpx 20rpx;
-  background: rgba(255,255,255,0.05);
-  border-radius: 12rpx;
-  border: 1rpx solid rgba(255,255,255,0.08);
-}
-
-.energy-footer {
-  display: flex;
-  justify-content: center;
-  gap: 12rpx;
-  margin-top: 20rpx;
-  position: relative;
-  z-index: 1;
-}
-
-.energy-footer-dot {
-  width: 8rpx;
-  height: 8rpx;
-  border-radius: 50%;
-  background: rgba(255,255,255,0.3);
-}
-
-.energy-footer-dot:nth-child(2) {
-  background: rgba(255,215,0,0.6);
-  width: 10rpx;
-  height: 10rpx;
-}
-
-.zodiac-display {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 20rpx;
-}
-
-.zodiac-name {
-  font-size: 40rpx;
-  font-weight: 700;
-  color: #F59E0B;
-}
-
-.numsoul-display {
-  display: flex;
-  gap: 24rpx;
-  align-items: center;
-}
-
-.numsoul-number {
-  width: 100rpx;
-  height: 100rpx;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-}
-
-.numsoul-num {
-  font-size: 44rpx;
-  font-weight: 700;
-  color: #fff;
-}
-
-.numsoul-info {
-  flex: 1;
-}
-
-.numsoul-title {
-  font-size: 28rpx;
-  font-weight: 600;
-  color: #333;
-  display: block;
-}
-
-.numsoul-keywords {
-  font-size: 24rpx;
-  color: #666;
-  display: block;
-  margin-top: 6rpx;
-}
-
-.numsoul-advice {
-  font-size: 24rpx;
-  color: #8B5CF6;
-  display: block;
-  margin-top: 8rpx;
-  line-height: 1.5;
-}
-
-.empty-data {
-  padding: 20rpx;
-  text-align: center;
-  color: #999;
-  font-size: 24rpx;
-}
-
-.empty-state {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 120rpx 32rpx;
-}
-
-.empty-icon {
-  font-size: 80rpx;
-  margin-bottom: 20rpx;
-}
-
-.empty-text {
-  font-size: 28rpx;
-  color: #999;
-}
-</style>

+ 0 - 509
cfc-frontend/pages/mind/training.vue

@@ -1,509 +0,0 @@
-<template>
-  <view class="container">
-    <!-- 训练概览 -->
-    <view class="stats-card" v-if="stats">
-      <view class="stats-row">
-        <view class="stats-item">
-          <text class="stats-value">{{ stats.totalGames || 0 }}</text>
-          <text class="stats-label">总训练次数</text>
-        </view>
-        <view class="stats-divider"></view>
-        <view class="stats-item">
-          <text class="stats-value">{{ stats.bestScore || 0 }}</text>
-          <text class="stats-label">最高分</text>
-        </view>
-        <view class="stats-divider"></view>
-        <view class="stats-item">
-          <text class="stats-value">{{ stats.avgScore ? Math.round(stats.avgScore) : 0 }}</text>
-          <text class="stats-label">平均分</text>
-        </view>
-        <view class="stats-divider"></view>
-        <view class="stats-item">
-          <text class="stats-value">{{ stats.totalPoints || 0 }}</text>
-          <text class="stats-label">累计积分</text>
-        </view>
-      </view>
-    </view>
-    <view class="stats-card stats-loading" v-else-if="statsLoading">
-      <text class="stats-loading-text">加载训练数据...</text>
-    </view>
-
-    <!-- 训练维度描述 -->
-    <view class="dimension-card" v-if="currentDim">
-      <view class="dimension-icon-wrap" :style="{ background: currentDim.lightColor }">
-        <text class="dimension-icon">{{ currentDim.icon }}</text>
-      </view>
-      <text class="dimension-name">{{ currentDim.name }}</text>
-      <text class="dimension-desc">{{ currentDim.desc }}</text>
-    </view>
-
-    <!-- 推荐游戏 -->
-    <view class="section" v-if="currentDim && currentDim.games.length > 0">
-      <view class="section-header">
-        <text class="section-title">推荐训练</text>
-      </view>
-      <view class="game-list">
-        <view class="game-card" v-for="game in currentDim.games" :key="game.code" @click="goGame(game)">
-          <view class="game-icon-wrap" :style="{ background: currentDim.lightColor }">
-            <text class="game-card-icon">{{ game.icon }}</text>
-          </view>
-          <view class="game-info">
-            <text class="game-title">{{ game.name }}</text>
-            <text class="game-desc">{{ game.desc }}</text>
-          </view>
-          <text class="game-arrow">&#x2192;</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 成长建议 -->
-    <view class="section" v-if="currentDim">
-      <view class="section-header">
-        <text class="section-title">成长建议</text>
-      </view>
-      <view class="tip-card">
-        <view class="tip-left-bar" :style="{ background: currentDim.color }"></view>
-        <view class="tip-body">
-          <text class="tip-content">{{ currentDim.tip }}</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 其他维度训练 -->
-    <view class="section">
-      <view class="section-header">
-        <text class="section-title">其他维度</text>
-      </view>
-      <view class="other-grid">
-        <view class="other-item" v-for="dim in otherDimensions" :key="dim.key" @click="switchDimension(dim.key)">
-          <view class="other-icon-wrap" :style="{ background: dim.lightColor }">
-            <text class="other-icon">{{ dim.icon }}</text>
-          </view>
-          <text class="other-name">{{ dim.name }}</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 底部占位 -->
-    <view class="bottom-spacer"></view>
-  </view>
-</template>
-
-<script>
-import { getGameStats } from '../../utils/api.js'
-
-var DIMENSION_MAP = {
-  perception: {
-    key: 'perception',
-    name: '感知训练',
-    icon: '\u{1F441}',
-    color: '#8B5CF6',
-    lightColor: '#EDE9FE',
-    desc: '通过图形匹配、颜色分辨等练习提升感知能力',
-    tip: '感知力是认知的起点。多观察身边事物的细节,锻炼视觉辨识能力,能有效提升感知水平。',
-    games: [
-      { code: 'schulte', name: '舒尔特方格', icon: '\u{1F3AF}', desc: '快速定位数字,训练视觉搜索' }
-    ]
-  },
-  focus: {
-    key: 'focus',
-    name: '专注训练',
-    icon: '\u{1F3AF}',
-    color: '#F97316',
-    lightColor: '#FFF7ED',
-    desc: '通过舒尔特方格、数字追踪等练习提升专注力',
-    tip: '专注力是一切学习的基础。每天坚持10分钟专注训练,持续21天会有显著提升。',
-    games: [
-      { code: 'schulte', name: '舒尔特方格', icon: '\u{1F3AF}', desc: '按序点击数字,训练持续专注' }
-    ]
-  },
-  memory: {
-    key: 'memory',
-    name: '记忆训练',
-    icon: '\u{1F9E0}',
-    color: '#10B981',
-    lightColor: '#ECFDF5',
-    desc: '通过数字记忆、序列回忆等练习提升记忆力',
-    tip: '记忆力可以通过科学训练显著提升。联想记忆法和间隔重复是最有效的两种方法。',
-    games: [
-      { code: '1a2b', name: '猜数字', icon: '\u{1F522}', desc: '记忆数字排列,训练工作记忆' }
-    ]
-  },
-  logic: {
-    key: 'logic',
-    name: '逻辑训练',
-    icon: '\u{1F9E9}',
-    color: '#3B82F6',
-    lightColor: '#EFF6FF',
-    desc: '通过推理题、模式识别等练习提升逻辑思维',
-    tip: '逻辑思维是解决复杂问题的关键。多做推理和归纳练习,能帮助建立清晰的思维框架。',
-    games: [
-      { code: 'sudoku', name: '数独', icon: '\u{1F9E9}', desc: '数字逻辑推理,训练演绎思维' },
-      { code: '1a2b', name: '猜数字', icon: '\u{1F522}', desc: '逻辑排除推理,训练分析能力' }
-    ]
-  },
-  spatial: {
-    key: 'spatial',
-    name: '空间训练',
-    icon: '\u{1F9CA}',
-    color: '#8D6E63',
-    lightColor: '#EFEBE9',
-    desc: '通过空间旋转、立体构建等练习提升空间思维能力',
-    tip: '空间思维对数学和科学学习至关重要。拼图和积木是提升空间能力的最佳方式。',
-    games: [
-      { code: 'sudoku', name: '数独', icon: '\u{1F9E9}', desc: '九宫格空间布局,训练空间推理' }
-    ]
-  },
-  processingSpeed: {
-    key: 'processingSpeed',
-    name: '加工速度训练',
-    icon: '\u26A1',
-    color: '#FFD700',
-    lightColor: '#FFFBEB',
-    desc: '通过快速反应、速度测试等练习提升加工速度',
-    tip: '加工速度影响学习和反应效率。速算、速读等限时练习是提升加工速度的有效方法。',
-    games: [
-      { code: 'schulte', name: '舒尔特方格', icon: '\u{1F3AF}', desc: '限时完成,训练反应速度' },
-      { code: '1a2b', name: '猜数字', icon: '\u{1F522}', desc: '快速推理,训练信息处理速度' }
-    ]
-  }
-}
-
-var GAME_PAGES = {
-  schulte: '/pages/games/schulte',
-  '1a2b': '/pages/games/1a2b',
-  sudoku: '/pages/games/sudoku'
-}
-
-export default {
-  components: { },
-  data: function() {
-    return {
-      dimension: null,
-      memberId: null,
-      stats: null,
-      statsLoading: false
-    }
-  },
-  computed: {
-    currentDim: function() {
-      var dim = DIMENSION_MAP[this.dimension]
-      return dim || null
-    },
-    currentQuote: function() {
-      var dim = this.currentDim
-      return dim ? dim.desc : '知己知彼,百战不殆'
-    },
-    otherDimensions: function() {
-      var self = this
-      var keys = Object.keys(DIMENSION_MAP)
-      var result = []
-      for (var i = 0; i < keys.length; i++) {
-        if (keys[i] !== self.dimension) {
-          result.push(DIMENSION_MAP[keys[i]])
-        }
-      }
-      return result
-    }
-  },
-  onLoad: function(options) {
-    if (options && options.dimension) {
-      this.dimension = options.dimension
-    }
-    if (options && options.memberId) {
-      this.memberId = options.memberId
-    } else {
-      this.memberId = uni.getStorageSync('currentChildId') || null
-    }
-    uni.setNavigationBarTitle({
-      title: this.currentDim ? this.currentDim.name : '能力训练'
-    })
-    this.loadStats()
-  },
-  methods: {
-    loadStats: function() {
-      var self = this
-      if (!self.memberId) return
-      self.statsLoading = true
-      getGameStats({ memberId: self.memberId }).then(function(res) {
-        if (res && res.data) {
-          self.stats = res.data
-        }
-      }).catch(function() {
-        // silent — stats not critical
-      }).finally(function() {
-        self.statsLoading = false
-      })
-    },
-    goGame: function(game) {
-      var url = GAME_PAGES[game.code]
-      if (!url) {
-        uni.showToast({ title: '游戏暂未开放', icon: 'none' })
-        return
-      }
-      var memberId = this.memberId || ''
-      url = url + '?memberId=' + memberId + '&dimension=' + (this.dimension || '')
-      uni.navigateTo({ url: url })
-    },
-    switchDimension: function(key) {
-      this.dimension = key
-      uni.setNavigationBarTitle({
-        title: DIMENSION_MAP[key].name
-      })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.container {
-  min-height: 100vh;
-  background: #f5f7fa;
-  padding-bottom: 120rpx;
-}
-
-/* ===== 训练概览卡 ===== */
-.stats-card {
-  margin: 30rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 36rpx 24rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-
-.stats-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: space-around;
-}
-
-.stats-item {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  flex: 1;
-}
-
-.stats-value {
-  font-size: 44rpx;
-  font-weight: bold;
-  color: #F97316;
-  margin-bottom: 8rpx;
-}
-
-.stats-label {
-  font-size: 22rpx;
-  color: #999;
-}
-
-.stats-divider {
-  width: 1rpx;
-  height: 60rpx;
-  background: #eee;
-  flex-shrink: 0;
-}
-
-.stats-loading {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 24rpx;
-}
-
-.stats-loading-text {
-  font-size: 26rpx;
-  color: #bbb;
-}
-
-/* ===== 维度描述卡 ===== */
-.dimension-card {
-  margin: 30rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 40rpx 32rpx;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-}
-
-.dimension-icon-wrap {
-  width: 120rpx;
-  height: 120rpx;
-  border-radius: 60rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-bottom: 20rpx;
-}
-
-.dimension-icon {
-  font-size: 56rpx;
-}
-
-.dimension-name {
-  font-size: 36rpx;
-  font-weight: bold;
-  color: #333;
-  margin-bottom: 16rpx;
-}
-
-.dimension-desc {
-  font-size: 26rpx;
-  color: #888;
-  text-align: center;
-  line-height: 1.6;
-}
-
-/* ===== 通用区段 ===== */
-.section {
-  margin: 20rpx 30rpx;
-}
-
-.section-header {
-  margin-bottom: 20rpx;
-}
-
-.section-title {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #333;
-}
-
-/* ===== 推荐游戏列表 ===== */
-.game-list {
-  display: flex;
-  flex-direction: column;
-}
-
-.game-card {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 24rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-  margin-bottom: 20rpx;
-}
-
-.game-card:active {
-  opacity: 0.7;
-}
-
-.game-icon-wrap {
-  width: 80rpx;
-  height: 80rpx;
-  border-radius: 40rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 20rpx;
-  flex-shrink: 0;
-}
-
-.game-card-icon {
-  font-size: 40rpx;
-}
-
-.game-info {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-}
-
-.game-title {
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #333;
-  margin-bottom: 6rpx;
-}
-
-.game-desc {
-  font-size: 22rpx;
-  color: #999;
-}
-
-.game-arrow {
-  font-size: 28rpx;
-  color: #ccc;
-  margin-left: 16rpx;
-  flex-shrink: 0;
-}
-
-/* ===== 成长建议卡 ===== */
-.tip-card {
-  display: flex;
-  flex-direction: row;
-  background: #fff;
-  border-radius: 20rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
-  overflow: hidden;
-}
-
-.tip-left-bar {
-  width: 6rpx;
-  flex-shrink: 0;
-}
-
-.tip-body {
-  flex: 1;
-  padding: 28rpx 24rpx;
-}
-
-.tip-content {
-  font-size: 26rpx;
-  color: #666;
-  line-height: 1.7;
-}
-
-/* ===== 其他维度网格 ===== */
-.other-grid {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  margin: 0 -10rpx;
-}
-
-.other-item {
-  width: calc(33.33% - 20rpx);
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  background: #fff;
-  border-radius: 16rpx;
-  padding: 24rpx 8rpx;
-  margin: 0 10rpx 20rpx;
-  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
-}
-
-.other-item:active {
-  opacity: 0.7;
-}
-
-.other-icon-wrap {
-  width: 64rpx;
-  height: 64rpx;
-  border-radius: 32rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-bottom: 10rpx;
-}
-
-.other-icon {
-  font-size: 32rpx;
-}
-
-.other-name {
-  font-size: 22rpx;
-  color: #666;
-  font-weight: 500;
-}
-
-/* ===== 底部 ===== */
-.bottom-spacer {
-  height: 120rpx;
-}
-</style>

+ 0 - 403
cfc-frontend/pages/wisdom/cognitive-report.vue

@@ -1,403 +0,0 @@
-<template>
-  <view class="container">
-    <view class="report-card" v-if="report">
-      <view class="report-header">
-        <text class="report-title">认知测评报告</text>
-        <text class="report-date">测评日期: {{ report.assessmentDate || '-' }}</text>
-      </view>
-
-      <!-- 综合认知评分 -->
-      <view class="overall-score-wrap">
-        <text class="overall-label">综合认知评分</text>
-        <view class="overall-row">
-          <text class="overall-value">{{ report.overallScore || 0 }}</text>
-          
-        </view>
-      </view>
-
-      <!-- 六维条形图 -->
-      <view class="dimension-list">
-        <view class="dimension-item" v-for="dim in dimensions" :key="dim.key">
-          <view class="dimension-header">
-            <view class="dimension-label-row">
-              <text class="dimension-icon">{{ dim.icon }}</text>
-              <text class="dimension-name">{{ dim.label }}</text>
-            </view>
-            <text class="dimension-score">{{ report[dim.key] || 0 }}</text>
-          </view>
-          <view class="dimension-bar">
-            <view class="dimension-bar-fill" :style="{ width: (report[dim.key] || 0) + '%' }"></view>
-          </view>
-          <text class="dimension-desc">{{ dim.desc }}</text>
-        </view>
-      </view>
-
-      <!-- 分析报告 -->
-      <view class="report-analysis" v-if="report.analysisReport">
-        <view class="analysis-header-row">
-          <text class="analysis-icon">&#x1F4DD;</text>
-          <text class="analysis-title">分析报告</text>
-        </view>
-        <text class="analysis-content">{{ report.analysisReport }}</text>
-      </view>
-
-      <!-- 成长建议 -->
-      <view class="report-suggestions" v-if="report.growthSuggestions">
-        <view class="analysis-header-row">
-          <text class="analysis-icon">&#x1F331;</text>
-          <text class="analysis-title">成长建议</text>
-        </view>
-        <text class="suggestion-content">{{ report.growthSuggestions }}</text>
-      </view>
-
-      <!-- 底部操作 -->
-      <view class="action-row">
-        <view class="action-btn secondary" @click="goAssessment">
-          <text class="action-btn-text">预约测评</text>
-        </view>
-        <view class="action-btn primary" @click="goTraining">
-          <text class="action-btn-text">认知训练</text>
-        </view>
-      </view>
-    </view>
-
-    <!-- 加载中 -->
-    <view class="loading-state" v-if="loading">
-      <text class="loading-text">加载中...</text>
-    </view>
-
-    <!-- 无数据 -->
-    <view class="empty-state" v-if="!report && !loading">
-      <text class="empty-icon">&#x1F9E0;</text>
-      <text class="empty-title">暂无认知测评数据</text>
-      <text class="empty-desc">完成认知能力游戏测评或DAN评估后,可在此查看详细的七维认知分析报告</text>
-      <view class="empty-btn" @click="goAssessment">预约测评</view>
-    </view>
-  </view>
-</template>
-
-<script>
-import { getAssessmentLatestResult } from '../../utils/api.js'
-
-export default {
-  components: { },
-  data: function() {
-    return {
-      memberId: null,
-      report: null,
-      loading: true,
-      dimensions: [
-        { key: 'perceptionScore', label: '感知能力', icon: '\u{1F441}', desc: '感官信息处理能力' },
-        { key: 'focusScore', label: '专注力', icon: '\u{1F3AF}', desc: '持续集中和抗干扰能力' },
-        { key: 'memoryScore', label: '记忆力', icon: '\u{1F9E0}', desc: '信息编码和回忆能力' },
-        { key: 'logicScore', label: '逻辑思维', icon: '\u{1F9E9}', desc: '推理和问题解决能力' },
-        { key: 'spatialScore', label: '空间思维', icon: '\u{1F9CA}', desc: '空间想象和方位判断' },
-        { key: 'processingSpeedScore', label: '加工速度', icon: '\u26A1', desc: '信息处理和反应速度' },
-        { key: 'languageScore', label: '语言表达', icon: '\u{1F60B}', desc: '词汇理解与语言组织能力' }
-      ]
-    }
-  },
-  onLoad: function(options) {
-    if (options && options.memberId) {
-      this.memberId = options.memberId
-    } else {
-      this.memberId = uni.getStorageSync('currentChildId') || null
-    }
-    this.loadReport()
-  },
-  methods: {
-    loadReport: function() {
-      var self = this
-      var memberId = self.memberId
-      if (!memberId) {
-        self.loading = false
-        return
-      }
-      self.loading = true
-      getAssessmentLatestResult(memberId).then(function(res) {
-        if (res.code === 200 && res.data) {
-          self.report = res.data
-        }
-        self.loading = false
-      }).catch(function() {
-        self.loading = false
-      })
-    },
-    goAssessment: function() {
-      uni.navigateTo({ url: '/pages/assessment/apply' })
-    },
-    goTraining: function() {
-      uni.navigateTo({ url: '/pages/mind/training?memberId=' + (this.memberId || '') })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.container {
-  min-height: 100vh;
-  background: #f5f7fa;
-  padding-bottom: 120rpx;
-}
-
-/* ===== 报告卡 ===== */
-.report-card {
-  margin: 20rpx 30rpx;
-  background: #fff;
-  border-radius: 24rpx;
-  padding: 40rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
-}
-
-.report-header {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 30rpx;
-}
-
-.report-title {
-  font-size: 36rpx;
-  font-weight: bold;
-  color: #333;
-}
-
-.report-date {
-  font-size: 24rpx;
-  color: #999;
-}
-
-/* ===== 综合评分 ===== */
-.overall-score-wrap {
-  text-align: center;
-  padding: 40rpx 0;
-  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-  border-radius: 20rpx;
-  margin-bottom: 40rpx;
-  color: #fff;
-}
-
-.overall-label {
-  font-size: 28rpx;
-  opacity: 0.9;
-  display: block;
-  margin-bottom: 16rpx;
-}
-
-.overall-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: center;
-}
-
-.overall-value {
-  font-size: 80rpx;
-  font-weight: bold;
-  margin-right: 20rpx;
-}
-
-.dan-badge {
-  background: rgba(255,255,255,0.25);
-  border-radius: 12rpx;
-  padding: 6rpx 20rpx;
-}
-
-.dan-level {
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #FFD700;
-}
-
-/* ===== 七维条形图 ===== */
-.dimension-list {
-  margin-bottom: 30rpx;
-}
-
-.dimension-item {
-  margin-bottom: 28rpx;
-}
-
-.dimension-header {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 10rpx;
-}
-
-.dimension-label-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-}
-
-.dimension-icon {
-  font-size: 28rpx;
-  margin-right: 10rpx;
-}
-
-.dimension-name {
-  font-size: 28rpx;
-  color: #333;
-  font-weight: 500;
-}
-
-.dimension-score {
-  font-size: 32rpx;
-  font-weight: bold;
-  color: #667eea;
-}
-
-.dimension-bar {
-  height: 16rpx;
-  background: #f0f0f0;
-  border-radius: 8rpx;
-  overflow: hidden;
-  margin-bottom: 8rpx;
-}
-
-.dimension-bar-fill {
-  height: 100%;
-  background: linear-gradient(90deg, #667eea, #764ba2);
-  border-radius: 8rpx;
-  transition: width 0.5s;
-}
-
-.dimension-desc {
-  font-size: 22rpx;
-  color: #999;
-  line-height: 1.4;
-}
-
-/* ===== 分析/建议 ===== */
-.report-analysis, .report-suggestions {
-  margin-top: 30rpx;
-  padding: 28rpx;
-  border-radius: 16rpx;
-}
-
-.report-analysis {
-  background: #f9f9ff;
-}
-
-.report-suggestions {
-  background: #f0fdf4;
-}
-
-.analysis-header-row {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  margin-bottom: 16rpx;
-}
-
-.analysis-icon {
-  font-size: 30rpx;
-  margin-right: 10rpx;
-}
-
-.analysis-title {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #333;
-}
-
-.analysis-content, .suggestion-content {
-  font-size: 26rpx;
-  color: #666;
-  line-height: 1.7;
-  display: block;
-}
-
-/* ===== 底部操作 ===== */
-.action-row {
-  display: flex;
-  flex-direction: row;
-  justify-content: space-between;
-  margin-top: 40rpx;
-}
-
-.action-btn {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 20rpx 0;
-  border-radius: 40rpx;
-}
-
-.action-btn.primary {
-  background: linear-gradient(135deg, #667eea, #764ba2);
-  margin-left: 16rpx;
-  box-shadow: 0 4rpx 16rpx rgba(102,126,234,0.3);
-}
-
-.action-btn.secondary {
-  background: #fff;
-  border: 2rpx solid #ddd;
-  margin-right: 16rpx;
-}
-
-.action-btn-text {
-  font-size: 28rpx;
-  font-weight: 500;
-  color: #fff;
-}
-
-.action-btn.secondary .action-btn-text {
-  color: #666;
-}
-
-/* ===== 加载中 ===== */
-.loading-state {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 120rpx 0;
-}
-
-.loading-text {
-  font-size: 28rpx;
-  color: #999;
-}
-
-/* ===== 空状态 ===== */
-.empty-state {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 120rpx 40rpx 80rpx;
-}
-
-.empty-icon {
-  font-size: 100rpx;
-  margin-bottom: 20rpx;
-}
-
-.empty-title {
-  font-size: 32rpx;
-  color: #333;
-  font-weight: bold;
-  margin-bottom: 16rpx;
-}
-
-.empty-desc {
-  font-size: 26rpx;
-  color: #999;
-  text-align: center;
-  line-height: 1.6;
-  margin-bottom: 40rpx;
-}
-
-.empty-btn {
-  background: linear-gradient(135deg, #667eea, #764ba2);
-  color: #fff;
-  font-size: 28rpx;
-  font-weight: bold;
-  padding: 16rpx 64rpx;
-  border-radius: 40rpx;
-  box-shadow: 0 4rpx 16rpx rgba(102,126,234,0.3);
-}
-</style>

+ 0 - 216
cfc-frontend/pages/wisdom/member-wisdom-detail.vue

@@ -1,216 +0,0 @@
-<template>
-  <view class="detail-container">
-    <!-- 顶部导航 -->
-
-    <!-- 五维能量条 -->
-    <FamilyEnergyBar
-      dimensionCode="wisdom"
-      :sandboxData="sandboxData"
-      :dualDimension="dualDimension" />
-
-    <!-- 成员信息卡 -->
-    <view class="member-card" v-if="memberInfo">
-      <view class="member-avatar wisdom-avatar">
-        <text class="avatar-text">{{ memberInfo.name && memberInfo.name.charAt(0) || '孩' }}</text>
-      </view>
-      <view class="member-info">
-        <text class="member-name">{{ memberInfo.name || '孩子' }}</text>
-        <text class="member-role">智慧维度</text>
-      </view>
-      <view class="member-score" v-if="dualDimension">
-        <text class="score-value wisdom-score">{{ dualDimension.energy || 0 }}</text>
-        <text class="score-label">能量值</text>
-      </view>
-    </view>
-
-    <!-- 家庭成员关系图谱(只读) -->
-    <FamilyRelationGraph
-      dimensionCode="wisdom"
-      :selfId="selfId"
-      :members="graphMembers"
-      :energyMap="energyMapForGraph"
-      :intimacyMap="intimacyMapForGraph"
-      :interactive="false" />
-
-    <!-- 今日任务 -->
-    <DimensionTasks
-      :memberId="memberId"
-      @taskClick="onTaskClick"
-      @moreTasks="goTasks" />
-
-    <!-- 活动 -->
-    <DimensionActivities
-      :isLoggedIn="true"
-      @activityClick="goActivityDetail"
-      @moreActivities="goMoreActivities" />
-
-    <!-- 商品 -->
-    <DimensionProducts
-      :isLoggedIn="true"
-      @productClick="goProductDetail"
-      @moreProducts="goMoreProducts" />
-
-    <!-- 底部占位 -->
-    <view class="bottom-spacer"></view>
-  </view>
-</template>
-
-<script>
-import FamilyEnergyBar from '../../components/FamilyEnergyBar.vue'
-import FamilyRelationGraph from '../../components/FamilyRelationGraph.vue'
-import DimensionTasks from '../../components/DimensionTasks.vue'
-import DimensionActivities from '../../components/DimensionActivities.vue'
-import DimensionProducts from '../../components/DimensionProducts.vue'
-import { getEnergyOverview, getFamilyEnergySandbox, getChildren } from '../../utils/api.js'
-
-export default {
-  components: { FamilyEnergyBar, FamilyRelationGraph, DimensionTasks, DimensionActivities, DimensionProducts },
-  data() {
-    return {
-      memberId: null,
-      selfId: null,
-      memberInfo: null,
-      graphMembers: [],
-      energyMapForGraph: {},
-      intimacyMapForGraph: {},
-      dualDimension: null,
-      sandboxData: null
-    }
-  },
-  onLoad: function(options) {
-    if (options && options.memberId) {
-      this.selfId = options.memberId
-      this.memberId = options.memberId
-      this.loadMemberData()
-    }
-  },
-  methods: {
-    goBack: function() {
-      uni.navigateBack()
-    },
-    loadMemberData: function() {
-      var self = this
-      var memberId = this.memberId
-      if (!memberId) return
-
-      getEnergyOverview(memberId).then(function(res) {
-        if (res && res.data) {
-          self.dualDimension = res.data
-        }
-      }).catch(function() {})
-
-      getFamilyEnergySandbox(memberId).then(function(res) {
-        if (res && res.data) {
-          self.sandboxData = res.data.sandboxData || null
-          self.graphMembers = res.data.members || []
-          self.energyMapForGraph = res.data.energyMap || {}
-          self.intimacyMapForGraph = res.data.intimacyMap || {}
-        }
-      }).catch(function() {})
-    },
-    onTaskClick: function(task) {},
-    goTasks: function() { uni.navigateTo({ url: '/pages/tasks/tasks' }) },
-    goActivityDetail: function(id) {},
-    goMoreActivities: function() {
-      uni.navigateTo({ url: '/pages/activity/index' })
-    },
-    goProductDetail: function(id) {
-      var token = uni.getStorageSync('token')
-      if (token) {
-        uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + id })
-      } else {
-        uni.navigateTo({ url: '/pages/login/login' })
-      }
-    },
-    goMoreProducts: function() {
-      uni.navigateTo({ url: '/pages/shop/index/index' })
-    }
-  }
-}
-</script>
-
-<style scoped>
-.detail-container {
-  min-height: 100vh;
-  background: #f5f7fa;
-  padding-bottom: 120rpx;
-}
-
-.nav-back-icon {
-  font-size: 36rpx;
-  color: #fff;
-}
-
-.member-card {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  background: #fff;
-  margin: 20rpx 30rpx;
-  padding: 30rpx;
-  border-radius: 20rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
-}
-
-.member-avatar {
-  width: 100rpx;
-  height: 100rpx;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 24rpx;
-}
-
-.wisdom-avatar {
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-}
-
-.avatar-text {
-  font-size: 40rpx;
-  font-weight: bold;
-  color: #fff;
-}
-
-.member-info {
-  flex: 1;
-}
-
-.member-name {
-  font-size: 32rpx;
-  font-weight: bold;
-  color: #333;
-  display: block;
-}
-
-.member-role {
-  font-size: 24rpx;
-  color: #999;
-  margin-top: 6rpx;
-  display: block;
-}
-
-.member-score {
-  text-align: center;
-}
-
-.score-value {
-  font-size: 40rpx;
-  font-weight: bold;
-  display: block;
-}
-
-.wisdom-score {
-  color: #B8860B;
-}
-
-.score-label {
-  font-size: 22rpx;
-  color: #999;
-  display: block;
-}
-
-.bottom-spacer {
-  height: 40rpx;
-}
-</style>

+ 0 - 921
cfc-frontend/pages/wisdom/self-quiz.vue

@@ -1,921 +0,0 @@
-<template>
-  <view class="container">
-    <view class="resume-prompt" v-if="showResume">
-      <text class="resume-text">检测到上次未完成的测评</text>
-      <view class="resume-btns">
-        <view class="btn btn--outline" @tap="resumeSession">继续答题</view>
-        <view class="btn btn--ghost" @tap="clearSession">重新开始</view>
-      </view>
-    </view>
-
-    <view class="step-wrap" v-if="!showResume">
-      <view class="step-row">
-        <view class="step-item" v-for="i in 4" :key="i" @tap="goStep(i)">
-          <view class="step-dot" :class="{
-            'step-dot--active': step === i,
-            'step-dot--done': step > i,
-            'step-dot--pending': step < i
-          }">
-            <text v-if="step > i" class="step-check">&#10003;</text>
-            <text v-else class="step-num">{{ i }}</text>
-          </view>
-          <text class="step-label" :class="{
-            'step-label--active': step >= i,
-            'step-label--muted': step < i
-          }">{{ stepLabels[i - 1] }}</text>
-        </view>
-      </view>
-    </view>
-
-    <view class="step-content" v-if="step === 1 && !showResume">
-      <view class="content-header">
-        <text class="content-title">选择测评目标</text>
-        <text class="content-sub">选择你想提升的能力维度</text>
-      </view>
-      <view class="goal-grid">
-        <view class="goal-card" v-for="g in goals" :key="g.id" :class="{ 'goal-card--selected': selectedGoal && selectedGoal.id === g.id }" @tap="selectGoal(g)">
-          <view class="goal-icon-wrap">
-            <text class="goal-icon">{{ g.icon }}</text>
-          </view>
-          <text class="goal-title">{{ g.title }}</text>
-          <text class="goal-desc">{{ g.desc }}</text>
-        </view>
-      </view>
-      <view class="action-bar">
-        <view class="btn btn--primary" :class="{ 'btn--disabled': !selectedGoal }" @tap="goStep2">下一步</view>
-      </view>
-    </view>
-
-    <view class="step-content" v-if="step === 2 && !showResume">
-      <view class="content-header">
-        <text class="content-title">选择题目类型</text>
-        <text class="content-sub">共5道题,选择适合的答题方式</text>
-      </view>
-      <view class="type-list">
-        <view class="type-card" v-for="t in questionTypes" :key="t.id" :class="{ 'type-card--selected': questionType === t.id }" @tap="selectType(t.id)">
-          <view class="type-icon-wrap">
-            <text class="type-icon">{{ t.icon }}</text>
-          </view>
-          <view class="type-info">
-            <text class="type-name">{{ t.name }}</text>
-            <text class="type-desc">{{ t.desc }}</text>
-          </view>
-          <view class="type-radio" :class="{ 'type-radio--checked': questionType === t.id }">
-            <view v-if="questionType === t.id" class="type-radio-dot"></view>
-          </view>
-        </view>
-      </view>
-      <view class="action-bar action-bar--double">
-        <view class="btn btn--outline" @tap="goStep1">上一步</view>
-        <view class="btn btn--primary" :class="{ 'btn--disabled': !questionType }" @tap="startQuiz">开始答题</view>
-      </view>
-    </view>
-
-    <view class="step-content" v-if="step === 3 && !showResume">
-      <view class="progress-header">
-        <text class="progress-label">第 {{ currentQ + 1 }}/{{ questions.length }} 题</text>
-        <view class="progress-bar">
-          <view class="progress-fill" :style="'width:' + progressPct + '%'"></view>
-        </view>
-      </view>
-      <view class="question-card" v-if="currentQuestion">
-        <view class="q-tag">{{ tagLabel }}</view>
-        <text class="q-text">{{ currentQuestion.question }}</text>
-        <view class="q-options" v-if="currentQuestion.isTF">
-          <view class="q-option q-option--tf" :class="{ 'q-option--selected': currentAnswer === '正确' }" @tap="currentAnswer = '正确'">
-            <text class="q-option-tf-text">正确</text>
-          </view>
-          <view class="q-option q-option--tf" :class="{ 'q-option--selected': currentAnswer === '错误' }" @tap="currentAnswer = '错误'">
-            <text class="q-option-tf-text">错误</text>
-          </view>
-        </view>
-        <view class="q-options" v-else>
-          <view class="q-option" v-for="(opt, oi) in currentQuestion.options" :key="oi" :class="{ 'q-option--selected': currentAnswer === opt }" @tap="currentAnswer = opt">
-            <view class="q-radio" :class="{ 'q-radio--checked': currentAnswer === opt }">
-              <view v-if="currentAnswer === opt" class="q-radio-dot"></view>
-            </view>
-            <text class="q-option-text">{{ opt }}</text>
-          </view>
-        </view>
-      </view>
-      <view class="action-bar action-bar--double">
-        <view class="btn btn--outline" :class="{ 'btn--disabled': currentQ === 0 }" @tap="prevQuestion">上一题</view>
-        <view class="btn btn--primary" :class="{ 'btn--disabled': !currentAnswer }" @tap="nextQuestion">{{ currentQ < questions.length - 1 ? '下一题' : '查看结果' }}</view>
-      </view>
-    </view>
-
-    <view class="step-content" v-if="step === 4 && !showResume">
-      <view class="result-header">
-        <view class="score-circle">
-          <text class="score-value">{{ score }}/5</text>
-        </view>
-        <text class="score-label">{{ resultMessage }}</text>
-      </view>
-      <view class="section-title">维度分析</view>
-      <view class="result-dims">
-        <view class="dim-bar-item" v-for="(dim, di) in dimBreakdown" :key="di">
-          <view class="dim-bar-header">
-            <text class="dim-bar-name">{{ dim.name }}</text>
-            <text class="dim-bar-count">{{ dim.correct }}/{{ dim.total }}</text>
-          </view>
-          <view class="dim-bar-track">
-            <view class="dim-bar-fill" :style="'width:' + dim.pct + '%'"></view>
-          </view>
-        </view>
-      </view>
-      <view class="action-bar">
-        <view class="btn btn--primary" @tap="resetQuiz">再测一次</view>
-        <view class="btn btn--outline" @tap="goHome">返回首页</view>
-      </view>
-    </view>
-
-    <view class="bottom-spacer"></view>
-  </view>
-</template>
-
-<script>
-var QUESTION_BANK = [
-  { dimension: 'focus', question: '以下哪个因素最容易影响注意力集中?', options: ['噪音', '温度', '光照', '以上都是'], answer: '以上都是' },
-  { dimension: 'focus', question: '持续专注工作建议多久休息一次?', options: ['15分钟', '30分钟', '45分钟', '60分钟'], answer: '45分钟' },
-  { dimension: 'focus', question: '舒尔特方格的训练目标是提升什么?', options: ['记忆力', '专注力', '逻辑思维', '创造力'], answer: '专注力' },
-  { dimension: 'memory', question: '短期记忆通常能记住几个信息组块?', options: ['3-4个', '5-9个', '10-15个', '20个以上'], answer: '5-9个' },
-  { dimension: 'memory', question: '以下哪个方法有助于增强记忆?', options: ['死记硬背', '联想记忆', '反复抄写', '一次性学习'], answer: '联想记忆' },
-  { dimension: 'memory', question: '睡眠对记忆的影响是什么?', options: ['削弱记忆', '巩固记忆', '无影响', '干扰记忆'], answer: '巩固记忆' },
-  { dimension: 'logic', question: '如果A>B且B>C,那么?', options: ['A=C', 'A>C', 'A<C', '不确定'], answer: 'A>C' },
-  { dimension: 'logic', question: '1, 1, 2, 3, 5, 8, ? 下一个数是多少?', options: ['10', '11', '12', '13'], answer: '13' },
-  { dimension: 'logic', question: '以下哪个不是有效推理方式?', options: ['归纳推理', '演绎推理', '循环论证', '类比推理'], answer: '循环论证' },
-  { dimension: 'perception', question: '人眼能分辨约多少种颜色?', options: ['几百种', '几千种', '几万种', '数百万种'], answer: '数百万种' },
-  { dimension: 'perception', question: '以下哪种错觉与视觉感知有关?', options: ['缪勒-莱尔错觉', '德布罗意错觉', '薛定谔错觉', '迈克尔逊错觉'], answer: '缪勒-莱尔错觉' },
-  { dimension: 'perception', question: '感觉适应是指什么?', options: ['敏感度降低', '敏感度升高', '感知消失', '感知增强'], answer: '敏感度降低' },
-  { dimension: 'spatial', question: '正方体有多少个面?', options: ['4', '6', '8', '12'], answer: '6' },
-  { dimension: 'spatial', question: '以下哪个是立体几何的基本要素?', options: ['点线面', '正反合', '红黄蓝', '上下左右'], answer: '点线面' },
-  { dimension: 'spatial', question: '一个立方体展开图有几个正方形?', options: ['4', '6', '8', '12'], answer: '6' },
-  { dimension: 'speed', question: '反应时实验测量的是什么?', options: ['思考速度', '反应速度', '运动速度', '阅读速度'], answer: '反应速度' },
-  { dimension: 'speed', question: '以下哪个因素会影响加工速度?', options: ['年龄', '情绪', '疲劳', '以上都是'], answer: '以上都是' },
-  { dimension: 'speed', question: '信息加工速度最快的年龄段是?', options: ['儿童期', '青春期', '成年早期', '中老年期'], answer: '成年早期' }
-]
-
-export default {
-  data() {
-    return {
-      step: 1,
-      selectedGoal: null,
-      questionType: null,
-      questions: [],
-      currentQ: 0,
-      answers: [],
-      showResume: false,
-      savedSession: null,
-      goals: [
-        { id: 'focus', title: '专注力', desc: '提升注意力集中能力', icon: '集' },
-        { id: 'memory', title: '记忆力', desc: '增强信息记忆与回忆能力', icon: '忆' },
-        { id: 'logic', title: '逻辑思维', desc: '锻炼逻辑推理与判断能力', icon: '逻' },
-        { id: 'perception', title: '感知觉', desc: '提高感官信息处理能力', icon: '感' },
-        { id: 'spatial', title: '空间思维', desc: '培养空间想象与构建能力', icon: '间' },
-        { id: 'speed', title: '加工速度', desc: '加快信息处理与反应速度', icon: '速' }
-      ],
-      questionTypes: [
-        { id: 'choice', name: '选择题', desc: '四选一标准选择题', icon: '选' },
-        { id: 'truefalse', name: '判断题', desc: '判断陈述正误', icon: '判' },
-        { id: 'mix', name: '混合题型', desc: '选择题与判断题混合', icon: '混' }
-      ],
-      stepLabels: ['目标', '题型', '答题', '结果']
-    }
-  },
-  computed: {
-    currentQuestion: function() {
-      if (this.questions.length === 0) {
-        return null
-      }
-      return this.questions[this.currentQ]
-    },
-    currentAnswer: {
-      get: function() {
-        return this.answers[this.currentQ] || null
-      },
-      set: function(val) {
-        this.$set(this.answers, this.currentQ, val)
-      }
-    },
-    progressPct: function() {
-      if (this.questions.length === 0) {
-        return 0
-      }
-      return ((this.currentQ + 1) / this.questions.length) * 100
-    },
-    score: function() {
-      var correct = 0
-      for (var i = 0; i < this.questions.length; i++) {
-        var q = this.questions[i]
-        var a = this.answers[i]
-        if (q && a && q.answer === a) {
-          correct++
-        }
-      }
-      return correct
-    },
-    resultMessage: function() {
-      var s = this.score
-      if (s >= 5) return '太棒了!满分通过!'
-      if (s >= 4) return '非常优秀!继续保持!'
-      if (s >= 3) return '表现不错,还有提升空间!'
-      return '继续加油,多练习会更好!'
-    },
-    tagLabel: function() {
-      if (!this.currentQuestion) return ''
-      var dimMap = {
-        focus: '专注力',
-        memory: '记忆力',
-        logic: '逻辑思维',
-        perception: '感知觉',
-        spatial: '空间思维',
-        speed: '加工速度'
-      }
-      return dimMap[this.currentQuestion.dimension] || ''
-    },
-    dimBreakdown: function() {
-      var dimNames = {
-        focus: '专注力',
-        memory: '记忆力',
-        logic: '逻辑思维',
-        perception: '感知觉',
-        spatial: '空间思维',
-        speed: '加工速度'
-      }
-      var dims = {}
-      for (var i = 0; i < this.questions.length; i++) {
-        var q = this.questions[i]
-        if (!q) continue
-        if (!dims[q.dimension]) {
-          dims[q.dimension] = { total: 0, correct: 0 }
-        }
-        dims[q.dimension].total++
-        if (this.answers[i] && q.answer === this.answers[i]) {
-          dims[q.dimension].correct++
-        }
-      }
-      var result = []
-      var keys = Object.keys(dims)
-      for (var k = 0; k < keys.length; k++) {
-        var key = keys[k]
-        var item = dims[key]
-        result.push({
-          name: dimNames[key] || key,
-          total: item.total,
-          correct: item.correct,
-          pct: item.total > 0 ? Math.round((item.correct / item.total) * 100) : 0
-        })
-      }
-      return result
-    }
-  },
-  onLoad: function() {
-    this.checkSession()
-  },
-  onUnload: function() {
-    this.saveSession()
-  },
-  methods: {
-    checkSession: function() {
-      var saved = uni.getStorageSync('selfQuizSession')
-      if (saved) {
-        try {
-          var session = JSON.parse(saved)
-          if (session && session.step && session.step >= 2 && session.step <= 3) {
-            this.showResume = true
-            this.savedSession = session
-          } else {
-            uni.removeStorageSync('selfQuizSession')
-          }
-        } catch (e) {
-          uni.removeStorageSync('selfQuizSession')
-        }
-      }
-    },
-    saveSession: function() {
-      var session = {
-        step: this.step,
-        selectedGoal: this.selectedGoal,
-        questionType: this.questionType,
-        questions: this.questions,
-        currentQ: this.currentQ,
-        answers: this.answers
-      }
-      uni.setStorageSync('selfQuizSession', JSON.stringify(session))
-    },
-    resumeSession: function() {
-      if (!this.savedSession) return
-      var s = this.savedSession
-      this.step = s.step
-      this.selectedGoal = s.selectedGoal
-      this.questionType = s.questionType
-      this.questions = s.questions || []
-      this.currentQ = s.currentQ || 0
-      this.answers = s.answers || []
-      this.showResume = false
-      this.savedSession = null
-    },
-    clearSession: function() {
-      uni.removeStorageSync('selfQuizSession')
-      this.showResume = false
-      this.savedSession = null
-    },
-    selectGoal: function(g) {
-      this.selectedGoal = g
-    },
-    goStep2: function() {
-      if (this.selectedGoal) {
-        this.step = 2
-        this.saveSession()
-      }
-    },
-    goStep1: function() {
-      this.step = 1
-      this.saveSession()
-    },
-    selectType: function(id) {
-      this.questionType = id
-    },
-    startQuiz: function() {
-      if (!this.questionType) return
-      this.generateQuestions()
-      this.currentQ = 0
-      this.answers = []
-      this.step = 3
-      this.saveSession()
-    },
-    shuffle: function(arr) {
-      var a = arr.slice()
-      for (var i = a.length - 1; i > 0; i--) {
-        var j = Math.floor(Math.random() * (i + 1))
-        var tmp = a[i]
-        a[i] = a[j]
-        a[j] = tmp
-      }
-      return a
-    },
-    convertToTF: function(q) {
-      var opts = q.options
-      var idx = opts.indexOf(q.answer)
-      var wrongOpts = []
-      for (var w = 0; w < opts.length; w++) {
-        if (opts[w] !== q.answer) {
-          wrongOpts.push(opts[w])
-        }
-      }
-      var isTrue = Math.random() > 0.4
-      var claimIdx
-      if (isTrue) {
-        claimIdx = idx
-      } else {
-        var randomWrong = wrongOpts[Math.floor(Math.random() * wrongOpts.length)]
-        claimIdx = opts.indexOf(randomWrong)
-      }
-      return {
-        dimension: q.dimension,
-        question: q.question + ' 正确答案是"' + opts[claimIdx] + '"',
-        options: ['正确', '错误'],
-        answer: isTrue ? '正确' : '错误',
-        isTF: true
-      }
-    },
-    generateQuestions: function() {
-      var goalId = this.selectedGoal && this.selectedGoal.id
-      if (!goalId) return
-      var pool = []
-      for (var p = 0; p < QUESTION_BANK.length; p++) {
-        if (QUESTION_BANK[p].dimension === goalId) {
-          pool.push(QUESTION_BANK[p])
-        }
-      }
-      var shuffled = this.shuffle(pool)
-      var type = this.questionType
-      if (type === 'truefalse') {
-        var tfQuestions = []
-        var count = Math.min(5, shuffled.length)
-        for (var ti = 0; ti < count; ti++) {
-          tfQuestions.push(this.convertToTF(shuffled[ti]))
-        }
-        this.questions = tfQuestions
-      } else if (type === 'mix') {
-        var allDims = ['focus', 'memory', 'logic', 'perception', 'spatial', 'speed']
-        var otherDims = []
-        for (var od = 0; od < allDims.length; od++) {
-          if (allDims[od] !== goalId) {
-            otherDims.push(allDims[od])
-          }
-        }
-        otherDims = this.shuffle(otherDims)
-        var selectedDims = [goalId].concat(otherDims.slice(0, 4))
-        var mixedQuestions = []
-        for (var di = 0; di < selectedDims.length; di++) {
-          var dimPool = []
-          for (var dq = 0; dq < QUESTION_BANK.length; dq++) {
-            if (QUESTION_BANK[dq].dimension === selectedDims[di]) {
-              dimPool.push(QUESTION_BANK[dq])
-            }
-          }
-          if (dimPool.length > 0) {
-            var pick = this.shuffle(dimPool)
-            mixedQuestions.push(pick[0])
-          }
-        }
-        this.questions = this.shuffle(mixedQuestions).slice(0, 5)
-      } else {
-        this.questions = shuffled.slice(0, 5)
-      }
-    },
-    nextQuestion: function() {
-      if (!this.currentAnswer) return
-      if (this.currentQ < this.questions.length - 1) {
-        this.currentQ++
-        this.saveSession()
-      } else {
-        this.step = 4
-        this.saveSession()
-      }
-    },
-    prevQuestion: function() {
-      if (this.currentQ > 0) {
-        this.currentQ--
-      }
-    },
-    resetQuiz: function() {
-      uni.removeStorageSync('selfQuizSession')
-      this.step = 1
-      this.selectedGoal = null
-      this.questionType = null
-      this.questions = []
-      this.currentQ = 0
-      this.answers = []
-    },
-    goHome: function() {
-      uni.navigateTo({ url: '/pages/wisdom-detail/index' })
-    },
-    goStep: function(n) {
-      if (n === this.step) return
-      if (n < this.step) {
-        this.step = n
-        this.saveSession()
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.container {
-  min-height: 100vh;
-  background: #f5f7fa;
-  padding: 30rpx;
-  padding-bottom: 120rpx;
-  box-sizing: border-box;
-}
-
-.resume-prompt {
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 50rpx 30rpx;
-  text-align: center;
-  margin-top: 160rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
-}
-.resume-text {
-  font-size: 28rpx;
-  color: #333;
-  display: block;
-  margin-bottom: 30rpx;
-}
-.resume-btns {
-  display: flex;
-  justify-content: center;
-  gap: 20rpx;
-}
-
-.step-wrap {
-  margin-bottom: 10rpx;
-}
-.step-row {
-  display: flex;
-  justify-content: center;
-  align-items: flex-start;
-  padding: 20rpx 0;
-}
-.step-item {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  margin: 0 24rpx;
-}
-.step-dot {
-  width: 48rpx;
-  height: 48rpx;
-  border-radius: 50%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  border: 3rpx solid #ddd;
-  background: #fff;
-  transition: all 0.3s;
-}
-.step-dot--active {
-  background: #FFD700;
-  border-color: #FFD700;
-}
-.step-dot--done {
-  border-color: #FFD700;
-  background: #FFD700;
-}
-.step-dot--pending {
-  border-color: #ddd;
-  background: #fff;
-}
-.step-check {
-  font-size: 22rpx;
-  color: #fff;
-  font-weight: bold;
-}
-.step-num {
-  font-size: 22rpx;
-  color: #999;
-}
-.step-dot--active .step-num {
-  color: #fff;
-}
-.step-label {
-  font-size: 20rpx;
-  margin-top: 8rpx;
-  color: #999;
-}
-.step-label--active {
-  color: #FFD700;
-  font-weight: bold;
-}
-.step-label--muted {
-  color: #ccc;
-}
-
-.step-content {
-  animation: fadeIn 0.3s ease;
-}
-@keyframes fadeIn {
-  from { opacity: 0; transform: translateY(20rpx); }
-  to { opacity: 1; transform: translateY(0); }
-}
-
-.content-header {
-  text-align: center;
-  margin-bottom: 30rpx;
-}
-.content-title {
-  font-size: 36rpx;
-  font-weight: bold;
-  color: #333;
-  display: block;
-}
-.content-sub {
-  font-size: 24rpx;
-  color: #999;
-  margin-top: 10rpx;
-  display: block;
-}
-.section-title {
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #333;
-  margin-bottom: 20rpx;
-}
-
-.goal-grid {
-  display: flex;
-  flex-wrap: wrap;
-}
-.goal-card {
-  width: calc(50% - 12rpx);
-  margin: 6rpx;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 30rpx 16rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  border: 3rpx solid transparent;
-  transition: all 0.3s;
-  box-sizing: border-box;
-}
-.goal-card--selected {
-  border-color: #FFD700;
-  background: #FFF8E1;
-}
-.goal-icon-wrap {
-  width: 80rpx;
-  height: 80rpx;
-  border-radius: 50%;
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-bottom: 16rpx;
-}
-.goal-icon {
-  font-size: 32rpx;
-  color: #fff;
-  font-weight: bold;
-}
-.goal-title {
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #333;
-  margin-bottom: 6rpx;
-}
-.goal-desc {
-  font-size: 22rpx;
-  color: #999;
-  text-align: center;
-  line-height: 1.4;
-}
-
-.type-list {
-  margin-top: 10rpx;
-}
-.type-card {
-  display: flex;
-  align-items: center;
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 28rpx 24rpx;
-  margin-bottom: 20rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
-  border: 3rpx solid transparent;
-  transition: all 0.3s;
-}
-.type-card--selected {
-  border-color: #FFD700;
-  background: #FFF8E1;
-}
-.type-icon-wrap {
-  width: 72rpx;
-  height: 72rpx;
-  border-radius: 50%;
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 20rpx;
-  flex-shrink: 0;
-}
-.type-icon {
-  font-size: 28rpx;
-  color: #fff;
-  font-weight: bold;
-}
-.type-info {
-  flex: 1;
-}
-.type-name {
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #333;
-  display: block;
-}
-.type-desc {
-  font-size: 22rpx;
-  color: #999;
-  margin-top: 4rpx;
-  display: block;
-}
-.type-radio {
-  width: 36rpx;
-  height: 36rpx;
-  border-radius: 50%;
-  border: 3rpx solid #ddd;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-  margin-left: 16rpx;
-}
-.type-radio--checked {
-  border-color: #FFD700;
-}
-.type-radio-dot {
-  width: 20rpx;
-  height: 20rpx;
-  border-radius: 50%;
-  background: #FFD700;
-}
-
-.progress-header {
-  margin-bottom: 30rpx;
-}
-.progress-label {
-  font-size: 26rpx;
-  color: #666;
-  display: block;
-  margin-bottom: 12rpx;
-}
-.progress-bar {
-  height: 8rpx;
-  background: #eee;
-  border-radius: 4rpx;
-  overflow: hidden;
-}
-.progress-fill {
-  height: 100%;
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-  border-radius: 4rpx;
-  transition: width 0.3s ease;
-}
-
-.question-card {
-  background: #fff;
-  border-radius: 20rpx;
-  padding: 36rpx 28rpx;
-  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
-  margin-bottom: 30rpx;
-}
-.q-tag {
-  display: inline-block;
-  padding: 6rpx 20rpx;
-  background: #FFF8E1;
-  color: #B8860B;
-  font-size: 20rpx;
-  border-radius: 20rpx;
-  margin-bottom: 20rpx;
-}
-.q-text {
-  font-size: 30rpx;
-  color: #333;
-  line-height: 1.6;
-  display: block;
-  margin-bottom: 30rpx;
-}
-.q-options {
-  display: flex;
-  flex-direction: column;
-  gap: 16rpx;
-}
-.q-option {
-  display: flex;
-  align-items: center;
-  padding: 24rpx 20rpx;
-  border-radius: 16rpx;
-  border: 2rpx solid #eee;
-  transition: all 0.3s;
-}
-.q-option--selected {
-  border-color: #FFD700;
-  background: #FFF8E1;
-}
-.q-option--tf {
-  justify-content: center;
-  padding: 28rpx;
-}
-.q-option-tf-text {
-  font-size: 28rpx;
-  font-weight: bold;
-  color: #666;
-}
-.q-option--selected .q-option-tf-text {
-  color: #B8860B;
-}
-.q-radio {
-  width: 32rpx;
-  height: 32rpx;
-  border-radius: 50%;
-  border: 3rpx solid #ddd;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-  margin-right: 16rpx;
-}
-.q-radio--checked {
-  border-color: #FFD700;
-}
-.q-radio-dot {
-  width: 18rpx;
-  height: 18rpx;
-  border-radius: 50%;
-  background: #FFD700;
-}
-.q-option-text {
-  font-size: 26rpx;
-  color: #333;
-  flex: 1;
-}
-
-.action-bar {
-  margin-top: 40rpx;
-  display: flex;
-  flex-direction: column;
-  gap: 16rpx;
-}
-.action-bar--double {
-  flex-direction: row;
-  gap: 20rpx;
-}
-.action-bar--double .btn {
-  flex: 1;
-}
-
-.btn {
-  padding: 24rpx 0;
-  border-radius: 16rpx;
-  text-align: center;
-  font-size: 28rpx;
-  font-weight: bold;
-  transition: all 0.3s;
-  box-sizing: border-box;
-}
-.btn--primary {
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-  color: #fff;
-}
-.btn--primary:active {
-  opacity: 0.9;
-  transform: scale(0.98);
-}
-.btn--disabled {
-  opacity: 0.4;
-  pointer-events: none;
-}
-.btn--outline {
-  border: 3rpx solid #FFD700;
-  color: #B8860B;
-  background: #fff;
-}
-.btn--outline:active {
-  background: #FFF8E1;
-}
-.btn--ghost {
-  color: #999;
-  background: transparent;
-}
-
-.result-header {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  margin: 40rpx 0;
-}
-.score-circle {
-  width: 160rpx;
-  height: 160rpx;
-  border-radius: 50%;
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  box-shadow: 0 8rpx 30rpx rgba(255,215,0,0.3);
-  margin-bottom: 20rpx;
-}
-.score-value {
-  font-size: 40rpx;
-  font-weight: bold;
-  color: #fff;
-}
-.score-label {
-  font-size: 28rpx;
-  color: #666;
-  display: block;
-}
-
-.result-dims {
-  margin: 20rpx 0 30rpx;
-}
-.dim-bar-item {
-  margin-bottom: 24rpx;
-}
-.dim-bar-header {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 8rpx;
-}
-.dim-bar-name {
-  font-size: 24rpx;
-  color: #333;
-  font-weight: bold;
-}
-.dim-bar-count {
-  font-size: 22rpx;
-  color: #999;
-}
-.dim-bar-track {
-  height: 8rpx;
-  background: #eee;
-  border-radius: 4rpx;
-  overflow: hidden;
-}
-.dim-bar-fill {
-  height: 100%;
-  background: linear-gradient(135deg, #FFD700, #FFA500);
-  border-radius: 4rpx;
-  transition: width 0.6s ease;
-}
-
-.bottom-spacer {
-  height: 120rpx;
-}
-</style>

+ 0 - 178
cfc-frontend/pages/wisdom/training-hub.vue

@@ -1,178 +0,0 @@
-<template>
-  <view class="container">
-    <view class="header-bar">
-      <text class="back-btn" @click="goBack">← 返回</text>
-      <text class="header-title">认知训练中心</text>
-      <text class="header-placeholder"></text>
-    </view>
-
-    <view class="desc-section">
-      <text class="desc-text">选择训练游戏,提升你的认知能力</text>
-    </view>
-
-    <view class="game-card" @click="goGame('schulte')">
-      <view class="game-card-left">
-        <text class="game-icon">🎯</text>
-      </view>
-      <view class="game-card-center">
-        <text class="game-name">舒尔特方格</text>
-        <text class="game-desc">提升专注力和反应速度</text>
-      </view>
-      <text class="game-arrow">›</text>
-    </view>
-
-    <view class="game-card" @click="goGame('1a2b')">
-      <view class="game-card-left">
-        <text class="game-icon">🔢</text>
-      </view>
-      <view class="game-card-center">
-        <text class="game-name">猜数字</text>
-        <text class="game-desc">锻炼逻辑思维和推理能力</text>
-      </view>
-      <text class="game-arrow">›</text>
-    </view>
-
-    <view class="note-section">
-      <text class="note-text">更多训练游戏正在开发中,敬请期待</text>
-    </view>
-  </view>
-</template>
-
-<script>
-export default {
-  data: function() {
-    return {
-      memberId: null
-    }
-  },
-  onLoad: function(options) {
-    if (options && options.memberId) {
-      this.memberId = options.memberId
-    }
-  },
-  methods: {
-    goBack: function() {
-      uni.navigateBack()
-    },
-    goGame: function(game) {
-      var url = ''
-      if (game === 'schulte') {
-        url = '/pages/games/schulte'
-      } else if (game === '1a2b') {
-        url = '/pages/games/1a2b'
-      }
-      if (url) {
-        uni.navigateTo({ url: url })
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.container {
-  min-height: 100vh;
-  background: linear-gradient(180deg, #FFF8E1 0%, #f5f7fa 100%);
-  padding-bottom: 60rpx;
-}
-
-.header-bar {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: space-between;
-  padding: 80rpx 30rpx 30rpx;
-}
-
-.back-btn {
-  font-size: 30rpx;
-  color: #B8860B;
-  padding: 10rpx;
-}
-
-.header-title {
-  font-size: 36rpx;
-  font-weight: bold;
-  color: #B8860B;
-}
-
-.header-placeholder {
-  width: 60rpx;
-}
-
-.desc-section {
-  padding: 0 30rpx 30rpx;
-}
-
-.desc-text {
-  font-size: 26rpx;
-  color: #999;
-  text-align: center;
-  display: block;
-}
-
-.game-card {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  background: #fff;
-  margin: 0 30rpx 20rpx;
-  padding: 30rpx;
-  border-radius: 20rpx;
-  box-shadow: 0 4rpx 20rpx rgba(255, 215, 0, 0.12);
-  border-left: 8rpx solid #FFD700;
-}
-
-.game-card:active {
-  opacity: 0.8;
-}
-
-.game-card-left {
-  width: 80rpx;
-  height: 80rpx;
-  border-radius: 20rpx;
-  background: #FFF8E1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 20rpx;
-}
-
-.game-icon {
-  font-size: 40rpx;
-}
-
-.game-card-center {
-  flex: 1;
-}
-
-.game-name {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #333;
-  display: block;
-  margin-bottom: 6rpx;
-}
-
-.game-desc {
-  font-size: 24rpx;
-  color: #999;
-  display: block;
-}
-
-.game-arrow {
-  font-size: 40rpx;
-  color: #ccc;
-  margin-left: 10rpx;
-}
-
-.note-section {
-  padding: 40rpx 30rpx;
-  text-align: center;
-}
-
-.note-text {
-  font-size: 24rpx;
-  color: #ccc;
-}
-</style>