Sfoglia il codice sorgente

feat: add frontend components, docs assets and .gitignore for Python

User 2 mesi fa
parent
commit
df35537a6e
28 ha cambiato i file con 7718 aggiunte e 0 eliminazioni
  1. 4 0
      .gitignore
  2. 250 0
      cfc-frontend/components/OnboardingGuide.vue
  3. 319 0
      cfc-frontend/components/wuxing-sandbox-helpers/flow-animation.js
  4. 138 0
      docs/ppt-cfc-overview/assets/animations/animations.css
  5. 99 0
      docs/ppt-cfc-overview/assets/animations/fx-runtime.js
  6. 150 0
      docs/ppt-cfc-overview/assets/base.css
  7. 9 0
      docs/ppt-cfc-overview/assets/fonts.css
  8. 960 0
      docs/ppt-cfc-overview/assets/runtime.js
  9. 663 0
      docs/ppt-cfc-overview/index.html
  10. 252 0
      docs/ppt-cfc-overview/style.css
  11. BIN
      docs/screenshots/policy/01-cover.png
  12. BIN
      docs/screenshots/policy/02-score-dashboard.png
  13. BIN
      docs/screenshots/policy/03-disease-risk.png
  14. BIN
      docs/screenshots/policy/04-nutrition-overview.png
  15. BIN
      docs/screenshots/policy/05-vitamins.png
  16. BIN
      docs/screenshots/policy/06-health-score.png
  17. BIN
      docs/screenshots/policy/07-gut-age.png
  18. BIN
      docs/screenshots/policy/08-bacteria-balance.png
  19. BIN
      docs/screenshots/policy/09-bacteria-diversity.png
  20. BIN
      docs/screenshots/policy/10-diet-recommendations.png
  21. BIN
      docs/screenshots/policy/health_china_2030.png
  22. BIN
      docs/screenshots/policy/health_consumption.png
  23. BIN
      docs/screenshots/policy/health_family_notice.png
  24. 2916 0
      docs/superpowers/plans/2026-06-15-comprehensive-discrepancy-fix.md
  25. 722 0
      docs/superpowers/plans/2026-06-15-integer-price-migration.md
  26. 235 0
      docs/superpowers/plans/2026-06-15-parent-homepage-overhaul.md
  27. 242 0
      docs/精准营养-slide内容方案.md
  28. 759 0
      docs/融资计划书-PPT内容方案.md

+ 4 - 0
.gitignore

@@ -43,6 +43,10 @@ coverage/
 *.png
 !docs/**/*.png
 
+# Python
+*.py
+__pycache__/
+
 # OpenCode
 .opencode/
 dev-browser/

+ 250 - 0
cfc-frontend/components/OnboardingGuide.vue

@@ -0,0 +1,250 @@
+<template>
+  <view class="onboarding-overlay" v-if="show">
+    <view class="onboarding-card">
+      <!-- 顶部品牌条 -->
+      <view class="onboarding-topbar"></view>
+
+      <!-- 步骤内容 -->
+      <view class="onboarding-content" :key="currentStep">
+        <text class="onboarding-icon">{{ steps[currentStep].icon }}</text>
+        <text class="onboarding-title">{{ steps[currentStep].title }}</text>
+        <text class="onboarding-desc">{{ steps[currentStep].desc }}</text>
+      </view>
+
+      <!-- 步进器 -->
+      <view class="onboarding-dots">
+        <view
+          v-for="(s, i) in steps"
+          :key="i"
+          class="dot"
+          :class="{ active: i === currentStep }">
+        </view>
+      </view>
+
+      <!-- 按钮 -->
+      <view class="onboarding-action">
+        <view
+          class="onboarding-btn"
+          :class="{ 'onboarding-btn--last': currentStep === steps.length - 1 }"
+          @click="handleNext">
+          <text>{{ currentStep < steps.length - 1 ? '下一步' : '开始体验!' }}</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+var PARENT_STEPS = [
+  {
+    icon: '🌏',
+    title: '五维能量沙盘',
+    desc: '这是你家的五维全景图,身·心·智·行·富,每个维度一目了然,帮你全面掌握家庭成长状态'
+  },
+  {
+    icon: '📋',
+    title: '任务激励系统',
+    desc: '给孩子布置小任务,完成后获得星星积分,养成好习惯就这么简单'
+  },
+  {
+    icon: '🎁',
+    title: '心愿兑换',
+    desc: '孩子用积分兑换心愿,努力就有回报,培养目标感和延迟满足能力'
+  }
+]
+
+var CHILD_STEPS = [
+  {
+    icon: '🌟',
+    title: '赚星星',
+    desc: '完成爸爸妈妈布置的任务,就能获得星星积分,越努力越多!'
+  },
+  {
+    icon: '🎮',
+    title: '玩中学',
+    desc: '完成任务后可以玩小游戏、兑换心愿,学习和快乐两不误!'
+  },
+  {
+    icon: '🏆',
+    title: '升级挑战',
+    desc: '从星星宝宝到宇宙小英雄,一步步升级,看看你能走多远!'
+  }
+]
+
+export default {
+  name: 'OnboardingGuide',
+  props: {
+    role: { type: String, default: 'parent' },
+    show: { type: Boolean, default: false }
+  },
+  data() {
+    return {
+      currentStep: 0
+    }
+  },
+  computed: {
+    steps() {
+      if (this.role === 'child') return CHILD_STEPS
+      return PARENT_STEPS
+    }
+  },
+  watch: {
+    show(val) {
+      if (val) {
+        this.currentStep = 0
+      }
+    }
+  },
+  methods: {
+    handleNext() {
+      if (this.currentStep < this.steps.length - 1) {
+        this.currentStep++
+      } else {
+        uni.setStorageSync('_onboardingDone', true)
+        this.$emit('close')
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* ============================================================
+   OnboardingGuide — 首次登录引导浮层
+   设计体系:统一使用 uni.scss design tokens
+   ============================================================ */
+
+.onboarding-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, 0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 9998;
+  animation: fadeIn 0.25s ease;
+}
+
+.onboarding-card {
+  width: 640rpx;
+  min-height: 680rpx;
+  background: var(--surface, #FFFFFF);
+  border-radius: var(--radius-xl, 32rpx);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  overflow: hidden;
+  box-shadow: var(--shadow-lg, 0 8rpx 32rpx rgba(91, 155, 213, 0.10));
+  animation: scaleIn 0.3s cubic-bezier(0.16, 1, 0.3, 1);
+}
+
+/* 顶部品牌装饰条 — 暖珊瑚色 */
+.onboarding-topbar {
+  width: 100%;
+  height: 12rpx;
+  background: var(--color-primary, #F97316);
+  flex-shrink: 0;
+}
+
+/* ============================================================
+   内容区 — flex 居中,带淡入动画
+   ============================================================ */
+.onboarding-content {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 80rpx 56rpx 48rpx;
+  animation: fadeIn 0.3s ease;
+}
+
+.onboarding-icon {
+  font-size: 80rpx;
+  margin-bottom: 32rpx;
+  display: block;
+}
+
+.onboarding-title {
+  font-size: 36rpx;
+  font-weight: 700;
+  color: var(--text, #1E293B);
+  text-align: center;
+  margin-bottom: 20rpx;
+  display: block;
+  line-height: 1.3;
+}
+
+.onboarding-desc {
+  font-size: 28rpx;
+  color: var(--text-secondary, #64748B);
+  text-align: center;
+  line-height: 1.6;
+  display: block;
+  padding: 0 16rpx;
+}
+
+/* ============================================================
+   步进器圆点
+   ============================================================ */
+.onboarding-dots {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 16rpx;
+  padding: 0 56rpx 40rpx;
+  flex-shrink: 0;
+}
+
+.dot {
+  width: 14rpx;
+  height: 14rpx;
+  border-radius: 50%;
+  background: var(--border, #CBD5E1);
+  transition: background 0.3s ease, transform 0.3s ease;
+}
+
+.dot.active {
+  background: var(--color-primary, #F97316);
+  transform: scale(1.3);
+}
+
+/* ============================================================
+   底部按钮
+   ============================================================ */
+.onboarding-action {
+  width: 100%;
+  padding: 0 56rpx 48rpx;
+  flex-shrink: 0;
+  box-sizing: border-box;
+}
+
+.onboarding-btn {
+  width: 100%;
+  height: 88rpx;
+  border-radius: 999rpx;
+  background: var(--color-primary, #5B9BD5);
+  color: #FFFFFF;
+  font-size: 32rpx;
+  font-weight: 700;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: opacity 0.2s ease, transform 0.15s ease;
+  box-shadow: 0 4rpx 16rpx rgba(91, 155, 213, 0.25);
+}
+
+.onboarding-btn:active {
+  opacity: 0.85;
+  transform: scale(0.97);
+}
+
+/* 最后一步:暖珊瑚色按钮 */
+.onboarding-btn--last {
+  background: var(--color-warm, #F08A7A);
+  box-shadow: 0 4rpx 16rpx rgba(240, 138, 122, 0.30);
+}
+</style>

+ 319 - 0
cfc-frontend/components/wuxing-sandbox-helpers/flow-animation.js

@@ -0,0 +1,319 @@
+/**
+ * wuxing-sandbox-helpers/flow-animation.js
+ * 五行能量流动粒子动画
+ * 
+ * 相生循环:沿外圆路径,能量依次转化(土→金→水→木→火)
+ *   身(土)→智(金)→富(水)→行(木)→心(火)→身(土)
+ *   路径:outer[i] → outer[(i+1)%5] (弧线)
+ *   颜色随路径从源维度过渡到目标维度
+ * 
+ * 相克循环:沿五角星边路径,能量抑制
+ *   身(土)→富(水), 富(水)→心(火), 心(火)→智(金), 智(金)→行(木), 行(木)→身(土)
+ *   路径:outer[i] → inner[(i+1)%5] → outer[(i+2)%5] (星边直线)
+ *   粒子保持源维度颜色
+ */
+
+var WUXING_COLORS = [
+  { r: 141, g: 110, b: 99 },  // 0 土·身 (body)
+  { r: 255, g: 107, b: 53 },  // 1 火·心 (mind)
+  { r: 255, g: 215, b: 0 },   // 2 金·智 (wisdom)
+  { r: 76, g: 175, b: 80 },   // 3 木·行 (action)
+  { r: 66, g: 165, b: 245 }   // 4 水·富 (wealth)
+]
+
+var DIM_KEYS = ['body', 'mind', 'wisdom', 'action', 'wealth']
+
+/**
+ * 维度代码 → 颜色
+ */
+function colorForDimCode(code) {
+  var idx = DIM_KEYS.indexOf(code)
+  if (idx < 0) return { r: 200, g: 200, b: 200 }
+  return WUXING_COLORS[idx]
+}
+
+/**
+ * 计算两点间的弧线路径(圆心(cx,cy),顺时针方向)
+ * @param {{x:number,y:number}} src
+ * @param {{x:number,y:number}} dst
+ * @param {number} cx
+ * @param {number} cy
+ * @param {number} numSegments - 分段数
+ * @returns {Array<{x:number,y:number}>}
+ */
+function arcWaypoints(src, dst, cx, cy, numSegments) {
+  numSegments = numSegments || 8
+  var angleSrc = Math.atan2(src.y - cy, src.x - cx)
+  var angleDst = Math.atan2(dst.y - cy, dst.x - cx)
+  var r = Math.sqrt(Math.pow(src.x - cx, 2) + Math.pow(src.y - cy, 2))
+
+  // 顺时针方向角度差
+  var diff = angleDst - angleSrc
+  if (diff < 0) diff += Math.PI * 2
+
+  var points = []
+  for (var i = 0; i <= numSegments; i++) {
+    var t = i / numSegments
+    var angle = angleSrc + diff * t
+    points.push({
+      x: cx + r * Math.cos(angle),
+      y: cy + r * Math.sin(angle)
+    })
+  }
+  return points
+}
+
+/**
+ * 粒子对象
+ */
+function Particle(path, srcColor, dstColor, speed, baseSize) {
+  this.path = path             // [{x,y},...] 路径控制点
+  this.srcColor = srcColor     // {r,g,b}
+  this.dstColor = dstColor     // {r,g,b}(相克 与 srcColor 相同)
+  this.progress = 0            // 0→1
+  this.speed = speed
+  this.baseSize = baseSize || 3
+  this.x = path[0].x
+  this.y = path[0].y
+  this.currentColor = { r: srcColor.r, g: srcColor.g, b: srcColor.b }
+  this.size = this.baseSize
+  this.alive = true
+}
+
+Particle.prototype.update = function () {
+  this.progress += this.speed
+  if (this.progress >= 1) {
+    this.alive = false
+    return
+  }
+
+  var t = this.progress
+  var totalSegs = this.path.length - 1
+  var segIdx = Math.min(Math.floor(t * totalSegs), totalSegs - 1)
+  var segFrac = (t * totalSegs) - segIdx
+  var p0 = this.path[segIdx]
+  var p1 = this.path[segIdx + 1]
+
+  this.x = p0.x + (p1.x - p0.x) * segFrac
+  this.y = p0.y + (p1.y - p0.y) * segFrac
+
+  // 颜色插值
+  this.currentColor = {
+    r: Math.round(this.srcColor.r + (this.dstColor.r - this.srcColor.r) * t),
+    g: Math.round(this.srcColor.g + (this.dstColor.g - this.srcColor.g) * t),
+    b: Math.round(this.srcColor.b + (this.dstColor.b - this.srcColor.b) * t)
+  }
+
+  // 粒子衰减
+  this.size = this.baseSize * (1 - t * 0.3)
+}
+
+/**
+ * FlowAnimation - 五行能量粒子流动画控制器
+ * 使用独立 Canvas 覆盖层绘制粒子
+ *
+ * @param {CanvasRenderingContext2D} ctx
+ * @param {number} w - Canvas 宽度(逻辑像素)
+ * @param {number} h - Canvas 高度(逻辑像素)
+ * @param {Object} opts
+ * @param {Array<{x:number,y:number}>} opts.outer - calcStarVertices 外顶点数组
+ * @param {Array<{x:number,y:number}>} opts.inner - calcStarVertices 内顶点数组
+ * @param {Array<string>} opts.dimOrder - sortedDimensions 的 code 数组
+ */
+export function FlowAnimation(ctx, w, h, opts) {
+  this._ctx = ctx
+  this._w = w
+  this._h = h
+  this._cx = w / 2
+  this._cy = h / 2
+  this._outer = opts.outer
+  this._inner = opts.inner
+  this._dimOrder = opts.dimOrder
+
+  this._particles = []
+  this._timer = null
+  this._running = false
+  this._frameCount = 0
+
+  this._buildShengPaths()
+  this._buildKePaths()
+}
+
+/**
+ * 构建相生路径(外圆弧线)
+ * 顶点顺序 0→1→2→3→4→0
+ */
+FlowAnimation.prototype._buildShengPaths = function () {
+  this._shengPaths = []
+  for (var i = 0; i < 5; i++) {
+    var src = this._outer[i]
+    var dst = this._outer[(i + 1) % 5]
+    this._shengPaths.push({
+      waypoints: arcWaypoints(src, dst, this._cx, this._cy, 6),
+      srcColor: colorForDimCode(this._dimOrder[i]),
+      dstColor: colorForDimCode(this._dimOrder[(i + 1) % 5])
+    })
+  }
+}
+
+/**
+ * 构建相克路径(五角星边)
+ * 相克索引对:0→2, 2→4, 4→1, 1→3, 3→0
+ * 路径:outer[s] → inner[(s+1)%5] → outer[e]
+ */
+FlowAnimation.prototype._buildKePaths = function () {
+  this._kePaths = []
+  var segments = [[0, 2], [2, 4], [4, 1], [1, 3], [3, 0]]
+  for (var i = 0; i < segments.length; i++) {
+    var s = segments[i][0]
+    var e = segments[i][1]
+    var srcColor = colorForDimCode(this._dimOrder[s])
+    this._kePaths.push({
+      waypoints: [
+        this._outer[s],
+        this._inner[(s + 1) % 5],
+        this._outer[e]
+      ],
+      srcColor: srcColor,
+      dstColor: srcColor  // 相克粒子保持源元素颜色
+    })
+  }
+}
+
+/**
+ * 启动连续动画循环
+ */
+FlowAnimation.prototype.start = function () {
+  if (this._running) return
+  this._running = true
+  this._loop()
+}
+
+/**
+ * 停止动画循环
+ */
+FlowAnimation.prototype.stop = function () {
+  this._running = false
+  if (this._timer) {
+    clearTimeout(this._timer)
+    this._timer = null
+  }
+}
+
+/**
+ * 刷新路径(当 Canvas 尺寸或维度排序发生变化时调用)
+ */
+FlowAnimation.prototype.refreshPaths = function (opts) {
+  if (opts) {
+    if (opts.outer) this._outer = opts.outer
+    if (opts.inner) this._inner = opts.inner
+    if (opts.dimOrder) this._dimOrder = opts.dimOrder
+    if (opts.w) {
+      this._w = opts.w
+      this._cx = opts.w / 2
+    }
+    if (opts.h) {
+      this._h = opts.h
+      this._cy = opts.h / 2
+    }
+  }
+  this._buildShengPaths()
+  this._buildKePaths()
+}
+
+FlowAnimation.prototype._loop = function () {
+  if (!this._running) return
+  var self = this
+  this._frameCount++
+
+  // 周期性发射粒子
+  if (this._frameCount % 3 === 0) {
+    this._emitSheng()
+  }
+  if (this._frameCount % 5 === 0) {
+    this._emitKe()
+  }
+
+  // 更新所有粒子
+  for (var i = this._particles.length - 1; i >= 0; i--) {
+    this._particles[i].update()
+    if (!this._particles[i].alive) {
+      this._particles.splice(i, 1)
+    }
+  }
+
+  // 绘制
+  this._render()
+
+  this._timer = setTimeout(function () {
+    self._loop()
+  }, 33) // ~30fps
+}
+
+/**
+ * 发射一个相生粒子(随机选择段)
+ */
+FlowAnimation.prototype._emitSheng = function () {
+  if (this._shengPaths.length === 0) return
+  var idx = Math.floor(Math.random() * this._shengPaths.length)
+  var seg = this._shengPaths[idx]
+  this._particles.push(new Particle(
+    seg.waypoints,
+    seg.srcColor,
+    seg.dstColor,
+    0.012 + Math.random() * 0.01,
+    2.5 + Math.random() * 1.5
+  ))
+}
+
+/**
+ * 发射一个相克粒子(随机选择段)
+ */
+FlowAnimation.prototype._emitKe = function () {
+  if (this._kePaths.length === 0) return
+  var idx = Math.floor(Math.random() * this._kePaths.length)
+  var seg = this._kePaths[idx]
+  this._particles.push(new Particle(
+    seg.waypoints,
+    seg.srcColor,
+    seg.srcColor,
+    0.01 + Math.random() * 0.008,
+    3 + Math.random() * 1.5
+  ))
+}
+
+/**
+ * 渲染所有粒子到 Canvas
+ */
+FlowAnimation.prototype._render = function () {
+  var ctx = this._ctx
+  ctx.clearRect(0, 0, this._w, this._h)
+
+  for (var i = 0; i < this._particles.length; i++) {
+    var p = this._particles[i]
+    var c = p.currentColor
+    var alpha = 1 - p.progress * 0.25
+    var r = Math.max(p.size, 1.5)
+
+    // 外层辉光
+    ctx.beginPath()
+    ctx.arc(p.x, p.y, r * 3.5, 0, Math.PI * 2)
+    ctx.closePath()
+    ctx.fillStyle = 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + (alpha * 0.1) + ')'
+    ctx.fill()
+
+    // 内层辉光
+    ctx.beginPath()
+    ctx.arc(p.x, p.y, r * 1.8, 0, Math.PI * 2)
+    ctx.closePath()
+    ctx.fillStyle = 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + (alpha * 0.35) + ')'
+    ctx.fill()
+
+    // 核心圆点
+    ctx.beginPath()
+    ctx.arc(p.x, p.y, r, 0, Math.PI * 2)
+    ctx.closePath()
+    ctx.fillStyle = 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + alpha + ')'
+    ctx.fill()
+  }
+}

+ 138 - 0
docs/ppt-cfc-overview/assets/animations/animations.css

@@ -0,0 +1,138 @@
+/* html-ppt :: animations.css
+ * Apply by adding class="anim-<name>" or data-anim="<name>".
+ * Durations are deliberately snappy; tweak --anim-dur per element.
+ */
+:root{--anim-dur:.7s;--anim-ease:cubic-bezier(.4,0,.2,1)}
+
+/* ---------- FADE DIRECTIONALS ---------- */
+@keyframes kf-fade-up{from{opacity:0;transform:translateY(32px)}to{opacity:1;transform:none}}
+@keyframes kf-fade-down{from{opacity:0;transform:translateY(-32px)}to{opacity:1;transform:none}}
+@keyframes kf-fade-left{from{opacity:0;transform:translateX(-40px)}to{opacity:1;transform:none}}
+@keyframes kf-fade-right{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:none}}
+.anim-fade-up{animation:kf-fade-up var(--anim-dur) var(--anim-ease) both}
+.anim-fade-down{animation:kf-fade-down var(--anim-dur) var(--anim-ease) both}
+.anim-fade-left{animation:kf-fade-left var(--anim-dur) var(--anim-ease) both}
+.anim-fade-right{animation:kf-fade-right var(--anim-dur) var(--anim-ease) both}
+
+/* ---------- RISE / DROP / ZOOM / BLUR / GLITCH ---------- */
+@keyframes kf-rise{from{opacity:0;transform:translateY(60px) scale(.97);filter:blur(6px)}to{opacity:1;transform:none;filter:none}}
+@keyframes kf-drop{from{opacity:0;transform:translateY(-60px) scale(.97)}to{opacity:1;transform:none}}
+@keyframes kf-zoom{0%{opacity:0;transform:scale(.6)}60%{transform:scale(1.04)}100%{opacity:1;transform:scale(1)}}
+@keyframes kf-blur{from{opacity:0;filter:blur(18px)}to{opacity:1;filter:none}}
+@keyframes kf-glitch{0%{opacity:0;transform:translateX(0);clip-path:inset(0 0 0 0)}
+  20%{opacity:1;transform:translateX(-6px);clip-path:inset(20% 0 30% 0)}
+  40%{transform:translateX(4px);clip-path:inset(50% 0 10% 0)}
+  60%{transform:translateX(-3px);clip-path:inset(10% 0 60% 0)}
+  80%{transform:translateX(2px);clip-path:inset(0 0 0 0)}
+  100%{opacity:1;transform:none}}
+.anim-rise-in{animation:kf-rise .9s var(--anim-ease) both}
+.anim-drop-in{animation:kf-drop .8s var(--anim-ease) both}
+.anim-zoom-pop{animation:kf-zoom .7s cubic-bezier(.22,1.3,.36,1) both}
+.anim-blur-in{animation:kf-blur .8s var(--anim-ease) both}
+.anim-glitch-in{animation:kf-glitch .8s steps(5,end) both}
+
+/* ---------- TYPEWRITER ---------- */
+.anim-typewriter{display:inline-block;overflow:hidden;white-space:nowrap;border-right:2px solid currentColor;
+  width:0;animation:kf-type 2.4s steps(40,end) forwards, kf-caret 1s step-end infinite}
+@keyframes kf-type{to{width:100%}}
+@keyframes kf-caret{50%{border-color:transparent}}
+
+/* ---------- GLOW / SHIMMER / GRADIENT-FLOW ---------- */
+@keyframes kf-neon{0%,100%{text-shadow:0 0 8px var(--accent),0 0 20px var(--accent)}
+  50%{text-shadow:0 0 16px var(--accent),0 0 40px var(--accent),0 0 80px var(--accent)}}
+.anim-neon-glow{animation:kf-neon 2s ease-in-out infinite}
+
+.anim-shimmer-sweep{position:relative;overflow:hidden}
+.anim-shimmer-sweep::after{content:"";position:absolute;inset:0;
+  background:linear-gradient(110deg,transparent 40%,rgba(255,255,255,.55) 50%,transparent 60%);
+  transform:translateX(-100%);animation:kf-shimmer 2.4s var(--anim-ease) infinite}
+@keyframes kf-shimmer{to{transform:translateX(100%)}}
+
+.anim-gradient-flow{background:linear-gradient(90deg,var(--accent),var(--accent-2,var(--accent)),var(--accent-3,var(--accent)),var(--accent));
+  background-size:300% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;
+  animation:kf-gradflow 4s linear infinite}
+@keyframes kf-gradflow{to{background-position:300% 0}}
+
+/* ---------- STAGGER LIST ---------- */
+.anim-stagger-list > *{opacity:0;animation:kf-rise .65s var(--anim-ease) both}
+.anim-stagger-list > *:nth-child(1){animation-delay:.05s}
+.anim-stagger-list > *:nth-child(2){animation-delay:.15s}
+.anim-stagger-list > *:nth-child(3){animation-delay:.25s}
+.anim-stagger-list > *:nth-child(4){animation-delay:.35s}
+.anim-stagger-list > *:nth-child(5){animation-delay:.45s}
+.anim-stagger-list > *:nth-child(6){animation-delay:.55s}
+.anim-stagger-list > *:nth-child(7){animation-delay:.65s}
+.anim-stagger-list > *:nth-child(8){animation-delay:.75s}
+.anim-stagger-list > *:nth-child(n+9){animation-delay:.85s}
+
+/* ---------- COUNTER-UP (JS-driven, marker class only) ---------- */
+.counter{font-variant-numeric:tabular-nums}
+
+/* ---------- SVG PATH DRAW ---------- */
+.anim-path-draw path,.anim-path-draw line,.anim-path-draw polyline,.anim-path-draw circle,.anim-path-draw rect{
+  stroke-dasharray:1000;stroke-dashoffset:1000;animation:kf-draw 2s var(--anim-ease) forwards}
+@keyframes kf-draw{to{stroke-dashoffset:0}}
+
+/* ---------- PARALLAX TILT (hover) ---------- */
+.anim-parallax-tilt{transform-style:preserve-3d;transition:transform .4s var(--anim-ease)}
+.anim-parallax-tilt:hover{transform:perspective(900px) rotateX(6deg) rotateY(-8deg) translateZ(10px)}
+
+/* ---------- CARD FLIP 3D ---------- */
+@keyframes kf-flip{from{transform:perspective(1200px) rotateY(-90deg);opacity:0}
+  to{transform:perspective(1200px) rotateY(0);opacity:1}}
+.anim-card-flip-3d{animation:kf-flip .9s var(--anim-ease) both;transform-style:preserve-3d;backface-visibility:hidden}
+
+/* ---------- CUBE ROTATE 3D ---------- */
+@keyframes kf-cube{from{transform:perspective(1200px) rotateX(20deg) rotateY(-90deg) translateZ(-200px);opacity:0}
+  to{transform:perspective(1200px) rotateX(0) rotateY(0) translateZ(0);opacity:1}}
+.anim-cube-rotate-3d{animation:kf-cube 1s var(--anim-ease) both}
+
+/* ---------- PAGE TURN 3D ---------- */
+@keyframes kf-pageturn{from{transform:perspective(1600px) rotateY(-85deg);transform-origin:left center;opacity:0}
+  to{transform:perspective(1600px) rotateY(0);opacity:1}}
+.anim-page-turn-3d{animation:kf-pageturn 1s var(--anim-ease) both;transform-origin:left center}
+
+/* ---------- PERSPECTIVE ZOOM ---------- */
+@keyframes kf-pzoom{from{opacity:0;transform:perspective(1400px) translateZ(-400px) rotateX(12deg)}
+  to{opacity:1;transform:none}}
+.anim-perspective-zoom{animation:kf-pzoom 1s var(--anim-ease) both}
+
+/* ---------- MARQUEE SCROLL ---------- */
+.anim-marquee-scroll{display:flex;gap:48px;white-space:nowrap;animation:kf-marquee 20s linear infinite}
+@keyframes kf-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
+
+/* ---------- KEN BURNS ---------- */
+@keyframes kf-kenburns{0%{transform:scale(1) translate(0,0)}100%{transform:scale(1.15) translate(-2%,-1%)}}
+.anim-kenburns{animation:kf-kenburns 14s ease-in-out infinite alternate}
+
+/* ---------- CONFETTI BURST (pseudo — pure CSS sparkles) ---------- */
+.anim-confetti-burst{position:relative}
+.anim-confetti-burst::before,.anim-confetti-burst::after{
+  content:"";position:absolute;top:50%;left:50%;width:8px;height:8px;border-radius:50%;
+  background:var(--accent);box-shadow:
+    20px -30px 0 var(--accent-2,var(--accent)),-25px -20px 0 var(--accent-3,var(--accent)),
+    30px 20px 0 var(--good,#1aaf6c),-30px 25px 0 var(--warn,#f5a524),
+    40px -10px 0 var(--bad,#e0445a),-45px 0 0 var(--accent),
+    10px 40px 0 var(--accent-2,var(--accent)),-15px -40px 0 var(--accent-3,var(--accent));
+  opacity:0;animation:kf-confetti 1.2s var(--anim-ease) forwards}
+.anim-confetti-burst::after{animation-delay:.15s;transform:rotate(45deg)}
+@keyframes kf-confetti{0%{opacity:0;transform:scale(.2)}30%{opacity:1}100%{opacity:0;transform:scale(2.2)}}
+
+/* ---------- SPOTLIGHT ---------- */
+@keyframes kf-spot{0%{clip-path:circle(0% at 50% 50%)}100%{clip-path:circle(140% at 50% 50%)}}
+.anim-spotlight{animation:kf-spot 1.1s var(--anim-ease) both}
+
+/* ---------- MORPH SHAPE (SVG) ---------- */
+.anim-morph-shape path{animation:kf-morph 6s ease-in-out infinite alternate}
+@keyframes kf-morph{0%{d:path("M60,120 Q120,20 180,120 T300,120")}
+  100%{d:path("M60,120 Q120,220 180,120 T300,120")}}
+
+/* ---------- RIPPLE REVEAL ---------- */
+@keyframes kf-ripple{0%{clip-path:circle(0% at 20% 80%);opacity:.4}
+  100%{clip-path:circle(160% at 20% 80%);opacity:1}}
+.anim-ripple-reveal{animation:kf-ripple 1.2s var(--anim-ease) both}
+
+/* reduced motion */
+@media (prefers-reduced-motion: reduce){
+  [class*="anim-"]{animation:none!important;transition:none!important}
+}

+ 99 - 0
docs/ppt-cfc-overview/assets/animations/fx-runtime.js

@@ -0,0 +1,99 @@
+/* html-ppt :: fx-runtime.js
+ * Canvas FX autoloader + lifecycle manager.
+ * - Dynamically loads all fx modules listed in FX_LIST
+ * - Initializes [data-fx] elements when their slide becomes active
+ * - Calls handle.stop() when the slide leaves
+ */
+(function(){
+  'use strict';
+
+  const FX_LIST = [
+    '_util',
+    'particle-burst','confetti-cannon','firework','starfield','matrix-rain',
+    'knowledge-graph','neural-net','constellation','orbit-ring','galaxy-swirl',
+    'word-cascade','letter-explode','chain-react','magnetic-field','data-stream',
+    'gradient-blob','sparkle-trail','shockwave','typewriter-multi','counter-explosion'
+  ];
+
+  // Resolve base path of this script so it works from any page location.
+  const myScript = document.currentScript || (function(){
+    const all = document.getElementsByTagName('script');
+    for (const s of all){ if (s.src && s.src.indexOf('fx-runtime.js')>-1) return s; }
+    return null;
+  })();
+  const base = myScript ? myScript.src.replace(/fx-runtime\.js.*$/, 'fx/') : 'assets/animations/fx/';
+
+  let loaded = 0;
+  const total = FX_LIST.length;
+  const ready = new Promise((resolve) => {
+    if (!total) return resolve();
+    FX_LIST.forEach((name) => {
+      const s = document.createElement('script');
+      s.src = base + name + '.js';
+      s.async = false;
+      s.onload = s.onerror = () => { if (++loaded >= total) resolve(); };
+      document.head.appendChild(s);
+    });
+  });
+
+  window.__hpxActive = window.__hpxActive || new Map();
+
+  function initFxIn(root){
+    if (!window.HPX) return;
+    const els = root.querySelectorAll('[data-fx]');
+    els.forEach((el) => {
+      if (window.__hpxActive.has(el)) return;
+      const name = el.getAttribute('data-fx');
+      const fn = window.HPX[name];
+      if (typeof fn !== 'function') return;
+      try {
+        const handle = fn(el, {}) || { stop(){} };
+        window.__hpxActive.set(el, handle);
+      } catch(e){ console.warn('[hpx-fx]', name, e); }
+    });
+  }
+
+  function stopFxIn(root){
+    const els = root.querySelectorAll('[data-fx]');
+    els.forEach((el) => {
+      const h = window.__hpxActive.get(el);
+      if (h && typeof h.stop === 'function'){
+        try{ h.stop(); }catch(e){}
+      }
+      window.__hpxActive.delete(el);
+    });
+  }
+
+  function reinitFxIn(root){
+    stopFxIn(root);
+    initFxIn(root);
+  }
+  window.__hpxReinit = reinitFxIn;
+
+  function boot(){
+    ready.then(() => {
+      const active = document.querySelector('.slide.is-active') || document.querySelector('.slide');
+      if (active) initFxIn(active);
+
+      // Watch all slides for class changes
+      const slides = document.querySelectorAll('.slide');
+      slides.forEach((sl) => {
+        const mo = new MutationObserver((muts) => {
+          for (const m of muts){
+            if (m.attributeName === 'class'){
+              if (sl.classList.contains('is-active')) initFxIn(sl);
+              else stopFxIn(sl);
+            }
+          }
+        });
+        mo.observe(sl, { attributes: true, attributeFilter: ['class'] });
+      });
+    });
+  }
+
+  if (document.readyState === 'loading'){
+    document.addEventListener('DOMContentLoaded', boot);
+  } else {
+    boot();
+  }
+})();

+ 150 - 0
docs/ppt-cfc-overview/assets/base.css

@@ -0,0 +1,150 @@
+/* html-ppt :: base.css — reset + shared tokens + layout primitives */
+/* Default tokens. Themes in assets/themes/*.css override the :root block. */
+:root {
+  --bg: #ffffff;
+  --bg-soft: #f7f7f8;
+  --surface: #ffffff;
+  --surface-2: #f2f2f4;
+  --border: rgba(0,0,0,.08);
+  --border-strong: rgba(0,0,0,.16);
+  --text-1: #111216;
+  --text-2: #55596a;
+  --text-3: #8a8f9e;
+  --accent: #3b6cff;
+  --accent-2: #7a5cff;
+  --accent-3: #ff5c8a;
+  --good: #1aaf6c;
+  --warn: #f5a524;
+  --bad:  #e0445a;
+  --grad: linear-gradient(135deg,#3b6cff,#7a5cff 55%,#ff5c8a);
+  --grad-soft: linear-gradient(135deg,#eef2ff,#f5ecff 55%,#ffeef5);
+  --radius: 18px;
+  --radius-sm: 12px;
+  --radius-lg: 26px;
+  --shadow: 0 10px 30px rgba(18,24,40,.08), 0 2px 6px rgba(18,24,40,.04);
+  --shadow-lg: 0 24px 60px rgba(18,24,40,.14), 0 6px 16px rgba(18,24,40,.06);
+  --font-sans: 'Inter','Noto Sans SC',-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;
+  --font-serif: 'Playfair Display','Noto Serif SC',Georgia,serif;
+  --font-mono: 'JetBrains Mono','IBM Plex Mono',SFMono-Regular,Menlo,monospace;
+  --font-display: var(--font-sans);
+  --letter-tight: -.03em;
+  --letter-normal: -.01em;
+  --ease: cubic-bezier(.4,0,.2,1);
+}
+
+*,*::before,*::after{box-sizing:border-box}
+html,body{margin:0;padding:0;background:var(--bg);color:var(--text-1);
+  font-family:var(--font-sans);font-weight:400;line-height:1.6;
+  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
+  letter-spacing:var(--letter-normal)}
+img,svg,video{max-width:100%;display:block}
+a{color:var(--accent);text-decoration:none}
+a:hover{text-decoration:underline}
+code,kbd,pre,samp{font-family:var(--font-mono)}
+
+/* ================= SLIDE SYSTEM ================= */
+.deck{position:relative;width:100vw;height:100vh;overflow:hidden;background:var(--bg)}
+.slide{
+  position:absolute;inset:0;
+  display:flex;flex-direction:column;justify-content:center;
+  padding:72px 96px;
+  box-sizing:border-box;
+  opacity:0;pointer-events:none;
+  transition:opacity .5s var(--ease), transform .5s var(--ease);
+  transform:translateX(30px);
+  overflow:hidden;
+}
+.slide.is-active{opacity:1;pointer-events:auto;transform:translateX(0);z-index:2}
+.slide.is-prev{transform:translateX(-30px)}
+
+/* single-page standalone (used when a layout file is opened directly) */
+body.single .slide{position:relative;width:100vw;height:100vh;opacity:1;transform:none;pointer-events:auto}
+
+/* ================= TYPOGRAPHY ================= */
+.eyebrow{font-size:13px;font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--text-3)}
+.kicker{font-size:14px;font-weight:600;color:var(--accent);letter-spacing:.08em;text-transform:uppercase}
+h1.title,.h1{font-family:var(--font-display);font-size:72px;line-height:1.05;font-weight:800;letter-spacing:var(--letter-tight);margin:0 0 18px;color:var(--text-1)}
+h2.title,.h2{font-family:var(--font-display);font-size:54px;line-height:1.1;font-weight:700;letter-spacing:var(--letter-tight);margin:0 0 14px}
+h3,.h3{font-size:32px;line-height:1.2;font-weight:600;letter-spacing:var(--letter-normal);margin:0 0 10px}
+h4,.h4{font-size:22px;line-height:1.3;font-weight:600;margin:0 0 8px}
+.lede{font-size:22px;line-height:1.55;color:var(--text-2);font-weight:300;max-width:62ch}
+.dim{color:var(--text-2)}
+.dim2{color:var(--text-3)}
+.mono{font-family:var(--font-mono)}
+.serif{font-family:var(--font-serif)}
+.gradient-text{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent}
+
+/* ================= LAYOUT PRIMITIVES ================= */
+.stack>*+*{margin-top:14px}
+.row{display:flex;gap:24px;align-items:center}
+.row.wrap{flex-wrap:wrap}
+.grid{display:grid;gap:24px}
+.g2{grid-template-columns:repeat(2,1fr)}
+.g3{grid-template-columns:repeat(3,1fr)}
+.g4{grid-template-columns:repeat(4,1fr)}
+.center{display:flex;align-items:center;justify-content:center;text-align:center}
+.fill{flex:1}
+.sp-t{padding-top:24px}.sp-b{padding-bottom:24px}
+.mt-s{margin-top:8px}.mt-m{margin-top:18px}.mt-l{margin-top:32px}
+.mb-s{margin-bottom:8px}.mb-m{margin-bottom:18px}.mb-l{margin-bottom:32px}
+
+/* ================= CARDS ================= */
+.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
+  padding:26px 28px;box-shadow:var(--shadow);position:relative;overflow:hidden}
+.card-soft{background:var(--surface-2);border:1px solid var(--border)}
+.card-outline{background:transparent;border:1.5px solid var(--border-strong);box-shadow:none}
+.card-accent{background:var(--surface);border-top:3px solid var(--accent)}
+.card-hover{transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
+.card-hover:hover{transform:translateY(-4px);box-shadow:var(--shadow-lg)}
+
+.pill{display:inline-block;padding:4px 12px;border-radius:999px;font-size:12px;font-weight:500;
+  background:var(--surface-2);color:var(--text-2);border:1px solid var(--border)}
+.pill-accent{background:color-mix(in srgb,var(--accent) 12%,transparent);color:var(--accent);border-color:color-mix(in srgb,var(--accent) 28%,transparent)}
+
+/* ================= BARS / DIVIDERS ================= */
+.divider{height:1px;background:var(--border);width:100%}
+.divider-accent{height:3px;width:72px;background:var(--accent);border-radius:2px}
+
+/* ================= CHROME (header/footer/progress) ================= */
+.deck-header{position:absolute;top:24px;left:40px;right:40px;display:flex;align-items:center;justify-content:space-between;
+  font-size:12px;color:var(--text-3);letter-spacing:.12em;text-transform:uppercase;z-index:10;pointer-events:none}
+.deck-footer{position:absolute;bottom:24px;left:40px;right:40px;display:flex;align-items:center;justify-content:space-between;
+  font-size:12px;color:var(--text-3);z-index:10;pointer-events:none}
+.slide-number::before{content:attr(data-current)}
+.slide-number::after{content:" / " attr(data-total)}
+.progress-bar{position:fixed;left:0;right:0;bottom:0;height:3px;background:transparent;z-index:20}
+.progress-bar > span{display:block;height:100%;width:0;background:var(--accent);transition:width .3s var(--ease)}
+
+/* ================= PRESENTER / OVERVIEW ================= */
+.notes{display:none!important}
+.notes-overlay{position:fixed;inset:auto 0 0 0;max-height:42vh;background:rgba(20,22,30,.95);color:#e8ebf4;
+  padding:20px 32px;font-size:16px;line-height:1.6;border-top:1px solid rgba(255,255,255,.1);transform:translateY(100%);
+  transition:transform .3s var(--ease);z-index:40;overflow:auto;font-family:var(--font-sans)}
+.notes-overlay.open{transform:translateY(0)}
+.overview{position:fixed;inset:0;background:rgba(10,12,18,.92);backdrop-filter:blur(12px);z-index:50;
+  display:none;padding:40px;overflow:auto}
+.overview.open{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;align-content:start}
+.overview .thumb{background:var(--surface);border:1px solid var(--border);border-radius:12px;
+  aspect-ratio:16/9;overflow:hidden;cursor:pointer;position:relative;color:var(--text-1);padding:16px;
+  font-size:11px;transition:transform .2s var(--ease)}
+.overview .thumb:hover{transform:scale(1.04)}
+.overview .thumb .n{position:absolute;top:8px;left:10px;font-weight:700;font-size:14px;color:var(--text-3)}
+.overview .thumb .t{position:absolute;bottom:10px;left:14px;right:14px;font-weight:600;color:var(--text-1)}
+
+/* ================= PRESENTER VIEW ================= */
+/* Presenter view opens in a separate popup window (S key).
+ * All presenter styles are self-contained in the popup HTML generated by runtime.js.
+ * The audience window (this file) is NOT affected — it stays as normal deck view.
+ * Only the .notes class below is needed to hide speaker notes from audience. */
+
+/* ================= UTILITY ================= */
+.hidden{display:none!important}
+.nowrap{white-space:nowrap}
+.tr{text-align:right}.tc{text-align:center}.tl{text-align:left}
+.uppercase{text-transform:uppercase;letter-spacing:.12em}
+
+/* ================= PRINT ================= */
+@media print{
+  .slide{position:relative;opacity:1!important;transform:none!important;page-break-after:always;height:100vh}
+  .deck-header,.deck-footer,.progress-bar,.notes-overlay,.overview{display:none!important}
+}

+ 9 - 0
docs/ppt-cfc-overview/assets/fonts.css

@@ -0,0 +1,9 @@
+/* html-ppt :: shared webfonts */
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700;800;900&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@200;300;400;500;600;700;900&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300;400;600;700&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;0,800;1,400&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500;700&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&display=swap');

+ 960 - 0
docs/ppt-cfc-overview/assets/runtime.js

@@ -0,0 +1,960 @@
+/* html-ppt :: runtime.js
+ * Keyboard-driven deck runtime. Zero dependencies.
+ *
+ * Features:
+ *   ← → / space / PgUp PgDn / Home End  navigation
+ *   F  fullscreen
+ *   S  presenter mode (opens a NEW WINDOW with current/next slide preview + notes + timer)
+ *       The original window stays as audience view, synced via BroadcastChannel.
+ *       Slide previews use CSS transform:scale() at design resolution for pixel-perfect layout.
+ *   N  quick notes overlay (bottom drawer)
+ *   O  slide overview grid
+ *   T  cycle themes (reads data-themes on <html> or <body>)
+ *   A  cycle demo animation on current slide
+ *   URL hash #/N  deep-link to slide N (1-based)
+ *   Progress bar auto-managed
+ */
+(function () {
+  'use strict';
+
+  const ANIMS = ['fade-up','fade-down','fade-left','fade-right','rise-in','drop-in',
+    'zoom-pop','blur-in','glitch-in','typewriter','neon-glow','shimmer-sweep',
+    'gradient-flow','stagger-list','counter-up','path-draw','parallax-tilt',
+    'card-flip-3d','cube-rotate-3d','page-turn-3d','perspective-zoom',
+    'marquee-scroll','kenburns','confetti-burst','spotlight','morph-shape','ripple-reveal'];
+
+  function ready(fn){ if(document.readyState!='loading')fn(); else document.addEventListener('DOMContentLoaded',fn);}
+
+  /* ========== Parse URL for preview-only mode ==========
+   * When loaded as iframe.src = "index.html?preview=3", runtime enters a
+   * locked single-slide mode: only slide N is visible, no chrome, no keys,
+   * no hash updates. This is how the presenter window shows pixel-perfect
+   * previews — by loading the actual deck file in an iframe and telling it
+   * to display only a specific slide.
+   */
+  function getPreviewIdx() {
+    const m = /[?&]preview=(\d+)/.exec(location.search || '');
+    return m ? parseInt(m[1], 10) - 1 : -1;
+  }
+
+  ready(function () {
+    const deck = document.querySelector('.deck');
+    if (!deck) return;
+    const slides = Array.from(deck.querySelectorAll('.slide'));
+    if (!slides.length) return;
+
+    const previewOnlyIdx = getPreviewIdx();
+    const isPreviewMode = previewOnlyIdx >= 0 && previewOnlyIdx < slides.length;
+
+    /* ===== Preview-only mode: show one slide, hide everything else ===== */
+    if (isPreviewMode) {
+      function showSlide(i) {
+        slides.forEach((s, j) => {
+          const active = (j === i);
+          s.classList.toggle('is-active', active);
+          s.style.display = active ? '' : 'none';
+          if (active) {
+            s.style.opacity = '1';
+            s.style.transform = 'none';
+            s.style.pointerEvents = 'auto';
+          }
+        });
+      }
+      showSlide(previewOnlyIdx);
+      /* Hide chrome that the presenter shouldn't see in preview */
+      const hideSel = '.progress-bar, .notes-overlay, .overview, .notes, aside.notes, .speaker-notes';
+      document.querySelectorAll(hideSel).forEach(el => { el.style.display = 'none'; });
+      document.documentElement.setAttribute('data-preview', '1');
+      document.body.setAttribute('data-preview', '1');
+      /* Auto-detect theme base path for theme switching in preview mode */
+      function getPreviewThemeBase() {
+        const base = document.documentElement.getAttribute('data-theme-base');
+        if (base) return base;
+        const tl = document.getElementById('theme-link');
+        if (tl) {
+          const raw = tl.getAttribute('href') || '';
+          const ls = raw.lastIndexOf('/');
+          if (ls >= 0) return raw.substring(0, ls + 1);
+        }
+        return 'assets/themes/';
+      }
+      const previewThemeBase = getPreviewThemeBase();
+
+      /* Listen for postMessage from parent presenter window:
+       *  - preview-goto: switch visible slide WITHOUT reloading
+       *  - preview-theme: switch theme CSS link to match audience window */
+      window.addEventListener('message', function(e) {
+        if (!e.data) return;
+        if (e.data.type === 'preview-goto') {
+          const n = parseInt(e.data.idx, 10);
+          if (n >= 0 && n < slides.length) showSlide(n);
+        } else if (e.data.type === 'preview-theme' && e.data.name) {
+          let link = document.getElementById('theme-link');
+          if (!link) {
+            link = document.createElement('link');
+            link.rel = 'stylesheet';
+            link.id = 'theme-link';
+            document.head.appendChild(link);
+          }
+          link.href = previewThemeBase + e.data.name + '.css';
+          document.documentElement.setAttribute('data-theme', e.data.name);
+        }
+      });
+      /* Signal to parent that preview iframe is ready */
+      try { window.parent && window.parent.postMessage({ type: 'preview-ready' }, '*'); } catch(e) {}
+      return;
+    }
+
+    let idx = 0;
+    const total = slides.length;
+
+    /* ===== BroadcastChannel for presenter sync ===== */
+    const CHANNEL_NAME = 'html-ppt-presenter-' + location.pathname;
+    let bc;
+    try { bc = new BroadcastChannel(CHANNEL_NAME); } catch(e) { bc = null; }
+
+    // Are we running inside the presenter popup? (legacy flag, now unused)
+    const isPresenterWindow = false;
+
+    /* ===== progress bar ===== */
+    let bar = document.querySelector('.progress-bar');
+    if (!bar) {
+      bar = document.createElement('div');
+      bar.className = 'progress-bar';
+      bar.innerHTML = '<span></span>';
+      document.body.appendChild(bar);
+    }
+    const barFill = bar.querySelector('span');
+
+    /* ===== notes overlay (N key) ===== */
+    let notes = document.querySelector('.notes-overlay');
+    if (!notes) {
+      notes = document.createElement('div');
+      notes.className = 'notes-overlay';
+      document.body.appendChild(notes);
+    }
+
+    /* ===== overview grid (O key) ===== */
+    let overview = document.querySelector('.overview');
+    if (!overview) {
+      overview = document.createElement('div');
+      overview.className = 'overview';
+      slides.forEach((s, i) => {
+        const t = document.createElement('div');
+        t.className = 'thumb';
+        // Force 16:9 aspect ratio robustly
+        t.style.padding = '0 0 56.25% 0';
+        t.style.height = '0';
+        t.style.position = 'relative';
+        t.style.overflow = 'hidden';
+
+        const title = s.getAttribute('data-title') ||
+          (s.querySelector('h1,h2,h3')||{}).textContent || ('Slide '+(i+1));
+        
+        // Create a container for the mini-slide
+        const mini = document.createElement('div');
+        mini.className = 'mini-slide';
+        mini.style.position = 'absolute';
+        mini.style.top = '0';
+        mini.style.left = '0';
+        mini.style.width = '1920px';
+        mini.style.height = '1080px';
+        mini.style.transformOrigin = 'top left';
+        mini.style.pointerEvents = 'none';
+        mini.style.background = 'var(--bg)';
+        
+        // Clone the slide content
+        const clone = s.cloneNode(true);
+        clone.className = 'slide is-active'; // force active styles
+        clone.style.position = 'absolute';
+        clone.style.inset = '0';
+        clone.style.transform = 'none';
+        clone.style.opacity = '1';
+        clone.style.padding = '72px 96px'; // ensure padding is kept
+        
+        mini.appendChild(clone);
+        t.appendChild(mini);
+
+        // Add the number and title overlay
+        const overlay = document.createElement('div');
+        overlay.style.position = 'absolute';
+        overlay.style.inset = '0';
+        overlay.style.background = 'linear-gradient(to bottom, rgba(0,0,0,0.2) 0%, transparent 40%, transparent 60%, rgba(0,0,0,0.8) 100%)';
+        overlay.style.color = '#fff';
+        overlay.style.zIndex = '10';
+        overlay.style.pointerEvents = 'none';
+        
+        const n = document.createElement('div');
+        n.className = 'n';
+        n.textContent = i + 1;
+        n.style.position = 'absolute';
+        n.style.top = '12px';
+        n.style.left = '16px';
+        n.style.fontWeight = '700';
+        n.style.fontSize = '16px';
+        n.style.color = '#fff';
+        n.style.textShadow = '0 1px 4px rgba(0,0,0,0.8)';
+        
+        const text = document.createElement('div');
+        text.className = 't';
+        text.textContent = title.trim().slice(0,80);
+        text.style.position = 'absolute';
+        text.style.bottom = '12px';
+        text.style.left = '16px';
+        text.style.right = '16px';
+        text.style.fontWeight = '600';
+        text.style.fontSize = '14px';
+        text.style.color = '#fff';
+        text.style.textShadow = '0 1px 4px rgba(0,0,0,0.8)';
+        
+        overlay.appendChild(n);
+        overlay.appendChild(text);
+        t.appendChild(overlay);
+
+        t.addEventListener('click', () => { go(i); toggleOverview(false); });
+        overview.appendChild(t);
+      });
+      document.body.appendChild(overview);
+    }
+
+    /* ===== navigation ===== */
+    function go(n, fromRemote){
+      n = Math.max(0, Math.min(total-1, n));
+      slides.forEach((s,i) => {
+        s.classList.toggle('is-active', i===n);
+        s.classList.toggle('is-prev', i<n);
+      });
+      idx = n;
+      barFill.style.width = ((n+1)/total*100)+'%';
+      const numEl = document.querySelector('.slide-number');
+      if (numEl) { numEl.setAttribute('data-current', n+1); numEl.setAttribute('data-total', total); }
+
+      // notes (bottom overlay)
+      const note = slides[n].querySelector('.notes, aside.notes, .speaker-notes');
+      notes.innerHTML = note ? note.innerHTML : '';
+
+      // hash
+      const hashTarget = '#/'+(n+1);
+      if (location.hash !== hashTarget && !isPresenterWindow) {
+        history.replaceState(null,'', hashTarget);
+      }
+
+      // re-trigger entry animations
+      slides[n].querySelectorAll('[data-anim]').forEach(el => {
+        const a = el.getAttribute('data-anim');
+        el.classList.remove('anim-'+a);
+        void el.offsetWidth;
+        el.classList.add('anim-'+a);
+      });
+
+      // counter-up
+      slides[n].querySelectorAll('.counter').forEach(el => {
+        const target = parseFloat(el.getAttribute('data-to')||el.textContent);
+        const dur = parseInt(el.getAttribute('data-dur')||'1200',10);
+        const start = performance.now();
+        const from = 0;
+        function tick(now){
+          const t = Math.min(1,(now-start)/dur);
+          const v = from + (target-from)*(1-Math.pow(1-t,3));
+          el.textContent = (target % 1 === 0) ? Math.round(v) : v.toFixed(1);
+          if (t<1) requestAnimationFrame(tick);
+        }
+        requestAnimationFrame(tick);
+      });
+
+      // Broadcast to other window (audience ↔ presenter)
+      if (!fromRemote && bc) {
+        bc.postMessage({ type: 'go', idx: n });
+      }
+    }
+
+    /* ===== listen for remote navigation / theme changes ===== */
+    if (bc) {
+      bc.onmessage = function(e) {
+        if (!e.data) return;
+        if (e.data.type === 'go' && typeof e.data.idx === 'number') {
+          go(e.data.idx, true);
+        } else if (e.data.type === 'theme' && e.data.name) {
+          /* Sync theme across windows */
+          const i = themes.indexOf(e.data.name);
+          if (i >= 0) themeIdx = i;
+          applyTheme(e.data.name);
+        }
+      };
+    }
+
+    function toggleNotes(force){ notes.classList.toggle('open', force!==undefined?force:!notes.classList.contains('open')); }
+    function toggleOverview(force){
+      const isOpen = force!==undefined ? force : !overview.classList.contains('open');
+      overview.classList.toggle('open', isOpen);
+      if (isOpen) {
+        requestAnimationFrame(() => {
+          const thumbs = overview.querySelectorAll('.thumb');
+          if (thumbs.length) {
+            const scale = thumbs[0].clientWidth / 1920;
+            overview.querySelectorAll('.mini-slide').forEach(m => {
+              m.style.transform = 'scale(' + scale + ')';
+            });
+          }
+        });
+      }
+    }
+
+    /* ========== PRESENTER MODE — Magnetic-card popup window ========== */
+    /* Opens a new window with 4 draggable, resizable cards:
+     *   CURRENT  — iframe(?preview=N)   pixel-perfect preview of current slide
+     *   NEXT     — iframe(?preview=N+1) pixel-perfect preview of next slide
+     *   SCRIPT   — large speaker notes (逐字稿)
+     *   TIMER    — elapsed timer + page counter + controls
+     * Cards remember position/size in localStorage.
+     * Two windows sync via BroadcastChannel.
+     */
+    let presenterWin = null;
+
+    function openPresenterWindow() {
+      if (presenterWin && !presenterWin.closed) {
+        presenterWin.focus();
+        return;
+      }
+
+      // Build absolute URL of THIS deck file (without hash/query)
+      const deckUrl = location.protocol + '//' + location.host + location.pathname;
+
+      // Collect slide titles + notes (HTML strings)
+      const slideMeta = slides.map((s, i) => {
+        const note = s.querySelector('.notes, aside.notes, .speaker-notes');
+        return {
+          title: s.getAttribute('data-title') ||
+            (s.querySelector('h1,h2,h3')||{}).textContent || ('Slide '+(i+1)),
+          notes: note ? note.innerHTML : ''
+        };
+      });
+
+      /* Capture current theme so presenter previews match the audience */
+      const currentTheme = root.getAttribute('data-theme') || (themes[themeIdx] || '');
+      const presenterHTML = buildPresenterHTML(deckUrl, slideMeta, total, idx, CHANNEL_NAME, currentTheme);
+
+      presenterWin = window.open('', 'html-ppt-presenter', 'width=1280,height=820,menubar=no,toolbar=no');
+      if (!presenterWin) {
+        alert('请允许弹出窗口以使用演讲者视图');
+        return;
+      }
+      presenterWin.document.open();
+      presenterWin.document.write(presenterHTML);
+      presenterWin.document.close();
+    }
+
+    function buildPresenterHTML(deckUrl, slideMeta, total, startIdx, channelName, currentTheme) {
+      const metaJSON = JSON.stringify(slideMeta);
+      const deckUrlJSON = JSON.stringify(deckUrl);
+      const channelJSON = JSON.stringify(channelName);
+      const themeJSON = JSON.stringify(currentTheme || '');
+      const storageKey = 'html-ppt-presenter:' + location.pathname;
+
+      // Build the document as a single template string for clarity
+      return `<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="utf-8">
+<title>Presenter View</title>
+<style>
+  * { margin: 0; padding: 0; box-sizing: border-box; }
+  html, body {
+    width: 100%; height: 100%; overflow: hidden;
+    background: #1a1d24;
+    background-image:
+      radial-gradient(circle at 20% 30%, rgba(88,166,255,.04), transparent 50%),
+      radial-gradient(circle at 80% 70%, rgba(188,140,255,.04), transparent 50%);
+    color: #e6edf3;
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", sans-serif;
+  }
+  /* Stage: positioned area where cards live */
+  #stage { position: absolute; inset: 0; overflow: hidden; }
+
+  /* Magnetic card */
+  .pcard {
+    position: absolute;
+    background: #0d1117;
+    border: 1px solid rgba(255,255,255,.1);
+    border-radius: 12px;
+    box-shadow: 0 8px 32px rgba(0,0,0,.45), 0 0 0 1px rgba(255,255,255,.02);
+    display: flex; flex-direction: column;
+    overflow: hidden;
+    min-width: 180px; min-height: 100px;
+    transition: box-shadow .2s, border-color .2s;
+  }
+  .pcard.dragging { box-shadow: 0 16px 48px rgba(0,0,0,.6), 0 0 0 2px rgba(88,166,255,.5); border-color: #58a6ff; transition: none; z-index: 9999; }
+  .pcard.resizing { box-shadow: 0 16px 48px rgba(0,0,0,.6), 0 0 0 2px rgba(63,185,80,.5); border-color: #3fb950; transition: none; z-index: 9999; }
+  .pcard:hover { border-color: rgba(88,166,255,.3); }
+
+  /* Card header (drag handle) */
+  .pcard-head {
+    display: flex; align-items: center; gap: 10px;
+    padding: 8px 12px;
+    background: rgba(255,255,255,.04);
+    border-bottom: 1px solid rgba(255,255,255,.06);
+    cursor: move;
+    user-select: none;
+    flex-shrink: 0;
+  }
+  .pcard-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dot-color, #58a6ff); flex-shrink: 0; }
+  .pcard-title {
+    font-size: 11px; letter-spacing: .15em; text-transform: uppercase;
+    font-weight: 700; color: #8b949e; flex: 1;
+  }
+  .pcard-meta { font-size: 11px; color: #6e7681; }
+
+  /* Card body */
+  .pcard-body { flex: 1; position: relative; overflow: hidden; min-height: 0; }
+
+  /* Preview cards (CURRENT/NEXT) — iframe-based pixel-perfect render */
+  .pcard-preview .pcard-body { background: #000; }
+  .pcard-preview iframe {
+    position: absolute; top: 0; left: 0;
+    width: 1920px; height: 1080px;
+    border: none;
+    transform-origin: top left;
+    pointer-events: none;
+    background: transparent;
+  }
+  .pcard-preview .preview-end {
+    position: absolute; inset: 0;
+    display: flex; align-items: center; justify-content: center;
+    color: #484f58; font-size: 14px; letter-spacing: .12em;
+  }
+
+  /* Notes card */
+  .pcard-notes .pcard-body {
+    padding: 14px 18px;
+    overflow-y: auto;
+    font-size: 18px; line-height: 1.75;
+    color: #d0d7de;
+    font-family: "Noto Sans SC", -apple-system, sans-serif;
+  }
+  .pcard-notes .pcard-body p { margin: 0 0 .7em 0; }
+  .pcard-notes .pcard-body strong { color: #f0883e; }
+  .pcard-notes .pcard-body em { color: #58a6ff; font-style: normal; }
+  .pcard-notes .pcard-body code {
+    font-family: "SF Mono", monospace; font-size: .9em;
+    background: rgba(255,255,255,.08); padding: 1px 6px; border-radius: 4px;
+  }
+  .pcard-notes .empty { color: #484f58; font-style: italic; }
+
+  /* Timer card */
+  .pcard-timer .pcard-body {
+    display: flex; flex-direction: column; gap: 14px;
+    padding: 18px 20px; justify-content: center;
+  }
+  .timer-display {
+    font-family: "SF Mono", "JetBrains Mono", monospace;
+    font-size: 42px; font-weight: 700;
+    color: #3fb950;
+    letter-spacing: .04em;
+    line-height: 1;
+  }
+  .timer-row {
+    display: flex; align-items: center; gap: 12px;
+    font-size: 14px; color: #8b949e;
+  }
+  .timer-row .label { font-size: 10px; letter-spacing: .15em; text-transform: uppercase; color: #6e7681; }
+  .timer-row .val { color: #e6edf3; font-weight: 600; font-family: "SF Mono", monospace; }
+  .timer-controls { display: flex; gap: 8px; flex-wrap: wrap; }
+  .timer-btn {
+    background: rgba(255,255,255,.06);
+    border: 1px solid rgba(255,255,255,.1);
+    color: #e6edf3;
+    padding: 6px 12px;
+    border-radius: 6px;
+    font-size: 12px;
+    cursor: pointer;
+    font-family: inherit;
+  }
+  .timer-btn:hover { background: rgba(88,166,255,.15); border-color: #58a6ff; }
+  .timer-btn:active { transform: translateY(1px); }
+
+  /* Resize handle */
+  .pcard-resize {
+    position: absolute; right: 0; bottom: 0;
+    width: 18px; height: 18px;
+    cursor: nwse-resize;
+    background: linear-gradient(135deg, transparent 50%, rgba(255,255,255,.25) 50%, rgba(255,255,255,.25) 60%, transparent 60%, transparent 70%, rgba(255,255,255,.25) 70%, rgba(255,255,255,.25) 80%, transparent 80%);
+    z-index: 5;
+  }
+  .pcard-resize:hover { background: linear-gradient(135deg, transparent 50%, #58a6ff 50%, #58a6ff 60%, transparent 60%, transparent 70%, #58a6ff 70%, #58a6ff 80%, transparent 80%); }
+
+  /* Bottom hint bar */
+  .hint-bar {
+    position: fixed; bottom: 0; left: 0; right: 0;
+    background: rgba(0,0,0,.6);
+    backdrop-filter: blur(10px);
+    border-top: 1px solid rgba(255,255,255,.08);
+    padding: 6px 16px;
+    font-size: 11px; color: #8b949e;
+    display: flex; gap: 18px; align-items: center;
+    z-index: 1000;
+  }
+  .hint-bar kbd {
+    background: rgba(255,255,255,.08);
+    padding: 1px 6px; border-radius: 3px;
+    font-family: "SF Mono", monospace;
+    font-size: 10px;
+    border: 1px solid rgba(255,255,255,.1);
+    color: #e6edf3;
+  }
+  .hint-bar .reset-layout {
+    margin-left: auto;
+    background: transparent; border: 1px solid rgba(255,255,255,.15);
+    color: #8b949e; padding: 3px 10px; border-radius: 4px;
+    font-size: 11px; cursor: pointer; font-family: inherit;
+  }
+  .hint-bar .reset-layout:hover { background: rgba(248,81,73,.15); border-color: #f85149; color: #f85149; }
+
+  body.is-dragging-card * { user-select: none !important; }
+  body.is-dragging-card iframe { pointer-events: none !important; }
+</style>
+</head>
+<body>
+
+<div id="stage">
+  <div class="pcard pcard-preview" id="card-cur" style="--dot-color:#58a6ff">
+    <div class="pcard-head" data-drag>
+      <span class="pcard-dot"></span>
+      <span class="pcard-title">CURRENT</span>
+      <span class="pcard-meta" id="cur-meta">—</span>
+    </div>
+    <div class="pcard-body"><iframe id="iframe-cur"></iframe></div>
+    <div class="pcard-resize" data-resize></div>
+  </div>
+
+  <div class="pcard pcard-preview" id="card-nxt" style="--dot-color:#bc8cff">
+    <div class="pcard-head" data-drag>
+      <span class="pcard-dot"></span>
+      <span class="pcard-title">NEXT</span>
+      <span class="pcard-meta" id="nxt-meta">—</span>
+    </div>
+    <div class="pcard-body"><iframe id="iframe-nxt"></iframe></div>
+    <div class="pcard-resize" data-resize></div>
+  </div>
+
+  <div class="pcard pcard-notes" id="card-notes" style="--dot-color:#f0883e">
+    <div class="pcard-head" data-drag>
+      <span class="pcard-dot"></span>
+      <span class="pcard-title">SPEAKER SCRIPT · 逐字稿</span>
+    </div>
+    <div class="pcard-body" id="notes-body"></div>
+    <div class="pcard-resize" data-resize></div>
+  </div>
+
+  <div class="pcard pcard-timer" id="card-timer" style="--dot-color:#3fb950">
+    <div class="pcard-head" data-drag>
+      <span class="pcard-dot"></span>
+      <span class="pcard-title">TIMER</span>
+    </div>
+    <div class="pcard-body">
+      <div class="timer-display" id="timer-display">00:00</div>
+      <div class="timer-row">
+        <span class="label">Slide</span>
+        <span class="val" id="timer-count">1 / ${total}</span>
+      </div>
+      <div class="timer-controls">
+        <button class="timer-btn" id="btn-prev">← Prev</button>
+        <button class="timer-btn" id="btn-next">Next →</button>
+        <button class="timer-btn" id="btn-reset">⏱ Reset</button>
+      </div>
+    </div>
+    <div class="pcard-resize" data-resize></div>
+  </div>
+</div>
+
+<div class="hint-bar">
+  <span><kbd>← →</kbd> 翻页</span>
+  <span><kbd>R</kbd> 重置计时</span>
+  <span><kbd>Esc</kbd> 关闭</span>
+  <span style="color:#6e7681">拖动卡片头部移动 · 拖动右下角调整大小</span>
+  <button class="reset-layout" id="reset-layout">重置布局</button>
+</div>
+
+<script>
+(function(){
+  var slideMeta = ${metaJSON};
+  var total = ${total};
+  var idx = ${startIdx};
+  var deckUrl = ${deckUrlJSON};
+  var STORAGE_KEY = ${JSON.stringify(storageKey)};
+  var bc;
+  try { bc = new BroadcastChannel(${channelJSON}); } catch(e) {}
+
+  var iframeCur = document.getElementById('iframe-cur');
+  var iframeNxt = document.getElementById('iframe-nxt');
+  var notesBody = document.getElementById('notes-body');
+  var curMeta = document.getElementById('cur-meta');
+  var nxtMeta = document.getElementById('nxt-meta');
+  var timerDisplay = document.getElementById('timer-display');
+  var timerCount = document.getElementById('timer-count');
+
+  /* ===== Default card layout ===== */
+  function defaultLayout() {
+    var w = window.innerWidth;
+    var h = window.innerHeight - 36; /* leave room for hint bar */
+    return {
+      'card-cur':   { x: 16,        y: 16,            w: Math.round(w*0.55) - 24, h: Math.round(h*0.62) - 16 },
+      'card-nxt':   { x: Math.round(w*0.55) + 8, y: 16, w: w - Math.round(w*0.55) - 24, h: Math.round(h*0.42) - 16 },
+      'card-notes': { x: Math.round(w*0.55) + 8, y: Math.round(h*0.42) + 8, w: w - Math.round(w*0.55) - 24, h: h - Math.round(h*0.42) - 16 },
+      'card-timer': { x: 16,        y: Math.round(h*0.62) + 8, w: Math.round(w*0.55) - 24, h: h - Math.round(h*0.62) - 16 }
+    };
+  }
+
+  /* ===== Apply / save / restore layout ===== */
+  function applyLayout(layout) {
+    Object.keys(layout).forEach(function(id){
+      var el = document.getElementById(id);
+      var l = layout[id];
+      if (el && l) {
+        el.style.left = l.x + 'px';
+        el.style.top = l.y + 'px';
+        el.style.width = l.w + 'px';
+        el.style.height = l.h + 'px';
+      }
+    });
+    rescaleAll();
+  }
+  function readLayout() {
+    try {
+      var saved = localStorage.getItem(STORAGE_KEY);
+      if (saved) return JSON.parse(saved);
+    } catch(e) {}
+    return defaultLayout();
+  }
+  function saveLayout() {
+    var layout = {};
+    ['card-cur','card-nxt','card-notes','card-timer'].forEach(function(id){
+      var el = document.getElementById(id);
+      if (el) {
+        layout[id] = {
+          x: parseInt(el.style.left,10) || 0,
+          y: parseInt(el.style.top,10) || 0,
+          w: parseInt(el.style.width,10) || 300,
+          h: parseInt(el.style.height,10) || 200
+        };
+      }
+    });
+    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(layout)); } catch(e) {}
+  }
+
+  /* ===== iframe rescale to fit card body ===== */
+  function rescaleIframe(iframe) {
+    if (!iframe || iframe.style.display === 'none') return;
+    var body = iframe.parentElement;
+    var cw = body.clientWidth, ch = body.clientHeight;
+    if (!cw || !ch) return;
+    var s = Math.min(cw / 1920, ch / 1080);
+    iframe.style.transform = 'scale(' + s + ')';
+    /* Center the scaled iframe in the body */
+    var sw = 1920 * s, sh = 1080 * s;
+    iframe.style.left = Math.max(0, (cw - sw) / 2) + 'px';
+    iframe.style.top = Math.max(0, (ch - sh) / 2) + 'px';
+  }
+  function rescaleAll() {
+    rescaleIframe(iframeCur);
+    rescaleIframe(iframeNxt);
+  }
+  window.addEventListener('resize', rescaleAll);
+
+  /* ===== Drag (move card by header) ===== */
+  document.querySelectorAll('[data-drag]').forEach(function(handle){
+    handle.addEventListener('mousedown', function(e){
+      if (e.button !== 0) return;
+      var card = handle.closest('.pcard');
+      if (!card) return;
+      e.preventDefault();
+      card.classList.add('dragging');
+      document.body.classList.add('is-dragging-card');
+      var startX = e.clientX, startY = e.clientY;
+      var startL = parseInt(card.style.left,10) || 0;
+      var startT = parseInt(card.style.top,10)  || 0;
+      function onMove(ev){
+        var nx = Math.max(0, Math.min(window.innerWidth - 100, startL + ev.clientX - startX));
+        var ny = Math.max(0, Math.min(window.innerHeight - 50, startT + ev.clientY - startY));
+        card.style.left = nx + 'px';
+        card.style.top = ny + 'px';
+      }
+      function onUp(){
+        card.classList.remove('dragging');
+        document.body.classList.remove('is-dragging-card');
+        document.removeEventListener('mousemove', onMove);
+        document.removeEventListener('mouseup', onUp);
+        saveLayout();
+      }
+      document.addEventListener('mousemove', onMove);
+      document.addEventListener('mouseup', onUp);
+    });
+  });
+
+  /* ===== Resize (drag bottom-right corner) ===== */
+  document.querySelectorAll('[data-resize]').forEach(function(handle){
+    handle.addEventListener('mousedown', function(e){
+      if (e.button !== 0) return;
+      var card = handle.closest('.pcard');
+      if (!card) return;
+      e.preventDefault(); e.stopPropagation();
+      card.classList.add('resizing');
+      document.body.classList.add('is-dragging-card');
+      var startX = e.clientX, startY = e.clientY;
+      var startW = parseInt(card.style.width,10)  || card.offsetWidth;
+      var startH = parseInt(card.style.height,10) || card.offsetHeight;
+      function onMove(ev){
+        var nw = Math.max(180, startW + ev.clientX - startX);
+        var nh = Math.max(100, startH + ev.clientY - startY);
+        card.style.width = nw + 'px';
+        card.style.height = nh + 'px';
+        if (card.querySelector('iframe')) rescaleIframe(card.querySelector('iframe'));
+      }
+      function onUp(){
+        card.classList.remove('resizing');
+        document.body.classList.remove('is-dragging-card');
+        document.removeEventListener('mousemove', onMove);
+        document.removeEventListener('mouseup', onUp);
+        rescaleAll();
+        saveLayout();
+      }
+      document.addEventListener('mousemove', onMove);
+      document.addEventListener('mouseup', onUp);
+    });
+  });
+
+  /* ===== Preview iframe ready tracking =====
+   * Each iframe loads the deck ONCE with ?preview=1 on init. Subsequent
+   * slide changes are sent via postMessage('preview-goto') so the iframe
+   * just toggles visibility of a different .slide — no reload, no flicker.
+   */
+  var iframeReady = { cur: false, nxt: false };
+  var currentTheme = ${themeJSON};
+  window.addEventListener('message', function(e) {
+    if (!e.data || e.data.type !== 'preview-ready') return;
+    var iframe = null;
+    if (e.source === iframeCur.contentWindow) {
+      iframeReady.cur = true;
+      iframe = iframeCur;
+      postPreviewGoto(iframeCur, idx);
+    } else if (e.source === iframeNxt.contentWindow) {
+      iframeReady.nxt = true;
+      iframe = iframeNxt;
+      postPreviewGoto(iframeNxt, idx + 1 < total ? idx + 1 : idx);
+    }
+    /* Sync current theme to the iframe */
+    if (iframe && currentTheme) {
+      try { iframe.contentWindow.postMessage({ type: 'preview-theme', name: currentTheme }, '*'); } catch(err) {}
+    }
+    if (iframe) rescaleIframe(iframe);
+  });
+
+  function postPreviewGoto(iframe, n) {
+    try {
+      iframe.contentWindow.postMessage({ type: 'preview-goto', idx: n }, '*');
+    } catch(e) {}
+  }
+
+  /* ===== Update content =====
+   * Smooth (no-reload) navigation: send postMessage to iframes instead of
+   * resetting src. Iframes stay loaded, just switch visible .slide.
+   */
+  function update(n) {
+    n = Math.max(0, Math.min(total - 1, n));
+    idx = n;
+
+    /* Current preview — postMessage (smooth) */
+    if (iframeReady.cur) postPreviewGoto(iframeCur, n);
+    curMeta.textContent = (n + 1) + '/' + total;
+
+    /* Next preview */
+    if (n + 1 < total) {
+      iframeNxt.style.display = '';
+      var endEl = document.querySelector('#card-nxt .preview-end');
+      if (endEl) endEl.remove();
+      if (iframeReady.nxt) postPreviewGoto(iframeNxt, n + 1);
+      nxtMeta.textContent = (n + 2) + '/' + total;
+    } else {
+      iframeNxt.style.display = 'none';
+      var body = document.querySelector('#card-nxt .pcard-body');
+      if (body && !body.querySelector('.preview-end')) {
+        var end = document.createElement('div');
+        end.className = 'preview-end';
+        end.textContent = '— END OF DECK —';
+        body.appendChild(end);
+      }
+      nxtMeta.textContent = 'END';
+    }
+
+    /* Notes */
+    var note = slideMeta[n].notes;
+    notesBody.innerHTML = note || '<span class="empty">(这一页还没有逐字稿)</span>';
+
+    /* Timer count */
+    timerCount.textContent = (n + 1) + ' / ' + total;
+  }
+
+  /* ===== Timer ===== */
+  var tStart = Date.now();
+  setInterval(function(){
+    var s = Math.floor((Date.now() - tStart) / 1000);
+    var mm = String(Math.floor(s/60)).padStart(2,'0');
+    var ss = String(s%60).padStart(2,'0');
+    timerDisplay.textContent = mm + ':' + ss;
+  }, 1000);
+  function resetTimer(){ tStart = Date.now(); timerDisplay.textContent = '00:00'; }
+
+  /* ===== BroadcastChannel sync ===== */
+  if (bc) {
+    bc.onmessage = function(e){
+      if (!e.data) return;
+      if (e.data.type === 'go') update(e.data.idx);
+      else if (e.data.type === 'theme' && e.data.name) {
+        currentTheme = e.data.name;
+        /* Forward theme change to preview iframes */
+        [iframeCur, iframeNxt].forEach(function(iframe){
+          try {
+            iframe.contentWindow.postMessage({ type: 'preview-theme', name: e.data.name }, '*');
+          } catch(err) {}
+        });
+      }
+    };
+  }
+  function go(n) {
+    update(n);
+    if (bc) bc.postMessage({ type: 'go', idx: idx });
+  }
+
+  /* ===== Buttons ===== */
+  document.getElementById('btn-prev').addEventListener('click', function(){ go(idx - 1); });
+  document.getElementById('btn-next').addEventListener('click', function(){ go(idx + 1); });
+  document.getElementById('btn-reset').addEventListener('click', resetTimer);
+  document.getElementById('reset-layout').addEventListener('click', function(){
+    if (confirm('恢复默认卡片布局?')) {
+      try { localStorage.removeItem(STORAGE_KEY); } catch(e){}
+      applyLayout(defaultLayout());
+    }
+  });
+
+  /* ===== Keyboard ===== */
+  document.addEventListener('keydown', function(e){
+    if (e.metaKey || e.ctrlKey || e.altKey) return;
+    switch(e.key) {
+      case 'ArrowRight': case ' ': case 'PageDown': go(idx + 1); e.preventDefault(); break;
+      case 'ArrowLeft':  case 'PageUp':   go(idx - 1); e.preventDefault(); break;
+      case 'Home': go(0); break;
+      case 'End':  go(total - 1); break;
+      case 'r': case 'R': resetTimer(); break;
+      case 'Escape': window.close(); break;
+    }
+  });
+
+  /* ===== Iframe load → rescale (catches initial size) ===== */
+  iframeCur.addEventListener('load', function(){ rescaleIframe(iframeCur); });
+  iframeNxt.addEventListener('load', function(){ rescaleIframe(iframeNxt); });
+
+  /* ===== Init =====
+   * Load each iframe ONCE with the deck file. After they post
+   * 'preview-ready', all subsequent navigation is via postMessage
+   * (smooth, no reload, no flicker).
+   */
+  applyLayout(readLayout());
+  iframeCur.src = deckUrl + '?preview=' + (idx + 1);
+  if (idx + 1 < total) iframeNxt.src = deckUrl + '?preview=' + (idx + 2);
+  /* Initialize notes/timer/count without touching iframes */
+  notesBody.innerHTML = slideMeta[idx].notes || '<span class="empty">(这一页还没有逐字稿)</span>';
+  curMeta.textContent = (idx + 1) + '/' + total;
+  nxtMeta.textContent = (idx + 2) + '/' + total;
+  timerCount.textContent = (idx + 1) + ' / ' + total;
+})();
+</` + `script>
+</body></html>`;
+    }
+
+    function fullscreen(){ const el=document.documentElement;
+      if (!document.fullscreenElement) el.requestFullscreen&&el.requestFullscreen();
+      else document.exitFullscreen&&document.exitFullscreen();
+    }
+
+    // theme cycling
+    const root = document.documentElement;
+    const themesAttr = root.getAttribute('data-themes') || document.body.getAttribute('data-themes');
+    const themes = themesAttr ? themesAttr.split(',').map(s=>s.trim()).filter(Boolean) : [];
+    let themeIdx = 0;
+
+    // Auto-detect theme base path from existing <link id="theme-link">
+    let themeBase = root.getAttribute('data-theme-base');
+    if (!themeBase) {
+      const existingLink = document.getElementById('theme-link');
+      if (existingLink) {
+        // el.getAttribute('href') gives the raw relative path written in HTML
+        const rawHref = existingLink.getAttribute('href') || '';
+        const lastSlash = rawHref.lastIndexOf('/');
+        themeBase = lastSlash >= 0 ? rawHref.substring(0, lastSlash + 1) : 'assets/themes/';
+      } else {
+        themeBase = 'assets/themes/';
+      }
+    }
+
+    function applyTheme(name) {
+      let link = document.getElementById('theme-link');
+      if (!link) {
+        link = document.createElement('link');
+        link.rel = 'stylesheet';
+        link.id = 'theme-link';
+        document.head.appendChild(link);
+      }
+      link.href = themeBase + name + '.css';
+      root.setAttribute('data-theme', name);
+      const ind = document.querySelector('.theme-indicator');
+      if (ind) ind.textContent = name;
+    }
+    function cycleTheme(fromRemote){
+      if (!themes.length) return;
+      themeIdx = (themeIdx+1) % themes.length;
+      const name = themes[themeIdx];
+      applyTheme(name);
+      /* Broadcast to other window (audience ↔ presenter) */
+      if (!fromRemote && bc) bc.postMessage({ type: 'theme', name: name });
+    }
+
+    // animation cycling on current slide
+    let animIdx = 0;
+    function cycleAnim(){
+      animIdx = (animIdx+1) % ANIMS.length;
+      const a = ANIMS[animIdx];
+      const target = slides[idx].querySelector('[data-anim-target]') || slides[idx];
+      ANIMS.forEach(x => target.classList.remove('anim-'+x));
+      void target.offsetWidth;
+      target.classList.add('anim-'+a);
+      target.setAttribute('data-anim', a);
+      const ind = document.querySelector('.anim-indicator');
+      if (ind) ind.textContent = a;
+    }
+
+    document.addEventListener('keydown', function (e) {
+      if (e.metaKey||e.ctrlKey||e.altKey) return;
+      switch (e.key) {
+        case 'ArrowRight': case ' ': case 'PageDown': case 'Enter': go(idx+1); e.preventDefault(); break;
+        case 'ArrowLeft': case 'PageUp': case 'Backspace': go(idx-1); e.preventDefault(); break;
+        case 'Home': go(0); break;
+        case 'End': go(total-1); break;
+        case 'f': case 'F': fullscreen(); break;
+        case 's': case 'S': openPresenterWindow(); break;
+        case 'n': case 'N': toggleNotes(); break;
+        case 'o': case 'O': toggleOverview(); break;
+        case 't': case 'T': cycleTheme(); break;
+        case 'a': case 'A': cycleAnim(); break;
+        case 'Escape': toggleOverview(false); toggleNotes(false); break;
+      }
+    });
+
+    // hash deep-link
+    function fromHash(){
+      const m = /^#\/(\d+)/.exec(location.hash||'');
+      if (m) go(Math.max(0, parseInt(m[1],10)-1));
+    }
+    window.addEventListener('hashchange', fromHash);
+    fromHash();
+    go(idx);
+  });
+})();

+ 663 - 0
docs/ppt-cfc-overview/index.html

@@ -0,0 +1,663 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>浠艾福 · 项目总体介绍 · 四年路线规划</title>
+<link rel="stylesheet" href="assets/fonts.css">
+<link rel="stylesheet" href="assets/base.css">
+<link rel="stylesheet" href="assets/animations/animations.css">
+<link rel="stylesheet" href="style.css">
+</head>
+<body class="tpl-pitch-deck tpl-cfc-overview">
+<div class="deck">
+
+  <!-- ════════════════════════════════════════════════ -->
+  <!-- 1. COVER -->
+  <!-- ════════════════════════════════════════════════ -->
+  <section class="slide" data-title="封面">
+    <div class="cover-bg"></div>
+    <div class="cover-blob"></div>
+    <div style="position:absolute;top:56px;left:112px">
+      <span class="brand-dot"></span>
+      <span class="brand">浠艾福 <span style="font-weight:400;color:var(--text-2)">Care Family</span></span>
+    </div>
+    <p class="kicker anim-fade-up" data-anim="fade-up">亿图腾(北京)科技有限公司 · 2026</p>
+    <h1 class="h1 anim-fade-up" data-anim="fade-up" style="animation-delay:.15s">
+      家庭健康成长平台<br>
+      <span class="gradient-text">项目总体介绍</span>
+    </h1>
+    <p class="lede mt-m anim-fade-up" data-anim="fade-up" style="animation-delay:.3s">
+      身 · 心 · 智 · 行 · 富 — 五维全景幸福矩阵<br>
+      连接家长、孩子、成长规划师的三方平台
+    </p>
+    <div class="row mt-l wrap anim-fade-up" data-anim="fade-up" style="animation-delay:.45s">
+      <span class="pill pill-accent">五维能量系统</span>
+      <span class="pill pill-accent">AI 家庭助手</span>
+      <span class="pill pill-accent">三端联动</span>
+      <span class="pill pill-accent">任务/心愿闭环</span>
+      <span class="pill pill-accent">精准营养</span>
+    </div>
+    <div class="deck-footer"><span>浠艾福 · 内部演示</span><span class="slide-number" data-current="1" data-total="16"></span></div>
+  </section>
+
+  <!-- ════════════════════════════════════════════════ -->
+  <!-- 2. 目录 -->
+  <!-- ════════════════════════════════════════════════ -->
+  <section class="slide tc" data-title="目录">
+    <p class="kicker">CONTENTS</p>
+    <h2 class="h2 mt-s">目录</h2>
+    <div class="grid g2 mt-l toc-grid">
+      <div class="card tc card-hover">
+        <div class="toc-num">01</div>
+        <h4>项目概述</h4>
+        <p class="dim">品牌理念 · 产品矩阵 · 技术架构</p>
+        <div class="toc-dot"></div>
+      </div>
+      <div class="card tc card-hover">
+        <div class="toc-num">02</div>
+        <h4>当前成果</h4>
+        <p class="dim">已上线功能 · 核心数据 · 用户场景</p>
+        <div class="toc-dot"></div>
+      </div>
+      <div class="card tc card-hover">
+        <div class="toc-num">03</div>
+        <h4>四年路线规划</h4>
+        <p class="dim">Phase 0~3 · 三年愿景</p>
+        <div class="toc-dot"></div>
+      </div>
+      <div class="card tc card-hover">
+        <div class="toc-num">04</div>
+        <h4>商业模式与壁垒</h4>
+        <p class="dim">三方平台经济 · 数据护城河</p>
+        <div class="toc-dot"></div>
+      </div>
+    </div>
+  </section>
+
+  <!-- ════════════════════════════════════════════════ -->
+  <!-- 3. section: 项目概述 -->
+  <!-- ════════════════════════════════════════════════ -->
+  <section class="slide" data-title="品牌理念">
+    <span class="section-num">01</span>
+    <p class="num-tag">PROJECT OVERVIEW</p>
+    <h2 class="h2 mt-s">五维全景幸福矩阵</h2>
+    <div class="grid g3 mt-l wuxing-grid">
+      <div class="card card-wuxing" style="--wx-color:#8D6E63">
+        <div class="wx-icon">🌏</div>
+        <h4>身 · 身泰</h4>
+        <p class="dim">主动健康,活得长久<br>好水与精准营养 · 体质调理 · 健康打卡</p>
+      </div>
+      <div class="card card-wuxing" style="--wx-color:#FF6B35">
+        <div class="wx-icon">🔥</div>
+        <h4>心 · 心怡</h4>
+        <p class="dim">情绪管理,内心丰盈<br>DAN 测评 · 心理报告 · 亲子沟通</p>
+      </div>
+      <div class="card card-wuxing" style="--wx-color:#FFD700">
+        <div class="wx-icon">⚔️</div>
+        <h4>智 · 智达</h4>
+        <p class="dim">认知提升,终身学习<br>认知测评 · 小游戏训练 · 舒尔特/数独</p>
+      </div>
+      <div class="card card-wuxing" style="--wx-color:#4CAF50">
+        <div class="wx-icon">🌿</div>
+        <h4>行 · 行远</h4>
+        <p class="dim">行动力,习惯养成<br>任务系统 · 积分激励 · 行为打卡</p>
+      </div>
+      <div class="card card-wuxing" style="--wx-color:#42A5F5">
+        <div class="wx-icon">💧</div>
+        <h4>富 · 富沛</h4>
+        <p class="dim">财商素养,价值创造<br>财富打卡 · 保单管理 · 创客成长</p>
+      </div>
+      <div class="card card-wuxing-glow">
+        <div class="wx-icon">✦</div>
+        <h4>五行相生</h4>
+        <p class="dim">智(金)→富(水)→行(木)→心(火)→身(土)<br>25+ 数据源 · 日上限 200 能量</p>
+      </div>
+    </div>
+  </section>
+
+  <!-- 4. 产品矩阵 -->
+  <section class="slide" data-title="产品矩阵">
+    <span class="section-num">01</span>
+    <p class="num-tag">PRODUCT MATRIX</p>
+    <h2 class="h2 mt-s">三端联动产品矩阵</h2>
+    <div class="grid g3 mt-l">
+      <div class="card card-accent">
+        <h4>📱 家长端小程序</h4>
+        <ul class="dim ul-sm">
+          <li>双角色系统(家长/孩子一键切换)</li>
+          <li>任务管理:创建→审核→全流程</li>
+          <li>测评预约:规划师→快照→支付→报告</li>
+          <li>套餐市场、家庭能量看板</li>
+        </ul>
+      </div>
+      <div class="card" style="border-top-color:#FF6B35">
+        <h4>🎮 孩子端小程序</h4>
+        <ul class="dim ul-sm">
+          <li>儿童友好 Claymorphism 设计</li>
+          <li>任务执行 + 小游戏(舒尔特/数独/猜数字)</li>
+          <li>心愿兑换:积分消耗→家长审批</li>
+          <li>成长报告:五维能量趋势</li>
+        </ul>
+      </div>
+      <div class="card" style="border-top-color:#4CAF50">
+        <h4>🖥️ Web 管理后台</h4>
+        <ul class="dim ul-sm">
+          <li>运营管理:用户/家庭/系统配置</li>
+          <li>服务商管理:入驻/套餐/订单</li>
+          <li>数据统计:增长分析 + 看板</li>
+          <li>四级地址管理(省市区街道)</li>
+        </ul>
+      </div>
+    </div>
+    <div class="row mt-l" style="gap:12px">
+      <span class="pill pill-accent">50+ 小程序页面</span>
+      <span class="pill pill-accent">4 个 TabBar</span>
+      <span class="pill pill-accent">29 个管理端页面</span>
+      <span class="pill pill-accent">48 个后端控制器</span>
+    </div>
+  </section>
+
+  <!-- 5. 技术架构 -->
+  <section class="slide" data-title="技术架构">
+    <span class="section-num">01</span>
+    <p class="num-tag">TECH ARCHITECTURE</p>
+    <h2 class="h2 mt-s">生产级技术架构</h2>
+    <div class="arch-row mt-l">
+      <div class="arch-tier">
+        <div class="arch-label">前端</div>
+        <div class="arch-box"><strong>微信小程序</strong><br><span class="dim">uni-app Vue 2</span></div>
+        <div class="arch-box"><strong>Web 管理端</strong><br><span class="dim">Vue 2 + Element UI</span></div>
+      </div>
+      <div class="arch-arrow">→</div>
+      <div class="arch-tier">
+        <div class="arch-label">网关</div>
+        <div class="arch-box"><strong>JWT 认证</strong><br><span class="dim">Bearer Token · 8 公开路径</span></div>
+      </div>
+      <div class="arch-arrow">→</div>
+      <div class="arch-tier">
+        <div class="arch-label">后端</div>
+        <div class="arch-box"><strong>Spring Boot 2.7.18</strong><br><span class="dim">Java 8 · 48 控制器</span></div>
+        <div class="arch-box"><strong>MyBatis-Plus</strong><br><span class="dim">47 实体 · 15 模块</span></div>
+      </div>
+      <div class="arch-arrow">→</div>
+      <div class="arch-tier">
+        <div class="arch-label">数据</div>
+        <div class="arch-box"><strong>MySQL 8.0</strong><br><span class="dim">192.168.16.251:3306</span></div>
+        <div class="arch-box"><strong>多数据源</strong><br><span class="dim">cfc 主库 + sfms 只读</span></div>
+      </div>
+    </div>
+    <div class="row mt-l wrap">
+      <span class="pill pill-accent">统一 @PostMapping</span>
+      <span class="pill pill-accent">Result&lt;T&gt; 响应</span>
+      <span class="pill pill-accent">JWT 拦截</span>
+      <span class="pill pill-accent">设计 Token 体系</span>
+      <span class="pill pill-accent">sfms 数据迁移完成</span>
+    </div>
+  </section>
+
+  <!-- ════════════════════════════════════════════════ -->
+  <!-- 6. section: 当前成果 -->
+  <!-- ════════════════════════════════════════════════ -->
+  <section class="slide" data-title="已上线功能">
+    <span class="section-num">02</span>
+    <p class="num-tag">CURRENT STATUS</p>
+    <h2 class="h2 mt-s">已上线功能模块</h2>
+    <div class="grid g4 mt-l feature-grid">
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>用户认证</h4><p class="dim">微信/手机登录 · 角色切换</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>家庭管理</h4><p class="dim">邀请码 · 绑定 · 解绑</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>任务系统</h4><p class="dim">CRUD · 审核 · 小游戏</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>心愿体系</h4><p class="dim">创建 · 审批 · 积分兑换</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>五维能量</h4><p class="dim">沙盘评分 · 账本 · 配置</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>测评闭环</h4><p class="dim">订单 · 预约 · 支付 · 结果</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>成长档案</h4><p class="dim">记录 · 计划 · 报告</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>小游戏</h4><p class="dim">舒尔特 · 猜数字 · 数独</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>AI 家庭助手</h4><p class="dim">Dify 对接 · 家庭上下文</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>商城模块</h4><p class="dim">商品 · 购物车 · 订单</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>服务商入驻</h4><p class="dim">规划师/活动方/供应商</p></div>
+      <div class="card feature-done"><div class="f-icon">✅</div><h4>数据迁移</h4><p class="dim">sfms 全量迁移完成</p></div>
+    </div>
+  </section>
+
+  <!-- 7. 核心数据 -->
+  <section class="slide" data-title="核心数据">
+    <span class="section-num">02</span>
+    <p class="num-tag">KEY METRICS</p>
+    <h2 class="h2 mt-s">核心数据概览</h2>
+    <div class="grid g4 mt-l" style="gap:32px">
+      <div class="metric"><div class="n">48</div><div class="l">后端控制器 · 15 功能模块</div></div>
+      <div class="metric"><div class="n">47</div><div class="l">数据实体 · MyBatis-Plus</div></div>
+      <div class="metric"><div class="n">50+</div><div class="l">小程序页面 · 4 TabBar</div></div>
+      <div class="metric"><div class="n">29</div><div class="l">Web 管理端页面</div></div>
+      <div class="metric"><div class="n">25+</div><div class="l">五维评分数据源</div></div>
+      <div class="metric"><div class="n">10+</div><div class="l">能量来源类型</div></div>
+      <div class="metric"><div class="n">4 级</div><div class="l">地址匹配(省市区街道)</div></div>
+      <div class="metric"><div class="n">4 类</div><div class="l">用户角色 + 多角色字段</div></div>
+    </div>
+    <div class="note-callout mt-l">
+      <span class="pill pill-accent">💡 数据资产</span>
+      <span class="dim" style="margin-left:12px">任务完成记录→努力曲线 / 五维能量→健康趋势 / 测评结果→成长档案 / 游戏记录→认知基线</span>
+    </div>
+  </section>
+
+  <!-- 8. AI 赋能 -->
+  <section class="slide" data-title="AI赋能">
+    <span class="section-num">02</span>
+    <p class="num-tag">AI CAPABILITIES</p>
+    <h2 class="h2 mt-s">AI 家庭助手</h2>
+    <div class="grid g2 mt-l">
+      <div class="card card-accent">
+        <h4>对话式家庭助手</h4>
+        <div class="ai-demo-box">
+          <div class="ai-user-msg">👤 今天家里和大宝、小宝吃饭,吃什么合适?</div>
+          <div class="ai-assistant-msg">🍽️ 根据您家的情况推荐:
+            <br>• <strong>大宝</strong>最近"智"维能量偏低(专注力训练少),建议多吃蛋黄、深海鱼、核桃——补脑又好吃
+            <br>• <strong>小宝</strong>上次体检略微缺铁,今天"身"维度的健康打卡目标还没完成,推荐猪肝菠菜粥
+            <br>• 您和爱人在平台的历史点餐记录显示偏好粤式清淡口味,排除辛辣和海鲜过敏
+            <br>→ 推荐:<strong>番茄鳕鱼粥 + 核桃拌菠菜 + 紫菜蛋花汤</strong>,全家适合</div>
+        </div>
+        <p class="dim" style="margin-top:10px;font-size:13px">系统自动读取:家庭成员档案 · 身体维度数据 · 历史菜谱 · 营养缺乏指标</p>
+      </div>
+      <div>
+        <div class="card" style="margin-bottom:12px">
+          <h4>AI 应用场景矩阵</h4>
+        </div>
+        <div class="ai-table">
+          <div class="ai-row ai-row-head"><span>场景</span><span>状态</span><span>说明</span><span>规划</span></div>
+          <div class="ai-row"><span>🍽️ 智能食谱推荐</span><span class="pill">规划中</span><span class="dim">结合成员身体情况+历史菜谱+营养缺乏给出全家食谱</span><span class="pill pill-accent">v2</span></div>
+          <div class="ai-row"><span>成长分析</span><span class="pill">已上线</span><span class="dim">读取数据回答孩子表现问题</span><span class="pill pill-accent">规划:自动周报</span></div>
+          <div class="ai-row"><span>测评解读</span><span class="pill">已上线</span><span class="dim">报告数据结构化展示</span><span class="pill pill-accent">规划:AI 自动解读</span></div>
+          <div class="ai-row"><span>智能推荐</span><span class="pill">已上线</span><span class="dim">基于维度的活动/商品推荐</span><span class="pill pill-accent">规划:个性化引擎</span></div>
+          <div class="ai-row"><span>任务生成</span><span class="pill">手动</span><span class="dim">手动创建</span><span class="pill pill-accent">规划:AI 自动生成</span></div>
+          <div class="ai-row"><span>成长规划</span><span class="pill">手动</span><span class="dim">规划师手动制定</span><span class="pill pill-accent">规划:AI 辅助方案</span></div>
+        </div>
+      </div>
+    </div>
+  </section>
+
+  <!-- ════════════════════════════════════════════════ -->
+  <!-- 9. section: 四年路线规划 — 总览 -->
+  <!-- ════════════════════════════════════════════════ -->
+  <section class="slide" data-title="路线规划总览">
+    <span class="section-num">03</span>
+    <p class="num-tag">ROADMAP</p>
+    <h2 class="h2 mt-s">四年三阶段路线规划</h2>
+    <div class="roadmap-timeline mt-l">
+      <div class="rm-track">
+        <div class="rm-phase rm-now">
+          <div class="rm-badge">当前</div>
+          <h4>Phase 0<br><span class="dim">基础建设期</span></h4>
+          <p class="rm-date">2025 H2 — 2026 H1</p>
+          <div class="rm-items">
+            <span class="pill">✅ 三端架构</span>
+            <span class="pill">✅ 五维能量</span>
+            <span class="pill">✅ 任务/心愿</span>
+            <span class="pill">✅ 测评闭环</span>
+            <span class="pill">✅ AI 助手</span>
+            <span class="pill">✅ 数据迁移</span>
+          </div>
+        </div>
+        <div class="rm-connector"><span class="rm-arrow">→</span></div>
+        <div class="rm-phase rm-next">
+          <div class="rm-badge">Q3-Q4 2026</div>
+          <h4>Phase 1<br><span class="dim">商业增强期</span></h4>
+          <p class="rm-date">2026 H2</p>
+          <div class="rm-items">
+            <span class="pill pill-accent">AI 深度化</span>
+            <span class="pill pill-accent">服务商网络</span>
+            <span class="pill pill-accent">精准营养</span>
+            <span class="pill pill-accent">会员订阅</span>
+          </div>
+        </div>
+        <div class="rm-connector"><span class="rm-arrow">→</span></div>
+        <div class="rm-phase">
+          <div class="rm-badge">2027</div>
+          <h4>Phase 2<br><span class="dim">增长规模化</span></h4>
+          <p class="rm-date">2027 全年</p>
+          <div class="rm-items">
+            <span class="pill pill-accent">分销裂变</span>
+            <span class="pill pill-accent">渠道合作</span>
+            <span class="pill pill-accent">社区互动</span>
+            <span class="pill pill-accent">广告投放</span>
+          </div>
+        </div>
+        <div class="rm-connector"><span class="rm-arrow">→</span></div>
+        <div class="rm-phase">
+          <div class="rm-badge">2028-2029</div>
+          <h4>Phase 3<br><span class="dim">生态平台化</span></h4>
+          <p class="rm-date">2028-2029</p>
+          <div class="rm-items">
+            <span class="pill pill-accent">开放平台</span>
+            <span class="pill pill-accent">行业标准</span>
+            <span class="pill pill-accent">第三方接入</span>
+            <span class="pill pill-accent">生态运营</span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="note-callout mt-l">
+      <span class="pill" style="background:var(--good);color:#fff;border:none">愿景</span>
+      <span class="dim" style="margin-left:12px">从家庭工具 → 服务平台 → 行业标准 → 生态入口</span>
+    </div>
+  </section>
+
+  <!-- 10. Phase 0 — 当前阶段 -->
+  <section class="slide" data-title="Phase 0">
+    <span class="section-num">03</span>
+    <p class="num-tag">PHASE 0 · 2025H2–2026H1</p>
+    <h2 class="h2 mt-s">基础建设期</h2>
+    <p class="lede mt-m" style="font-size:20px;max-width:80ch">完成从 0 到 1 的产品基建,建立核心功能闭环,验证三方平台模型可行性。</p>
+    <div class="grid g3 mt-l">
+      <div class="card card-accent">
+        <h4>🎯 核心目标</h4>
+        <ul class="dim ul-sm">
+          <li>三端架构完成(后端/小程序/Web)</li>
+          <li>五维能量系统上线</li>
+          <li>任务/心愿闭环打通</li>
+          <li>AI 家庭助手 MVP</li>
+          <li>sfms 全量数据迁移完成</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>📦 已交付成果</h4>
+        <ul class="dim ul-sm">
+          <li>48 控制器 · 47 实体 · 50+ 页面</li>
+          <li>15 功能模块完整上线</li>
+          <li>25+ 数据源参与五维评分</li>
+          <li>10+ 能量来源类型</li>
+          <li>4 类角色权限体系</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>🔧 技术基建</h4>
+        <ul class="dim ul-sm">
+          <li>Spring Boot 2.7.18 + MyBatis-Plus</li>
+          <li>JWT 认证 + 角色拦截</li>
+          <li>多数据源(cfc + sfms)</li>
+          <li>统一 Result&lt;T&gt; + @PostMapping</li>
+          <li>设计 Token 体系</li>
+        </ul>
+      </div>
+    </div>
+  </section>
+
+  <!-- 11. Phase 1 — 商业增强 -->
+  <section class="slide" data-title="Phase 1">
+    <span class="section-num">03</span>
+    <p class="num-tag">PHASE 1 · 2026H2</p>
+    <h2 class="h2 mt-s">商业增强期</h2>
+    <p class="lede mt-m" style="font-size:20px;max-width:80ch">在基建基础上深化 AI 能力、拓展服务商网络、启动商业化闭环。</p>
+    <div class="grid g3 mt-l">
+      <div class="card card-accent">
+        <h4>🤖 AI 深度化</h4>
+        <ul class="dim ul-sm">
+          <li>AI 自动解读测评报告,标注风险项</li>
+          <li>基于孩子能力的 AI 个性化任务生成</li>
+          <li>多维特征推荐引擎 v1</li>
+          <li>AI 周报/月报自动生成</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>🏪 服务商网络</h4>
+        <ul class="dim ul-sm">
+          <li>规划师认证体系线上化</li>
+          <li>活动方入驻工具包</li>
+          <li>批量任务下发功能</li>
+          <li>服务商等级 + 团队管理深化</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>💎 商业化启动</h4>
+        <ul class="dim ul-sm">
+          <li>会员订阅体系上线(月卡/年卡)</li>
+          <li>精准营养闭环:检测→分析→方案</li>
+          <li>活动/课程售卖功能</li>
+          <li>推广分佣系统 v1</li>
+        </ul>
+      </div>
+    </div>
+    <div class="row mt-l" style="gap:12px">
+      <span class="pill pill-accent">关键指标</span>
+      <span class="dim">注册家庭 3000+ · 规划师 50+ · AI 解读覆盖 80% 测评</span>
+    </div>
+  </section>
+
+  <!-- 12. Phase 2 — 增长规模化 -->
+  <section class="slide" data-title="Phase 2">
+    <span class="section-num">03</span>
+    <p class="num-tag">PHASE 2 · 2027 全年</p>
+    <h2 class="h2 mt-s">增长规模化</h2>
+    <p class="lede mt-m" style="font-size:20px;max-width:80ch">以社交裂变 + 渠道合作驱动用户增长,实现商业模型规模化验证。</p>
+    <div class="grid g3 mt-l">
+      <div class="card card-accent">
+        <h4>📈 用户增长</h4>
+        <ul class="dim ul-sm">
+          <li>社交裂变:邀请奖励 + 任务拼团</li>
+          <li>渠道合作:家校/社区/教培机构</li>
+          <li>广告投放:微信广告+搜索优化</li>
+          <li>K-factor 目标 &gt; 1.2</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>🔄 飞轮加速</h4>
+        <ul class="dim ul-sm">
+          <li>更多家长 → 吸引更多规划师</li>
+          <li>更多规划师 → 更优质服务</li>
+          <li>更优质服务 → 更多家庭</li>
+          <li>CAC &lt; 20 元 · LTV/CAC &gt; 12x</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>🏗️ 能力扩展</h4>
+        <ul class="dim ul-sm">
+          <li>社区互动功能(主题/问答/圈子)</li>
+          <li>家庭社交网络(组队/排行榜)</li>
+          <li>活动/课程批量录入管理</li>
+          <li>月交易额目标 30 万+</li>
+        </ul>
+      </div>
+    </div>
+    <div class="note-callout mt-l">
+      <span class="pill" style="background:var(--good);color:#fff;border:none">里程碑</span>
+      <span class="dim" style="margin-left:12px">注册家庭 10,000+ · 月活跃率 &gt;40% · 商业化收入覆盖运营成本</span>
+    </div>
+  </section>
+
+  <!-- 13. Phase 3 — 生态平台化 -->
+  <section class="slide" data-title="Phase 3">
+    <span class="section-num">03</span>
+    <p class="num-tag">PHASE 3 · 2028–2029</p>
+    <h2 class="h2 mt-s">生态平台化</h2>
+    <p class="lede mt-m" style="font-size:20px;max-width:80ch">从工具型平台升级为生态型平台,建立行业标准,构建家庭服务超级入口。</p>
+    <div class="grid g3 mt-l">
+      <div class="card card-accent">
+        <h4>🏛️ 行业标准</h4>
+        <ul class="dim ul-sm">
+          <li>"五维健康"评价体系行业标准</li>
+          <li>家庭健康指数权威认证</li>
+          <li>成长规划师认证行业规范</li>
+          <li>家庭教育领域权威数据平台</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>🔗 开放平台</h4>
+        <ul class="dim ul-sm">
+          <li>Open API 第三方服务接入</li>
+          <li>服务商自助入驻工具</li>
+          <li>数据开放接口(脱敏)</li>
+          <li>插件/扩展市场</li>
+        </ul>
+      </div>
+      <div class="card">
+        <h4>🌐 生态运营</h4>
+        <ul class="dim ul-sm">
+          <li>跨平台内容/服务分发</li>
+          <li>第三方生态服务交易</li>
+          <li>家庭成长大数据分析</li>
+          <li>覆盖 10 万+ 家庭</li>
+        </ul>
+      </div>
+    </div>
+  </section>
+
+  <!-- 14. 三年愿景总览 -->
+  <section class="slide" data-title="三年愿景">
+    <span class="section-num">03</span>
+    <p class="num-tag">3-YEAR VISION</p>
+    <h2 class="h2 mt-s">三年发展愿景</h2>
+    <div class="vision-track mt-l">
+      <div class="vision-node">
+        <div class="vision-year">2027</div>
+        <div class="vision-card card">
+          <h4>国内领先</h4>
+          <p class="dim">成为国内领先的家庭健康成长平台<br>覆盖 <strong>10 万+</strong> 家庭 · 服务商 <strong>500+</strong></p>
+          <div class="vision-icon">🚀</div>
+        </div>
+      </div>
+      <div class="vision-connector">
+        <div class="vision-line"></div>
+        <div class="vision-dot"></div>
+        <div class="vision-line"></div>
+      </div>
+      <div class="vision-node">
+        <div class="vision-year">2028</div>
+        <div class="vision-card card">
+          <h4>行业标准</h4>
+          <p class="dim">建立"五维健康"行业标准评价体系<br>成为家庭教育领域权威数据平台</p>
+          <div class="vision-icon">🏛️</div>
+        </div>
+      </div>
+      <div class="vision-connector">
+        <div class="vision-line"></div>
+        <div class="vision-dot"></div>
+        <div class="vision-line"></div>
+      </div>
+      <div class="vision-node">
+        <div class="vision-year">2029</div>
+        <div class="vision-card card">
+          <h4>生态入口</h4>
+          <p class="dim">开放平台生态,接入第三方服务商<br>构建家庭服务超级入口 · 覆盖 <strong>50 万+</strong></p>
+          <div class="vision-icon">🌐</div>
+        </div>
+      </div>
+    </div>
+    <div class="note-callout mt-l tc" style="text-align:center">
+      <span style="font-size:24px;font-weight:700;background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent">从家庭工具 → 服务平台 → 行业标准 → 生态入口</span>
+    </div>
+  </section>
+
+  <!-- ════════════════════════════════════════════════ -->
+  <!-- 15. section: 商业模式与壁垒 -->
+  <!-- ════════════════════════════════════════════════ -->
+  <section class="slide" data-title="商业模式">
+    <span class="section-num">04</span>
+    <p class="num-tag">BUSINESS MODEL</p>
+    <h2 class="h2 mt-s">三方平台经济模型</h2>
+    <div class="grid g3 mt-l">
+      <div class="card">
+        <h4>💰 测评服务费</h4>
+        <div class="metric mt-s"><div class="n">15-25%</div><div class="l">订单抽佣</div></div>
+        <p class="dim mt-s">家长支付测评订单,平台抽取佣金</p>
+      </div>
+      <div class="card card-accent">
+        <h4>📦 规划师佣金</h4>
+        <div class="metric mt-s"><div class="n">10-20%</div><div class="l">套餐抽佣</div></div>
+        <p class="dim mt-s">规划师套餐销售,平台抽取分成</p>
+      </div>
+      <div class="card">
+        <h4>🛒 商品交易费</h4>
+        <div class="metric mt-s"><div class="n">5-15%</div><div class="l">商城抽佣</div></div>
+        <p class="dim mt-s">商城交易,平台抽取手续费</p>
+      </div>
+      <div class="card">
+        <h4>👑 会员订阅</h4>
+        <div class="metric mt-s"><div class="n">月卡/年卡</div><div class="l">增值 + AI 助手</div></div>
+        <p class="dim mt-s">家庭会员,高频增值服务</p>
+      </div>
+      <div class="card">
+        <h4>🔄 推广分佣</h4>
+        <div class="metric mt-s"><div class="n">二级分销</div><div class="l">社交裂变</div></div>
+        <p class="dim mt-s">家长推荐家长,裂变增长</p>
+      </div>
+      <div class="card" style="border-color:var(--accent);background:var(--grad-soft)">
+        <h4>📊 单位经济</h4>
+        <div class="metric mt-s"><div class="n">12-50x</div><div class="l">LTV / CAC</div></div>
+        <p class="dim mt-s">CAC 15-25 元 · LTV 300-800 元</p>
+      </div>
+    </div>
+  </section>
+
+  <!-- 16. 核心壁垒 -->
+  <section class="slide" data-title="核心壁垒">
+    <span class="section-num">04</span>
+    <p class="num-tag">MOAT</p>
+    <h2 class="h2 mt-s">四大核心壁垒</h2>
+    <div class="grid g2 mt-l moat-grid">
+      <div class="card moat-card">
+        <div class="moat-icon">🔄</div>
+        <h4>三方网络效应</h4>
+        <p class="dim">家长 → 规划师 → 服务,形成正向飞轮</p>
+        <div class="moat-flywheel">
+          <span>更多家长</span> → <span>更多规划师</span> → <span>更优服务</span> → <span>更多家庭</span>
+        </div>
+      </div>
+      <div class="card moat-card">
+        <div class="moat-icon">📊</div>
+        <h4>行为数据护城河</h4>
+        <p class="dim">47 实体 · 25+ 数据源 · 时间越久越深</p>
+        <div class="moat-tags">
+          <span class="pill">任务记录→努力曲线</span>
+          <span class="pill">能量变化→健康趋势</span>
+          <span class="pill">测评结果→成长档案</span>
+          <span class="pill">游戏记录→认知基线</span>
+        </div>
+      </div>
+      <div class="card moat-card">
+        <div class="moat-icon">⭐</div>
+        <h4>五维评价体系</h4>
+        <p class="dim">自研方法论 · 五行相生 · 多源加权</p>
+        <div class="moat-tags">
+          <span class="pill">沙盘评分+账本计量</span>
+          <span class="pill">动态配置映射比例</span>
+          <span class="pill">日上限 200 安全机制</span>
+        </div>
+      </div>
+      <div class="card moat-card">
+        <div class="moat-icon">📍</div>
+        <h4>四级地址匹配</h4>
+        <p class="dim">省市区街道联动 · 区域回退降级</p>
+        <div class="moat-tags">
+          <span class="pill">精确街道级匹配</span>
+          <span class="pill">逐级降级回退</span>
+          <span class="pill">本地化服务基建</span>
+        </div>
+      </div>
+    </div>
+    <div class="note-callout mt-l tc">
+      <span style="font-weight:700;font-size:22px;">竞品追赶需要 <span style="color:var(--bad)">2-3 年</span></span>
+    </div>
+  </section>
+
+  <!-- 17. THANKS -->
+  <section class="slide center tc" data-title="感谢">
+    <div class="cover-bg"></div>
+    <div class="thanks-content">
+      <div class="mega">感谢</div>
+      <p class="mega-sub">浠艾福 Care Family — 给全家的一站式幸福提案</p>
+      <div class="row mt-l" style="justify-content:center;gap:24px">
+        <span class="pill pill-accent">身 · 心 · 智 · 行 · 富</span>
+        <span class="pill">亿图腾(北京)科技有限公司</span>
+      </div>
+      <div class="thanks-contact mt-l">
+        <div class="contact-row">
+          <span class="dim">小程序:搜索"浠艾福"</span>
+          <span class="dim">后端:localhost:8080</span>
+          <span class="dim">Web 管理:localhost:8082</span>
+        </div>
+      </div>
+    </div>
+  </section>
+
+</div>
+<script src="assets/runtime.js"></script>
+</body>
+</html>

+ 252 - 0
docs/ppt-cfc-overview/style.css

@@ -0,0 +1,252 @@
+/* ========================================================================
+   cfc-overview — 浠艾福项目总体介绍 · 四年路线规划
+   Scoped under .tpl-cfc-overview, extends pitch-deck tokens
+   ======================================================================== */
+
+/* ── Inherit pitch-deck brand tokens ── */
+.tpl-cfc-overview{
+  --accent:#F97316;
+  --accent-2:#FB923C;
+  --accent-3:#F97316;
+  --grad:linear-gradient(135deg,#F97316 0%,#FB923C 55%,#FBBF24 100%);
+  --grad-soft:linear-gradient(135deg,#FFF7ED,#FFEDD5 55%,#FEF3C7);
+  --good:#16a34a;
+  --warn:#f59e0b;
+  --bad:#dc2626;
+  --font-sans:'Inter','Noto Sans SC','PingFang SC','Microsoft YaHei',sans-serif;
+}
+
+/* ── Brand color overrides for buttons/pills ── */
+.tpl-cfc-overview .kicker{color:var(--accent)}
+.tpl-cfc-overview .num-tag{color:var(--accent)}
+.tpl-cfc-overview .pill-accent{background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent);border-color:color-mix(in srgb,var(--accent) 30%,transparent)}
+.tpl-cfc-overview .brand-dot{background:var(--grad)}
+
+/* ── Fix relative paths for assets ── */
+/* Since assets are accessed via absolute path /root/.agents/... nothing needed */
+
+/* ========================================================================
+   TOC GRID
+   ======================================================================== */
+.tpl-cfc-overview .toc-grid .card{padding:48px 20px 36px;position:relative}
+.tpl-cfc-overview .toc-num{font-size:48px;font-weight:900;color:var(--surface-2);line-height:1;margin-bottom:4px;letter-spacing:-.04em}
+.tpl-cfc-overview .toc-dot{width:8px;height:8px;border-radius:50%;background:var(--accent);margin:14px auto 0;opacity:.6}
+
+/* ========================================================================
+   五维 2x3 卡片
+   ======================================================================== */
+.tpl-cfc-overview .wuxing-grid{gap:20px}
+.tpl-cfc-overview .card-wuxing{border-left:4px solid var(--wx-color,#ccc);padding:20px 24px}
+.tpl-cfc-overview .card-wuxing .wx-icon{font-size:28px;margin-bottom:6px}
+.tpl-cfc-overview .card-wuxing h4{margin-bottom:4px;font-size:20px}
+.tpl-cfc-overview .card-wuxing p{font-size:14px}
+.tpl-cfc-overview .card-wuxing-glow{
+  background:var(--grad-soft);
+  border:1.5px solid color-mix(in srgb,var(--accent) 40%,transparent);
+  padding:20px 24px;
+  display:flex;flex-direction:column;align-items:center;text-align:center
+}
+.tpl-cfc-overview .card-wuxing-glow .wx-icon{font-size:28px;margin-bottom:4px}
+.tpl-cfc-overview .card-wuxing-glow h4{font-size:20px;margin-bottom:4px}
+
+/* ========================================================================
+   技术架构
+   ======================================================================== */
+.tpl-cfc-overview .arch-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
+.tpl-cfc-overview .arch-tier{display:flex;flex-direction:column;gap:8px;align-items:center}
+.tpl-cfc-overview .arch-label{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--text-3);margin-bottom:4px}
+.tpl-cfc-overview .arch-box{
+  background:var(--surface);
+  border:1px solid var(--border);
+  border-radius:12px;
+  padding:10px 16px;
+  text-align:center;
+  font-size:13px;
+  line-height:1.4;
+  white-space:nowrap;
+  box-shadow:var(--shadow);
+}
+.tpl-cfc-overview .arch-box strong{display:block;font-size:14px}
+.tpl-cfc-overview .arch-arrow{font-size:24px;color:var(--accent);font-weight:300}
+
+/* ========================================================================
+   AI 对话气泡
+   ======================================================================== */
+.tpl-cfc-overview .ai-demo-box{
+  background:var(--surface-2);
+  border:1px solid var(--border);
+  border-radius:12px;
+  padding:14px 16px;
+  font-size:13px;
+  line-height:1.6;
+  margin-bottom:8px;
+}
+.tpl-cfc-overview .ai-user-msg{
+  color:var(--text-1);
+  font-weight:500;
+  margin-bottom:10px;
+}
+.tpl-cfc-overview .ai-assistant-msg{
+  background:var(--surface);
+  border:1px solid var(--border);
+  border-radius:10px;
+  padding:10px 14px;
+  color:var(--text-1);
+  line-height:1.7;
+}
+.tpl-cfc-overview .ai-assistant-msg strong{color:var(--accent)}
+
+/* AI 表格表头 */
+.tpl-cfc-overview .ai-row-head{
+  background:var(--surface-2);
+  font-size:11px;
+  font-weight:700;
+  text-transform:uppercase;
+  letter-spacing:.06em;
+  color:var(--text-3);
+  padding:6px 16px;
+}
+
+/* ========================================================================
+   已上线功能网格
+   ======================================================================== */
+.tpl-cfc-overview .feature-grid{gap:14px}
+.tpl-cfc-overview .feature-done{padding:16px 18px}
+.tpl-cfc-overview .feature-done .f-icon{font-size:20px;margin-bottom:4px}
+.tpl-cfc-overview .feature-done h4{font-size:15px;margin-bottom:2px}
+.tpl-cfc-overview .feature-done p{font-size:12px;line-height:1.4}
+
+/* ========================================================================
+   AI 应用场景表格
+   ======================================================================== */
+.tpl-cfc-overview .ai-table{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden}
+.tpl-cfc-overview .ai-row{
+  display:grid;
+  grid-template-columns:1.5fr auto 2fr auto;
+  gap:8px;
+  align-items:center;
+  padding:10px 16px;
+  font-size:13px;
+  border-bottom:1px solid var(--border);
+  line-height:1.4;
+}
+.tpl-cfc-overview .ai-row:last-child{border-bottom:none}
+.tpl-cfc-overview .ai-row .pill{font-size:11px;white-space:nowrap}
+
+/* ========================================================================
+   Roadmap 时间轴 — 水平排列
+   ======================================================================== */
+.tpl-cfc-overview .roadmap-timeline{overflow-x:auto;padding-bottom:8px}
+.tpl-cfc-overview .rm-track{display:flex;align-items:stretch;gap:0;min-width:900px}
+.tpl-cfc-overview .rm-phase{
+  flex:1;
+  background:var(--surface);
+  border:1px solid var(--border);
+  border-radius:var(--radius);
+  padding:20px 18px;
+  position:relative;
+  min-width:200px;
+}
+.tpl-cfc-overview .rm-phase.rm-now{border-color:var(--accent);border-width:2px}
+.tpl-cfc-overview .rm-phase.rm-next{border-color:color-mix(in srgb,var(--accent) 50%,transparent)}
+.tpl-cfc-overview .rm-badge{
+  display:inline-block;
+  font-size:11px;
+  font-weight:700;
+  padding:2px 10px;
+  border-radius:999px;
+  background:var(--accent);
+  color:#fff;
+  margin-bottom:10px;
+}
+.tpl-cfc-overview .rm-phase.rm-now .rm-badge{background:var(--good)}
+.tpl-cfc-overview .rm-phase h4{font-size:17px;margin-bottom:2px;line-height:1.4}
+.tpl-cfc-overview .rm-phase h4 .dim{font-size:14px;font-weight:400}
+.tpl-cfc-overview .rm-date{font-size:12px;color:var(--text-3);margin-bottom:10px}
+.tpl-cfc-overview .rm-items{display:flex;flex-wrap:wrap;gap:6px}
+.tpl-cfc-overview .rm-items .pill{font-size:11px;padding:2px 8px}
+.tpl-cfc-overview .rm-connector{
+  display:flex;align-items:center;justify-content:center;
+  min-width:32px;flex-shrink:0;
+}
+.tpl-cfc-overview .rm-arrow{
+  font-size:22px;color:var(--text-3);font-weight:300;opacity:.5;
+}
+.tpl-cfc-overview .note-callout{
+  padding:12px 16px;
+  background:var(--surface-2);
+  border-radius:var(--radius-sm);
+  border:1px solid var(--border);
+}
+
+/* ========================================================================
+   三年愿景 — 水平时间轴
+   ======================================================================== */
+.tpl-cfc-overview .vision-track{display:flex;align-items:center;gap:0}
+.tpl-cfc-overview .vision-node{flex:1;text-align:center;min-width:0}
+.tpl-cfc-overview .vision-year{
+  font-size:48px;
+  font-weight:900;
+  letter-spacing:-.04em;
+  background:var(--grad);
+  -webkit-background-clip:text;
+  background-clip:text;
+  color:transparent;
+  line-height:1;
+  margin-bottom:12px;
+}
+.tpl-cfc-overview .vision-card{position:relative;padding:24px 16px;margin:0 8px}
+.tpl-cfc-overview .vision-card h4{font-size:20px;margin-bottom:6px}
+.tpl-cfc-overview .vision-card p{font-size:13px;line-height:1.5}
+.tpl-cfc-overview .vision-icon{
+  position:absolute;top:-14px;right:12px;
+  font-size:28px;opacity:.6;
+}
+.tpl-cfc-overview .vision-connector{
+  display:flex;align-items:center;
+  min-width:40px;
+}
+.tpl-cfc-overview .vision-line{flex:1;height:2px;background:var(--border-strong)}
+.tpl-cfc-overview .vision-dot{
+  width:10px;height:10px;
+  border-radius:50%;
+  background:var(--accent);
+  flex-shrink:0;
+}
+
+/* ========================================================================
+   核心壁垒
+   ======================================================================== */
+.tpl-cfc-overview .moat-grid{gap:20px}
+.tpl-cfc-overview .moat-card{padding:24px}
+.tpl-cfc-overview .moat-icon{font-size:32px;margin-bottom:6px}
+.tpl-cfc-overview .moat-card h4{font-size:20px;margin-bottom:4px}
+.tpl-cfc-overview .moat-card p{font-size:13px;margin-bottom:10px}
+.tpl-cfc-overview .moat-flywheel{
+  display:flex;align-items:center;gap:6px;
+  font-size:12px;font-weight:600;
+  color:var(--accent);
+  flex-wrap:wrap;
+  background:var(--surface-2);
+  padding:8px 12px;
+  border-radius:8px;
+}
+.tpl-cfc-overview .moat-tags{display:flex;flex-wrap:wrap;gap:4px}
+.tpl-cfc-overview .moat-tags .pill{font-size:11px}
+
+/* ========================================================================
+   Thanks
+   ======================================================================== */
+.tpl-cfc-overview .thanks-content{position:relative;z-index:2}
+.tpl-cfc-overview .thanks-contact{max-width:500px;margin:0 auto}
+.tpl-cfc-overview .contact-row{display:flex;flex-direction:column;gap:6px}
+
+/* ========================================================================
+   Small utilities
+   ======================================================================== */
+.tpl-cfc-overview .ul-sm{list-style:none;padding:0;margin:0}
+.tpl-cfc-overview .ul-sm li{position:relative;padding-left:16px;margin-bottom:5px;font-size:14px;line-height:1.5}
+.tpl-cfc-overview .ul-sm li::before{content:"·";position:absolute;left:0;font-weight:700;color:var(--accent)}
+
+/* ── KPI grid metric numbers ── */
+.tpl-cfc-overview .metric .n{font-size:56px}

BIN
docs/screenshots/policy/01-cover.png


BIN
docs/screenshots/policy/02-score-dashboard.png


BIN
docs/screenshots/policy/03-disease-risk.png


BIN
docs/screenshots/policy/04-nutrition-overview.png


BIN
docs/screenshots/policy/05-vitamins.png


BIN
docs/screenshots/policy/06-health-score.png


BIN
docs/screenshots/policy/07-gut-age.png


BIN
docs/screenshots/policy/08-bacteria-balance.png


BIN
docs/screenshots/policy/09-bacteria-diversity.png


BIN
docs/screenshots/policy/10-diet-recommendations.png


BIN
docs/screenshots/policy/health_china_2030.png


BIN
docs/screenshots/policy/health_consumption.png


BIN
docs/screenshots/policy/health_family_notice.png


+ 2916 - 0
docs/superpowers/plans/2026-06-15-comprehensive-discrepancy-fix.md

@@ -0,0 +1,2916 @@
+# 代码 vs 设计差异 — 全面修复计划
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 修复小程序全部 5 个 TabBar 页面(首页/身泰/心智/行远/富沛)的代码实现与 v2.0 设计文档 + 五维页面重设计 spec 之间的所有差异
+
+**架构:** 分 9 阶段 19 Tasks 按依赖顺序推进。Phase 1-2 基础设施 → Phase 3-4 Tab页改造(含心智认知雷达图+大五人格图+关系图、行远关系雷达图)→ Phase 5 富沛页重做 → Phase 6 组件改造 → Phase 7 后端改造(含整型金额)→ Phase 8 RadarChart 组件 + 后端数据 API → Phase 9 验证。
+- **首页五行沙盘保留不动**(仅增量加 FamilyEnergyBar/服务商入口/维度区块)
+- **心智页**:能量柱 + 认知六维雷达图 + 大五人格图(10岁+DAN) + 关系图 + 任务 + 活动 + 商品
+- **行远页**:六类关系雷达图(孩子/另一半/父母/同伴/师长/领导) + 任务 + 活动 + 商品
+- 需新建 `<RadarChart>` canvas 通用组件,心智和行远页共用
+
+**前置设计文档:**
+- `docs/需求分析/底签页面详细设计.md` (v2.0 TabBar 5页设计基线)
+- `docs/superpowers/specs/2026-06-13-five-dimension-pages-redesign.md` (三页统一改造+堆叠图+游客模式)
+
+**Tech Stack:** uni-app Vue 2, Spring Boot 2.7, MyBatis-Plus
+
+**优先级:** P0(阻塞) > P1(重要) > P2(一般) > P3(优化)
+
+**设计约束 (新增):**
+- **禁止使用多精度类型(decimal、double、float)**:数据库中所有涉及金额、百分比、评分等数值字段使用整型(`INT`/`BIGINT`),举例如下:
+  - 金额以 **分** 为单位存储(如 `1 元 = 100`),前端展示时除以 100 并保留 2 位小数
+  - 百分比以 **千分比** 存储(如 `23.5% = 235`),前端展示时除以 10 并拼接 `%`
+  - 评分以 **百分制整数** 存储(如 `85.5 = 855`),前端展示时除以 10
+- **现有代码改造**:`Product.price`/`memberPrice` 等已存在的 `BigDecimal` 字段,在本次修复中改为 `Integer`(分),前端价格展示统一加 `¥` 前缀并做 `(price / 100).toFixed(2)` 转换
+- **后端输出**:DTO 层输出时仍保留 price/memberPrice 等字段名,类型统一为 `Integer`,前端自行做分→元展示转换
+- **理由**:彻底规避浮点数精度问题(0.1+0.2≠0.3),消除跨系统金额计算风险,简化数据库计算逻辑
+
+---
+
+## 文件改动清单
+
+| 文件 | 操作 | 任务 |
+|------|------|------|
+| `cfc-frontend/pages.json` | 修改 | T1 TabBar配置 |
+| `cfc-frontend/static/tab-*.png` | 创建 | T2 图标 |
+| `cfc-frontend/pages/index/index.vue` | 修改 | T3/T10 |
+| `cfc-frontend/pages/teacher/index.vue` | 修改 | T4 |
+| `cfc-frontend/pages/body/index.vue` | 修改 | T5 |
+| `cfc-frontend/pages/mind/index.vue` | 修改 | T6 |
+| `cfc-frontend/pages/action/index.vue` | 修改 | T7 |
+| `cfc-frontend/pages/index/parent-index.vue` | 修改 | T8 |
+| `cfc-frontend/pages/index/child-index.vue` | 修改 | T9 |
+| `cfc-frontend/pages/wealth/index.vue` | **重写** | T11 |
+| `cfc-frontend/components/FamilyEnergyBar.vue` | 修改 | T12 |
+| `cfc-frontend/components/DimensionProducts.vue` | 修改 | T13 |
+| `cfc-frontend/components/DimensionActivities.vue` | 修改 | T13 |
+| `cfc-frontend/utils/api.js` | 修改 | T10 |
+| `cfc-backend/.../controller/product/ProductController.java` | 修改 | T14 |
+| `cfc-backend/.../entity/Activity.java` | 修改 | T15 |
+| `cfc-backend/.../controller/sncp/ActivityController.java` | 修改 | T15 |
+| `cfc-frontend/components/RadarChart.vue` | **新建** | T17 |
+| `cfc-backend/.../controller/ContactController.java` | **新建** | T18 |
+| `cfc-backend/.../entity/Contact.java` | 修改(如有) | T18 |
+| `cfc-backend/.../controller/assessment/AssessmentController.java` | 修改 | T18 |
+
+---
+
+## Phase 1 — TabBar 基础设施 (P0 🔴)
+
+### Task 1: pages.json TabBar 配置修改
+
+**Files:**
+- Modify: `cfc-frontend/pages.json:589-626`
+
+- [ ] **Step 1: 修改 TabBar list 配置**
+
+将 TabBar 5 个 Tab 的名称和路径更新:
+
+```json
+{
+  "tabBar": {
+    "color": "#7A7E83",
+    "selectedColor": "#F97316",
+    "borderStyle": "black",
+    "backgroundColor": "#FFFFFF",
+    "list": [
+      {
+        "pagePath": "pages/index/index",
+        "text": "首页",
+        "iconPath": "static/tab-home.png",
+        "selectedIconPath": "static/tab-home-active.png"
+      },
+      {
+        "pagePath": "pages/body/index",
+        "text": "身泰",
+        "iconPath": "static/tab-body.png",
+        "selectedIconPath": "static/tab-body-active.png"
+      },
+      {
+        "pagePath": "pages/mind/index",
+        "text": "心智",
+        "iconPath": "static/tab-mind.png",
+        "selectedIconPath": "static/tab-mind-active.png"
+      },
+      {
+        "pagePath": "pages/action/index",
+        "text": "行远",
+        "iconPath": "static/tab-action.png",
+        "selectedIconPath": "static/tab-action-active.png"
+      },
+      {
+        "pagePath": "pages/wealth/index",
+        "text": "富沛",
+        "iconPath": "static/tab-wealth.png",
+        "selectedIconPath": "static/tab-wealth-active.png"
+      }
+    ]
+  }
+}
+```
+
+变更点:
+- Tab 2: "身体" → "身泰"(路径 `pages/body/index` 不变)
+- Tab 4: "行动" → "行远"(路径 `pages/action/index` 不变)
+- Tab 5: "我的" + `pages/profile/profile` → "富沛" + `pages/wealth/index`
+
+- [ ] **Step 2: 确认 wealth 子包注册存在**
+
+确认 `pages.json` 的 `subPackages` 数组中包含:
+```json
+{
+  "root": "pages/wealth",
+  "pages": [
+    { "path": "index", "style": { "navigationBarTitleText": "富沛" } },
+    { "path": "insurance-list", "style": { "navigationBarTitleText": "保单管理" } },
+    { "path": "insurance-add", "style": { "navigationBarTitleText": "保单详情" } },
+    { "path": "checkin", "style": { "navigationBarTitleText": "财商打卡" } }
+  ]
+}
+```
+
+如果缺少,补上 `navigationBarTitleText: "富沛"` 配置。
+
+- [ ] **Step 3: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages.json")`
+预期:无错误
+
+---
+
+### Task 2: TabBar 图标文件补全
+
+**Files:**
+- Create: `cfc-frontend/static/tab-wealth.png`(从 `tab-profile.png` 复制)
+- Create: `cfc-frontend/static/tab-wealth-active.png`(从 `tab-profile-active.png` 复制)
+
+- [ ] **Step 1: 复制图标文件**
+
+```bash
+cp cfc-frontend/static/tab-profile.png cfc-frontend/static/tab-wealth.png
+cp cfc-frontend/static/tab-profile-active.png cfc-frontend/static/tab-wealth-active.png
+```
+
+- [ ] **Step 2: 确认图标文件存在**
+
+验证 `tab-wealth.png` 和 `tab-wealth-active.png` 文件存在且非空。
+
+---
+
+## Phase 2 — 首页路由清理 (P0 🔴)
+
+### Task 3: index.vue — 移除 TeacherIndex 引入和路由分支
+
+**Files:**
+- Modify: `cfc-frontend/pages/index/index.vue`
+
+- [ ] **Step 1: 移除 TeacherIndex import**
+
+修改前(约第 108 行):
+```javascript
+import TeacherIndex from '../teacher/index.vue'
+```
+
+修改后:
+```javascript
+// TeacherIndex 已废弃(2026-06-15),教师功能折入 parent-index
+```
+
+- [ ] **Step 2: 从 components 对象中移除 TeacherIndex**
+
+修改前:
+```javascript
+components: {
+  ParentIndex,
+  ChildIndex,
+  TeacherIndex,
+  PageBanner,
+  FamilyEnergyBar
+}
+```
+
+修改后:
+```javascript
+components: {
+  ParentIndex,
+  ChildIndex,
+  PageBanner,
+  FamilyEnergyBar
+}
+```
+
+- [ ] **Step 3: 移除 teacher 路由分支**
+
+修改前(约第 97 行):
+```html
+<teacher-index v-else-if="currentRole === 'teacher'" />
+```
+
+修改后:删除该行。
+
+- [ ] **Step 4: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/index/index.vue")`
+预期:clean(TeacherIndex 引用全部移除)
+
+---
+
+### Task 4: teacher/index.vue — 软废弃
+
+**Files:**
+- Modify: `cfc-frontend/pages/teacher/index.vue`
+
+- [ ] **Step 1: 文件首部添加废弃标记**
+
+在 `<template>` 上方加注释:
+```html
+<!--
+  ⚠️ 废弃说明(2026-06-15):
+  此页面已按照设计决策废弃。教师首页功能已折入 parent-index。
+  保留文件仅用于参考,不通过任何路由访问。
+-->
+```
+
+- [ ] **Step 2: 替换 `<template>` 为废弃提示页**
+
+将整个 template 内容替换为:
+```html
+<template>
+  <view class="deprecated-container">
+    <view class="deprecated-card">
+      <text class="deprecated-icon">📋</text>
+      <text class="deprecated-title">服务商功能已整合</text>
+      <text class="deprecated-desc">服务商工具和待办已迁移至家长首页,请从首页进入</text>
+      <view class="deprecated-btn" @click="goToHome">回到首页</view>
+    </view>
+  </view>
+</template>
+```
+
+- [ ] **Step 3: 精简 script**
+
+保留 `goToHome` 方法,移除其他所有业务逻辑:
+```javascript
+<script>
+export default {
+  methods: {
+    goToHome() {
+      uni.switchTab({ url: '/pages/index/index' })
+    }
+  }
+}
+</script>
+```
+
+- [ ] **Step 4: 添加废弃提示页样式**
+
+```css
+<style scoped>
+.deprecated-container {
+  min-height: 100vh;
+  background: #f5f7fa;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 60rpx;
+}
+.deprecated-card {
+  background: #fff;
+  border-radius: 24rpx;
+  padding: 80rpx 60rpx;
+  text-align: center;
+  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
+}
+.deprecated-icon {
+  font-size: 80rpx;
+  margin-bottom: 24rpx;
+}
+.deprecated-title {
+  font-size: 36rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 16rpx;
+  display: block;
+}
+.deprecated-desc {
+  font-size: 28rpx;
+  color: #999;
+  margin-bottom: 40rpx;
+  display: block;
+}
+.deprecated-btn {
+  display: inline-block;
+  background: #F97316;
+  color: #fff;
+  font-size: 30rpx;
+  font-weight: 600;
+  padding: 20rpx 60rpx;
+  border-radius: 40rpx;
+}
+.deprecated-btn:active { opacity: 0.8; }
+</style>
+```
+
+- [ ] **Step 5: 确认 teacher 子包路由保留**
+
+teacher 子包页面(families/team/messages/orders 等)作为功能页面仍可通过导航访问,仅首页废弃。**pages.json 中 teacher subPackage 无需修改。**
+
+- [ ] **Step 6: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/teacher/index.vue")`
+预期:clean
+
+---
+
+## Phase 3 — Tab页游客模式改造 (P1 🟠)
+
+### Task 5: body/index.vue — 游客可见商品/活动 + 修复 BASE_URL
+
+**Files:**
+- Modify: `cfc-frontend/pages/body/index.vue`
+
+- [ ] **Step 1: DimensionActivities 和 DimensionProducts 移除 `v-if="isLoggedIn"`**
+
+修改前(约第 94-107 行):
+```html
+<DimensionActivities
+  v-if="isLoggedIn"
+  dimensionCode="body"
+  :activities="dimensionActivities"
+  @activityClick="goActivityDetail"
+  @moreActivities="goMoreActivities" />
+<DimensionProducts
+  v-if="isLoggedIn"
+  dimensionCode="body"
+  :products="dimensionProducts"
+  @productClick="goProductDetail"
+  @moreProducts="goMoreProducts" />
+<DimensionTasks
+  v-if="isLoggedIn"
+  dimensionCode="body"
+  :tasks="todayTasks"
+  @taskClick="goTaskDetail" />
+```
+
+修改后:
+```html
+<DimensionActivities
+  :isLoggedIn="isLoggedIn"
+  dimensionCode="body"
+  :activities="dimensionActivities"
+  @activityClick="goActivityDetail"
+  @moreActivities="goMoreActivities" />
+<DimensionProducts
+  :isLoggedIn="isLoggedIn"
+  dimensionCode="body"
+  :products="dimensionProducts"
+  @productClick="goProductDetail"
+  @moreProducts="goMoreProducts" />
+<DimensionTasks
+  v-if="isLoggedIn"
+  dimensionCode="body"
+  :tasks="todayTasks"
+  @taskClick="goTaskDetail" />
+```
+
+变更:Activities/Products 不再依赖 `isLoggedIn` 控制渲染;传入 `:isLoggedIn="isLoggedIn"` 让组件内部处理价格/提示切换。Tasks 仍保留 `v-if="isLoggedIn"`。
+
+- [ ] **Step 2: onShow 中游客也加载活动和商品数据**
+
+修改前(onShow 约第 130 行):
+```javascript
+onShow() {
+  var token = uni.getStorageSync('token')
+  this.isLoggedIn = !!token
+  if (!this.isLoggedIn) { this.loading = false; return }
+  // ... 登录后的数据加载
+  this.loadDimensionActivities()
+  this.loadDimensionProducts()
+  this.loadTodayTasks()
+}
+```
+
+修改后:
+```javascript
+onShow() {
+  var token = uni.getStorageSync('token')
+  this.isLoggedIn = !!token
+  // 游客也加载活动和商品
+  this.loadDimensionActivities()
+  this.loadDimensionProducts()
+  if (this.isLoggedIn) {
+    this.loadTodayTasks()
+    // ...其他登录后数据
+  }
+}
+```
+
+- [ ] **Step 3: 修复 BASE_URL 硬编码**
+
+修改前(约第 150 行):
+```javascript
+const BASE_URL = 'http://localhost:8080'
+```
+
+修改后:
+```javascript
+import config from '../../config'
+const BASE_URL = config.baseUrl || config.api('')
+```
+
+注:如果该文件使用的是 `uni.request` 直连而非 api.js 封装,this 就是修复;如果实际使用的是 `utils/api.js` 中的封装方法,则此步可跳过(仅删除硬编码行)。
+
+搜索确认 `BASE_URL` 变量在文件中的实际使用方式。
+
+- [ ] **Step 4: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/body/index.vue")`
+预期:clean
+
+---
+
+### Task 6: mind/index.vue — FamilyEnergyBar 堆叠图 + 游客可见商品/活动
+
+**Files:**
+- Modify: `cfc-frontend/pages/mind/index.vue`
+- (FamilyEnergyBar 组件修改在 Task 12)
+
+- [ ] **Step 1: FamilyEnergyBar 传入 dualDimension 堆叠参数**
+
+修改前(约第 20-24 行):
+```html
+<FamilyEnergyBar
+  v-if="isLoggedIn"
+  dimensionCode="mind"
+  :sandboxData="sandboxData"
+  :dualDimension="dualDimension" />
+```
+
+修改后:
+```html
+<FamilyEnergyBar
+  v-if="isLoggedIn"
+  dimensionCode="mind"
+  :sandboxData="sandboxData"
+  :dualDimension="dualDimensionData" />
+```
+
+- [ ] **Step 2: 在 data 中添加 dualDimensionData**
+
+```javascript
+data() {
+  return {
+    // ... 现有 data
+    dualDimensionData: null  // { primary: {score,label,color}, secondary: {score,label,color} }
+  }
+}
+```
+
+- [ ] **Step 3: 修改 loadEnergyData 方法构建堆叠数据**
+
+在 `methods` 中找到 `loadEnergyData`(约第 662-680 行),修改为从 API 返回的 dimensions 数组中提取 mind + wisdom 两个维度的数据:
+
+```javascript
+loadEnergyData: function() {
+  var self = this
+  if (!this.currentChildId) return
+  getEnergyOverview(this.currentChildId).then(function(res) {
+    if (res && res.data) {
+      var dims = res.data.dimensions || []
+      var mindDim = null
+      var wisdomDim = null
+      for (var i = 0; i < dims.length; i++) {
+        if (dims[i].code === 'mind') mindDim = dims[i]
+        if (dims[i].code === 'wisdom') wisdomDim = dims[i]
+      }
+      self.dualDimensionData = {
+        primary: {
+          code: 'mind',
+          score: (mindDim && mindDim.score) || 0,
+          label: '心理',
+          color: '#FF6B35'
+        },
+        secondary: {
+          code: 'wisdom',
+          score: (wisdomDim && wisdomDim.score) || 0,
+          label: '认知',
+          color: '#8B5CF6'
+        }
+      }
+    }
+  }).catch(function(e) {
+    console.log('获取能量概览失败', e)
+  })
+}
+```
+
+- [ ] **Step 4: DimensionActivities/Products 移除 `v-if="isLoggedIn"`(同 Task 5 Step 1)**
+
+修改前(约第 261-280 行):
+```html
+<DimensionTasks v-if="isLoggedIn" ... />
+<DimensionActivities v-if="isLoggedIn" ... />
+<DimensionProducts v-if="isLoggedIn" ... />
+```
+
+修改后:
+```html
+<DimensionTasks v-if="isLoggedIn" ... />
+<DimensionActivities :isLoggedIn="isLoggedIn" ... />
+<DimensionProducts :isLoggedIn="isLoggedIn" ... />
+```
+
+- [ ] **Step 5: onShow 中游客也加载活动和商品数据**
+
+```javascript
+// 在 token 检查外也调用:
+this.loadDimensionActivities()
+this.loadDimensionProducts()
+```
+
+- [ ] **Step 6: 新增认知六维雷达图区块**
+
+在能量柱下方增加认知能力六维雷达图。使用 `<RadarChart>` 组件(Task 17 创建),传入 6 个维度数据。
+
+所需 API:`GET /api/assessment/cognitive-profile?childId={childId}`(需登录)
+
+模板位置(能量柱下方):
+```html
+<!-- ===== 认知能力雷达图 ===== -->
+<view class="section" v-if="isLoggedIn && cognitiveData">
+  <view class="section-header">
+    <text class="section-title">🧠 认知能力</text>
+  </view>
+  <RadarChart
+    :dimensions="cognitiveDimensions"
+    :scores="cognitiveScores"
+    :maxScore="100"
+    :label="'认知能力评估'"
+    :width="600"
+    :height="600" />
+</view>
+```
+
+在 data 中增加:
+```javascript
+cognitiveData: null,
+cognitiveDimensions: [
+  { key: 'sensation', label: '感知觉' },
+  { key: 'attention', label: '注意力' },
+  { key: 'memory', label: '记忆力' },
+  { key: 'logic', label: '逻辑推理' },
+  { key: 'spatial', label: '空间想象' },
+  { key: 'speed', label: '加工速度' }
+],
+cognitiveScores: []
+```
+
+在 methods 中添加 `loadCognitiveData`:
+```javascript
+loadCognitiveData: function() {
+  if (!this.currentChildId) return
+  var self = this
+  // 调用认知能力 API(如不存在则显示模拟占位)
+  uni.request({
+    url: '/api/assessment/cognitive-profile',
+    method: 'POST',
+    data: { childId: this.currentChildId },
+    success: function(res) {
+      if (res.data && res.data.code === 200 && res.data.data) {
+        var data = res.data.data
+        self.cognitiveScores = [
+          data.sensation || 0,
+          data.attention || 0,
+          data.memory || 0,
+          data.logic || 0,
+          data.spatial || 0,
+          data.speed || 0
+        ]
+      }
+    },
+    fail: function() {
+      // API 未就绪时静默降级
+      self.cognitiveData = null
+    }
+  })
+}
+```
+
+在 `onShow` 的登录分支中调用 `this.loadCognitiveData()`。
+
+- [ ] **Step 7: 新增大五人格图区块(仅 10 岁以上,需 DAN 测评)**
+
+在认知雷达图下方增加大五人格图。使用 `<RadarChart>` 组件(Task 17),5 边形显示 OCEAN 五个维度。仅在孩子年龄 >= 10 岁且有 DAN 测评结果时显示。
+
+```html
+<!-- ===== 大五人格 ===== -->
+<view class="section" v-if="isLoggedIn && bigFiveData && showBigFive">
+  <view class="section-header">
+    <text class="section-title">🌟 大五人格</text>
+    <text class="section-subtitle">10岁以上 · 基于DAN测评</text>
+  </view>
+  <RadarChart
+    :dimensions="bigFiveDimensions"
+    :scores="bigFiveScores"
+    :maxScore="100"
+    :label="'大五人格剖面'"
+    :width="600"
+    :height="600" />
+</view>
+```
+
+data 中增加:
+```javascript
+bigFiveData: null,
+showBigFive: false,
+bigFiveDimensions: [
+  { key: 'openness', label: '开放性' },
+  { key: 'conscientiousness', label: '尽责性' },
+  { key: 'extraversion', label: '外向性' },
+  { key: 'agreeableness', label: '宜人性' },
+  { key: 'neuroticism', label: '神经质' }
+],
+bigFiveScores: []
+```
+
+在 methods 中添加 `loadBigFiveData`(从 DAN 测评结果读取):
+```javascript
+loadBigFiveData: function() {
+  if (!this.currentChildId) return
+  var self = this
+  // 从 DAN 测评结果获取大五人格数据
+  uni.request({
+    url: '/api/assessment/dan/big-five',
+    method: 'POST',
+    data: { childId: this.currentChildId },
+    success: function(res) {
+      if (res.data && res.data.code === 200 && res.data.data) {
+        var data = res.data.data
+        self.bigFiveScores = [
+          data.openness || 0,
+          data.conscientiousness || 0,
+          data.extraversion || 0,
+          data.agreeableness || 0,
+          data.neuroticism || 0
+        ]
+        self.showBigFive = true
+      }
+    },
+    fail: function() {
+      self.showBigFive = false
+    }
+  })
+}
+```
+
+- [ ] **Step 8: 新增关系图区块**
+
+在布局顺序中(能量柱 → 雷达图 → 大五 → **关系图** → 任务 → 活动 → 商品),关系图区块展示家庭关系或认知关联概览。具体实现待实施时根据数据可用性确定。
+
+```html
+<!-- ===== 关系图 ===== -->
+<view class="section" v-if="isLoggedIn">
+  <view class="section-header">
+    <text class="section-title">🔗 关系图谱</text>
+  </view>
+  <!-- 待实施时根据后端数据确定具体展示 -->
+  <view class="relationship-placeholder">
+    <text class="placeholder-text">关系数据加载中...</text>
+  </view>
+</view>
+```
+
+- [ ] **Step 9: 调整页面布局顺序**
+
+确保心智页的区块排列遵循:**能量柱 → 认知雷达图 → 大五人格图 → 关系图 → 任务 → 活动 → 商品**
+
+```html
+<!-- 布局顺序(从顶到底) -->
+<FamilyEnergyBar ... />                    <!-- 1. 能量柱 -->
+<RadarChart v-if="... cognitive" ... />    <!-- 2. 认知雷达图 -->
+<RadarChart v-if="... bigFive" ... />     <!-- 3. 大五人格 -->
+<!-- 关系图 -->                              <!-- 4. 关系图 -->
+<DimensionTasks v-if="isLoggedIn" ... />   <!-- 5. 任务 -->
+<DimensionActivities ... />                <!-- 6. 活动 -->
+<DimensionProducts ... />                  <!-- 7. 商品 -->
+```
+
+- [ ] **Step 10: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/mind/index.vue")`
+预期:clean
+
+---
+
+### Task 7: action/index.vue — 关系雷达图 + 游客可见商品/活动 + 修复 funcList 商城路径
+
+**Files:**
+- Modify: `cfc-frontend/pages/action/index.vue`
+
+**设计说明(2026-06-15 确认):** 行远页核心展示六类人际关系雷达图——与孩子、另一半、父母、同伴、师长、领导的关系。数据从 Contact 关系表读取(多维度评分:亲密度、信任度、沟通频率等),使用 `<RadarChart>` 组件渲染。
+
+**Files:**
+- Modify: `cfc-frontend/pages/action/index.vue`
+
+- [ ] **Step 1: DimensionActivities/Products 移除 `v-if="isLoggedIn"`**
+
+修改前(约第 37-56 行):
+```html
+<DimensionTasks v-if="isLoggedIn" ... />
+<DimensionActivities v-if="isLoggedIn" ... />
+<DimensionProducts v-if="isLoggedIn" ... />
+```
+
+修改后:
+```html
+<DimensionTasks v-if="isLoggedIn" ... />
+<DimensionActivities :isLoggedIn="isLoggedIn" ... />
+<DimensionProducts :isLoggedIn="isLoggedIn" ... />
+```
+
+- [ ] **Step 2: onShow 中游客也加载活动和商品数据**
+
+```javascript
+// 同 Task 5 Step 2
+this.loadDimensionActivities()
+this.loadDimensionProducts()
+```
+
+- [ ] **Step 3: 修复 funcList 中商城路径**
+
+修改前(约第 141 行):
+```javascript
+{ icon: '\u{1F6CD}', label: '商城', needLogin: false, page: '/pages/shop/index' }
+```
+
+修改后(确认实际商城入口页路径):
+```javascript
+{ icon: '\u{1F6CD}', label: '商城', needLogin: false, page: '/pages/discover/index?type=product' }
+```
+
+**注:** 需先确认 `pages.json` 中商城实际入口页路径。可能的路径有:
+- `pages/discover/index`(发现页带 type 参数)
+- `pages/shop/detail/detail`
+- 或改为调用 `goProductList` 方法
+
+搜索 `pages.json` 确认正确路径。
+
+- [ ] **Step 4: 新增六类关系雷达图区块**
+
+在页面顶部(FamilyEnergyBar 下方)增加六类人际关系雷达图。每个关系显示为一个雷达图,维度包括:亲密度、信任度、沟通频率、支持度、冲突处理等。
+
+数据来源:Contact 关系表,通过 `GET /api/contact/relationships` 接口获取(需登录)。
+
+```html
+<!-- ===== 六类关系雷达图 ===== -->
+<view class="section" v-if="isLoggedIn">
+  <view class="section-header">
+    <text class="section-title">🤝 人际关系</text>
+  </view>
+  <scroll-view scroll-x class="relationship-scroll" show-scrollbar="false">
+    <view class="relationship-card" v-for="(rel, idx) in relationships" :key="idx" @click="goRelationshipDetail(rel.type)">
+      <view class="rel-header">
+        <text class="rel-icon">{{ rel.icon }}</text>
+        <text class="rel-type">{{ rel.label }}</text>
+      </view>
+      <RadarChart
+        :dimensions="rel.dimensions"
+        :scores="rel.scores"
+        :maxScore="5"
+        :width="280"
+        :height="280"
+        :showLabels="false" />
+      <text class="rel-score">综合 {{ rel.totalScore }}分</text>
+    </view>
+  </scroll-view>
+</view>
+```
+
+在 data 中增加:
+```javascript
+relationships: [],
+relationshipTypes: [
+  { type: 'child',     label: '与孩子的关系', icon: '👶' },
+  { type: 'spouse',    label: '与另一半的关系', icon: '💑' },
+  { type: 'parent',    label: '与父母的关系', icon: '👴' },
+  { type: 'peer',      label: '与同伴的关系', icon: '🤝' },
+  { type: 'teacher',   label: '与师长的关系', icon: '👨‍🏫' },
+  { type: 'leader',    label: '与领导的关系', icon: '👔' }
+]
+```
+
+在 methods 中添加 `loadRelationships`:
+```javascript
+loadRelationships: function() {
+  var self = this
+  // 从 Contact 关系表获取数据
+  uni.request({
+    url: '/api/contact/relationships',
+    method: 'POST',
+    success: function(res) {
+      if (res.data && res.data.code === 200 && res.data.data) {
+        var data = res.data.data
+        self.relationships = self.relationshipTypes.map(function(typeDef) {
+          var relData = data[typeDef.type] || {}
+          return {
+            type: typeDef.type,
+            icon: typeDef.icon,
+            label: typeDef.label,
+            dimensions: [
+              { key: 'intimacy', label: '亲密度' },
+              { key: 'trust', label: '信任度' },
+              { key: 'communication', label: '沟通频率' },
+              { key: 'support', label: '支持度' },
+              { key: 'conflict', label: '冲突处理' }
+            ],
+            scores: [
+              relData.intimacy || 0,
+              relData.trust || 0,
+              relData.communication || 0,
+              relData.support || 0,
+              relData.conflict || 0
+            ],
+            totalScore: relData.totalScore || 0
+          }
+        })
+      }
+    },
+    fail: function() {
+      // API 未就绪时用空数组
+      self.relationships = []
+    }
+  })
+}
+```
+
+在 `onShow` 登录分支中调用 `this.loadRelationships()`。
+
+- [ ] **Step 5: 调整页面布局顺序**
+
+行远页布局顺序:**关系雷达图 → 任务 → 活动 → 商品**
+
+```html
+<!-- 布局顺序(从顶到底) -->
+<FamilyEnergyBar ... />                    <!-- 1. 能量柱 -->
+<!-- 关系雷达图(横向滚动) -->              <!-- 2. 六类关系 -->
+<DimensionTasks v-if="isLoggedIn" ... />   <!-- 3. 任务 -->
+<DimensionActivities ... />                <!-- 4. 活动 -->
+<DimensionProducts ... />                  <!-- 5. 商品 -->
+```
+
+- [ ] **Step 6: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/action/index.vue")`
+预期:clean
+
+---
+
+## Phase 4 — 首页改造 (P1 🟠)
+
+### Task 8: parent-index.vue — 保留沙盘 + 新增 FamilyEnergyBar + 服务商入口 + 维度区块
+
+**Files:**
+- Modify: `cfc-frontend/pages/index/parent-index.vue`
+
+**设计决策(2026-06-15 确认):** 五行沙盘为首页必要组件,**保留不动**。本 Task 只做增量改造——增加 FamilyEnergyBar、服务商快捷入口、维度商品/活动区块。
+
+- [ ] **Step 1: [无需操作] 五行沙盘保留不动**
+
+⚠️ 已有 import `import WuxingSandbox from '@/components/wuxing-sandbox.vue'`、components 注册、模板中的 `<wuxing-sandbox>` 实例全部保留。**不要删除任何沙盘相关代码。**
+
+- [ ] **Step 2: 在 Banner 下方增加 FamilyEnergyBar**
+
+在 banner 或顶部区域后增加:
+```html
+<!-- 家庭能量横条 -->
+<FamilyEnergyBar
+  v-if="hasFamilyData"
+  dimensionCode="parent"
+  :sandboxData="sandboxData" />
+```
+
+添加 import:
+```javascript
+import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'
+```
+
+在 components 中添加 `FamilyEnergyBar`。
+
+- [ ] **Step 3: 增加服务商快捷入口**
+
+在「待处理事项」区域后增加(当 `isTeacher` 为 true 时显示):
+```html
+<!-- ===== 服务商快捷入口 ===== -->
+<view class="teacher-quick-section" v-if="isTeacher">
+  <view class="section-title">🔧 服务商工具</view>
+  <view class="pending-grid">
+    <view class="pending-card" @click="goToTeacherFamilies">
+      <view class="pending-icon pending-icon--blue">
+        <text class="pending-emoji">👨‍👩‍👧</text>
+      </view>
+      <view class="pending-info">
+        <text class="pending-count accent-color">{{ serviceFamilyCount }}</text>
+        <text class="pending-label">服务家庭</text>
+      </view>
+    </view>
+    <view class="pending-card" @click="goToAssignTask">
+      <view class="pending-icon pending-icon--green">
+        <text class="pending-emoji">📝</text>
+      </view>
+      <view class="pending-info">
+        <text class="pending-count">{{ pendingTaskCount }}</text>
+        <text class="pending-label">待办任务</text>
+      </view>
+    </view>
+    <view class="pending-card" @click="goToIncome">
+      <view class="pending-icon pending-icon--gold">
+        <text class="pending-emoji">💰</text>
+      </view>
+      <view class="pending-info">
+        <text class="pending-count">¥{{ todayIncome }}</text>
+        <text class="pending-label">今日收入</text>
+      </view>
+    </view>
+    <view class="pending-card" @click="goToTeam">
+      <view class="pending-icon pending-icon--purple">
+        <text class="pending-emoji">👥</text>
+      </view>
+      <view class="pending-info">
+        <text class="pending-count">{{ teamCount }}</text>
+        <text class="pending-label">团队</text>
+      </view>
+    </view>
+  </view>
+</view>
+```
+
+在 data 中增加字段:
+```javascript
+isTeacher: false,
+serviceFamilyCount: 0,
+pendingTaskCount: 0,
+todayIncome: 0,
+teamCount: 0
+```
+
+在 `onShow` 中检查教师身份并加载对应数据:
+```javascript
+// 检查当前用户角色
+var roles = uni.getStorageSync('roles') || ''
+this.isTeacher = roles.indexOf('teacher') !== -1
+if (this.isTeacher) {
+  this.loadTeacherDashboard()
+}
+```
+
+添加 `loadTeacherDashboard` 方法(调用教师统计 API 或使用默认值)。
+
+- [ ] **Step 4: 增加维度商品/活动推荐区块**
+
+在「成长建议」区域后增加:
+```html
+<!-- ===== 推荐活动 ===== -->
+<DimensionActivities
+  dimensionCode="all"
+  :isLoggedIn="isLoggedIn"
+  :activities="recommendedActivities"
+  @activityClick="goActivityDetail"
+  @moreActivities="goMoreActivities" />
+
+<!-- ===== 推荐商品 ===== -->
+<DimensionProducts
+  dimensionCode="all"
+  :isLoggedIn="isLoggedIn"
+  :products="recommendedProducts"
+  @productClick="goProductDetail"
+  @moreProducts="goMoreProducts" />
+```
+
+添加 import:
+```javascript
+import DimensionActivities from '@/components/DimensionActivities.vue'
+import DimensionProducts from '@/components/DimensionProducts.vue'
+```
+
+在 data 中增加:
+```javascript
+recommendedActivities: [],
+recommendedProducts: []
+```
+
+在 `onShow` 登录分支中调用加载方法。
+
+- [ ] **Step 5: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/index/parent-index.vue")`
+预期:clean
+
+---
+
+### Task 9: child-index.vue — FamilyEnergyBar + 维度商品/活动 + reLaunch→switchTab
+
+**Files:**
+- Modify: `cfc-frontend/pages/index/child-index.vue`
+
+- [ ] **Step 1: 在 Banner 下方增加 FamilyEnergyBar**
+
+```html
+<!-- 家庭能量横条 -->
+<FamilyEnergyBar
+  v-if="hasChildren"
+  dimensionCode="child"
+  :sandboxData="sandboxData" />
+```
+
+添加 import:
+```javascript
+import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'
+```
+
+- [ ] **Step 2: 增加维度商品/活动区块**
+
+在「最近能量变动」下方增加:
+```html
+<!-- ===== 推荐活动 ===== -->
+<DimensionActivities
+  v-if="hasChildren"
+  dimensionCode="child"
+  :isLoggedIn="isLoggedIn"
+  :activities="dimensionActivities"
+  @activityClick="goActivityDetail"
+  @moreActivities="goMoreActivities" />
+
+<!-- ===== 推荐商品 ===== -->
+<DimensionProducts
+  v-if="hasChildren"
+  dimensionCode="child"
+  :isLoggedIn="isLoggedIn"
+  :products="dimensionProducts"
+  @productClick="goProductDetail"
+  @moreProducts="goMoreProducts" />
+```
+
+添加 import:
+```javascript
+import DimensionActivities from '@/components/DimensionActivities.vue'
+import DimensionProducts from '@/components/DimensionProducts.vue'
+```
+
+data 中增加:
+```javascript
+dimensionActivities: [],
+dimensionProducts: []
+```
+
+在 `loadData` 中调用加载方法。
+
+- [ ] **Step 3: reLaunch 改 switchTab(约第 432 行)**
+
+修改前:
+```javascript
+uni.reLaunch({ url: '/pages/index/parent-index' })
+```
+
+修改后:
+```javascript
+uni.switchTab({ url: '/pages/index/index' })
+```
+
+**原因:** `switchTab` 不会销毁当前页面栈,保留 TabBar 底部导航状态。`reLaunch` 会清空页面栈导致异常。
+
+- [ ] **Step 4: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/index/child-index.vue")`
+预期:clean
+
+---
+
+### Task 10: index.vue — 未登录态商品接口置换 + 隐藏价格 + 弹登录
+
+**Files:**
+- Modify: `cfc-frontend/pages/index/index.vue`
+
+- [ ] **Step 1: 未登录态商品列表改用 `/api/product/list`**
+
+找到未登录态的商品加载代码(约第 200 行),确认当前调用方式。如果使用 `productList()`(DanShop旧接口),改为调用 api.js 中封装的 `/api/product/list`:
+
+```javascript
+// 修改前
+import { productList } from '../../utils/api'
+productList(params).then(res => { ... })
+
+// 修改后
+import { getProductList } from '../../utils/api'
+getProductList({ page: 1, limit: 10 }).then(res => {
+  if (res.code === 200) {
+    self.products = res.data.records || res.data.list || []
+  }
+}).catch(e => { console.log(e) })
+```
+
+**注:** 需先确认 `utils/api.js` 中是否有 `getProductList` 方法。如果没有,在 Task 14 中原有方法名不变,只修改后端接口。
+
+- [ ] **Step 2: 未登录态商品隐藏价格**
+
+找到商品卡片模板(约第 44-48 行):
+```html
+<view class="product-price-row">
+  <text class="product-price">{{ formatPriceWithSymbol(item.price) }}</text>
+  <text v-if="item.memberPrice" class="product-member">会员{{ formatPriceWithSymbol(item.memberPrice) }}</text>
+</view>
+```
+
+改为(价格改为整型分→元,引入格式化函数):
+```html
+<view class="product-price-row">
+  <text v-if="isLoggedIn" class="product-price">{{ formatPriceWithSymbol(item.price) }}</text>
+  <text v-if="isLoggedIn && item.memberPrice" class="product-member">会员{{ formatPriceWithSymbol(item.memberPrice) }}</text>
+  <text v-else class="product-price-login">登录查看</text>
+</view>
+```
+
+- [ ] **Step 3: 未登录态商品点击弹登录提示**
+
+修改商品 card 的点击事件(约第 40 行):
+```html
+<!-- 修改前 -->
+<view class="product-card" @click="goDetail(item.id)">
+
+<!-- 修改后 -->
+<view class="product-card" @click="isLoggedIn ? goDetail(item.id) : handleLogin()">
+```
+
+添加 `handleLogin` 方法:
+```javascript
+handleLogin() {
+  uni.showModal({
+    title: '提示',
+    content: '请先登录后查看详情',
+    success: function(res) {
+      if (res.confirm) {
+        uni.navigateTo({
+          url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/index/index')
+        })
+      }
+    }
+  })
+}
+```
+
+- [ ] **Step 4: 确保 onShow 中游客能加载商品**
+
+```javascript
+onShow() {
+  if (!this.isLoggedIn()) {
+    this.currentRole = ''
+    this.loadProducts()  // 游客也加载商品
+    return
+  }
+  this.syncRoleAndTabBar()
+}
+```
+
+- [ ] **Step 5: utils/api.js — 添加商品列表接口方法(如果不存在)**
+
+```javascript
+// 在 api.js 中添加
+export function getProductList(params) {
+  return uni.request({
+    url: config.api('/api/product/list'),
+    method: 'POST',
+    data: params || {}
+  })
+}
+```
+
+- [ ] **Step 6: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/index/index.vue")`
+预期:clean
+
+---
+
+## Phase 5 — 富沛页面重做 (P1 🟠)
+
+### Task 11: pages/wealth/index.vue — 完整重构
+
+**Files:**
+- **重写**: `cfc-frontend/pages/wealth/index.vue`
+
+**设计参照:** `docs/需求分析/底签页面详细设计.md` §5 富沛 (pages 500-593)
+
+**设计布局(已登录态):**
+```
+[Logo] 富沛                    brand-header h:220rpx 深蓝渐变
+丰盈内心,富足生活,厚德载物
+🔥 富沛能量: 1,500
+
+👤 [头像] 张三  家长模式 [切换]  user-card
+🏅 系统积分: 500
+💧 富沛能量: 1,500 ↑较上周+120
+
+📊 财富数据                     section-header
+⭐ 总积分: 2,580
+📈 富沛能量增长趋势              迷你柱状/折线
+[📋 积分记录]
+
+🏅 勋章墙                       section-header
+[已获得][已获得][已获得]         badge-grid 3×3
+[灰色  ][灰色  ][已获得]         (亮色=已获得)
+[查看全部 ›]
+
+📋 功能菜单                     按分组排列
+── 个人 ──
+📝 修改个人资料
+👶 我的孩子            (家长)
+── 财富 ──
+💝 心愿单 / 📢 推广中心
+── 服务 ──
+👑 会员中心 / 🏪 服务商中心
+── 设置 ──
+🔐 设置密码 / 邀请 / 退出登录
+
+📈 成长报告                     section-header(家长可见)
+[👶 小明综合成长报告 ...]        child's name + 综合评分: 78分 ↑+5
+[查看完整报告 ›]
+```
+
+**未登录态布局:**
+```
+[Logo] 富沛                    brand-header(与已登录统一)
+丰盈内心,富足生活,厚德载物
+💧 登录查看富沛能量
+
+         👤
+      登录浠艾福                login-prompt
+  登录后可查看个人资料、积分记录等
+      [登录 / 注册]
+```
+
+- [ ] **Step 1: 重写完整 template**
+
+```html
+<template>
+  <view class="container">
+    <!-- ===== 品牌头部 ===== -->
+    <view class="brand-header">
+      <view class="brand-content">
+        <text class="brand-logo">💧 富沛</text>
+        <text class="brand-tagline">丰盈内心,富足生活,厚德载物</text>
+        <text v-if="isLoggedIn" class="brand-energy">🔥 富沛能量: {{ wealthEnergy }}</text>
+        <text v-else class="brand-energy dim">💧 登录查看富沛能量</text>
+      </view>
+    </view>
+
+    <!-- ===== 未登录态:登录引导 ===== -->
+    <view v-if="!isLoggedIn" class="login-prompt">
+      <view class="prompt-icon">👤</view>
+      <text class="prompt-title">登录浠艾福</text>
+      <text class="prompt-desc">登录后可查看个人资料、积分记录等</text>
+      <button class="login-btn" @click="goLogin">登录 / 注册</button>
+    </view>
+
+    <!-- ===== 已登录态:完整内容 ===== -->
+    <template v-else>
+      <!-- 退出切换按钮 -->
+      <view class="exit-switch" v-if="isSwitchedChild" @click="exitSwitch">
+        <text>🔄 退出切换</text>
+      </view>
+
+      <!-- 用户卡片 -->
+      <view class="user-card">
+        <view class="avatar">{{ avatarText }}</view>
+        <view class="user-info">
+          <view class="nickname">{{ nickname || '未设置昵称' }}</view>
+          <view class="role">{{ roleLabel }}</view>
+          <view class="system-points">🏅 系统积分: {{ systemPoints }}</view>
+          <view class="wealth-energy-line">
+            💧 富沛能量: <text class="energy-value">{{ wealthEnergy }}</text>
+            <text class="energy-trend" v-if="energyTrend !== 0">
+              {{ energyTrend > 0 ? '↑' : '↓' }}较上周{{ Math.abs(energyTrend) }}
+            </text>
+          </view>
+        </view>
+        <button class="btn-switch" v-if="!isSwitchedChild" @click="switchMode">切换</button>
+      </view>
+
+      <!-- 财富数据 -->
+      <view class="section">
+        <view class="section-header">
+          <text class="section-title">📊 财富数据</text>
+        </view>
+        <view class="wealth-card">
+          <view class="wealth-item">
+            <text class="wealth-value">{{ totalPoints }}</text>
+            <text class="wealth-label">总积分</text>
+          </view>
+          <view class="wealth-divider"></view>
+          <view class="wealth-item">
+            <text class="wealth-value">{{ wealthEnergy }}</text>
+            <text class="wealth-label">富沛能量</text>
+          </view>
+          <view class="wealth-divider"></view>
+          <view class="wealth-item" @click="goToPointsLogs">
+            <text class="wealth-value link">📋</text>
+            <text class="wealth-label">积分记录</text>
+          </view>
+        </view>
+        <!-- 能量增长趋势(迷你) -->
+        <view class="energy-trend-bar" v-if="trendDays.length > 0">
+          <view class="trend-item" v-for="(day, idx) in trendDays" :key="idx">
+            <view class="trend-bar" :style="{ height: day.percent + '%', background: '#F97316' }"></view>
+            <text class="trend-label">{{ day.label }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 勋章墙 -->
+      <view class="section">
+        <view class="section-header">
+          <text class="section-title">🏅 勋章墙</text>
+          <text class="section-more" @click="goAllBadges">查看全部 ›</text>
+        </view>
+        <view class="badge-grid">
+          <view class="badge-item" v-for="badge in badges" :key="badge.id">
+            <view :class="['badge-icon-wrap', badge.unlocked ? 'unlocked' : 'locked']">
+              <text class="badge-icon">{{ badge.icon }}</text>
+            </view>
+            <text class="badge-name">{{ badge.name }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 功能菜单(分组) -->
+      <view class="menu-list">
+        <view class="menu-group-title">── 个人 ──</view>
+        <view class="menu-item" @click="goToEditInfo">
+          <text>📝 修改个人资料</text>
+          <text class="arrow">›</text>
+        </view>
+        <view class="menu-item" v-if="role === 'parent'" @click="goToChildren">
+          <text>👶 我的孩子</text>
+          <text class="arrow">›</text>
+        </view>
+        <view class="menu-item" v-if="role === 'parent'" @click="goToFamilyMembers">
+          <text>👨‍👩‍👧‍👦 家庭成员</text>
+          <text class="arrow">›</text>
+        </view>
+
+        <view class="menu-group-title">── 财富 ──</view>
+        <view class="menu-item" @click="goToWishlist">
+          <text>💝 心愿单</text>
+          <text class="arrow">›</text>
+        </view>
+        <view class="menu-item" @click="goToPromotion">
+          <text>📢 推广中心</text>
+          <text class="arrow">›</text>
+        </view>
+
+        <view class="menu-group-title">── 服务 ──</view>
+        <view class="menu-item" @click="goToMembership">
+          <text>👑 会员中心</text>
+          <text class="arrow">›</text>
+        </view>
+        <view class="menu-item" v-if="isVendor" @click="goToVendorCenter">
+          <text>🏪 服务商中心</text>
+          <text class="arrow">›</text>
+        </view>
+
+        <view class="menu-group-title">── 设置 ──</view>
+        <view class="menu-item" @click="showPasswordModal = true">
+          <text>🔐 {{ hasPassword ? '重置密码' : '设置密码' }}</text>
+          <text class="arrow">›</text>
+        </view>
+        <view class="menu-item menu-item-switch" v-if="role === 'parent'">
+          <text>👁 对家庭成员可见</text>
+          <switch :checked="showToFamily === 1" @change="onShowToFamilyChange" color="#5B9BD5" />
+        </view>
+        <button class="menu-item menu-item-btn" open-type="share" @click="inviteFamily" v-if="role === 'parent'">
+          <text>👨‍👩‍👧‍👦 邀请家人</text>
+          <text class="arrow">›</text>
+        </button>
+        <button class="menu-item menu-item-btn" open-type="share" @click="inviteTeacher" v-if="role === 'parent'">
+          <text>👨‍🏫 邀请规划师</text>
+          <text class="arrow">›</text>
+        </button>
+        <view class="menu-item" @click="logout">
+          <text>🚪 退出登录</text>
+          <text class="arrow">›</text>
+        </view>
+      </view>
+
+      <!-- 成长报告(家长可见) -->
+      <view class="section" v-if="role === 'parent' && growthReport">
+        <view class="section-header">
+          <text class="section-title">📈 成长报告</text>
+          <text class="section-more" @click="goGrowthReport">查看完整报告 ›</text>
+        </view>
+        <view class="report-card" @click="goGrowthReport">
+          <view class="report-child-avatar">👶</view>
+          <view class="report-info">
+            <text class="report-child-name">{{ growthReport.childName }}综合成长报告</text>
+            <view class="report-score-row">
+              <text class="report-score-label">综合评分: </text>
+              <text class="report-score-value">{{ growthReport.score }}分</text>
+              <text class="report-score-trend" v-if="growthReport.trend !== 0">
+                {{ growthReport.trend > 0 ? '↑' : '↓' }}{{ Math.abs(growthReport.trend) }}
+              </text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </template>
+
+    <!-- 设置/重置密码弹窗 -->
+    <view class="modal-mask" v-if="showPasswordModal" @click="showPasswordModal = false">
+      <view class="modal" @click.stop>
+        <view class="modal-title">{{ hasPassword ? '重置密码' : '设置密码' }}</view>
+        <view class="form-item" v-if="hasPassword">
+          <input v-model="oldPassword" type="number" password placeholder="输入旧密码" maxlength="6" />
+        </view>
+        <view class="form-item">
+          <input v-model="newPassword" type="number" password placeholder="输入新密码" maxlength="6" />
+        </view>
+        <view class="form-item">
+          <input v-model="confirmPassword" type="number" password placeholder="确认新密码" maxlength="6" />
+        </view>
+        <view class="modal-btns">
+          <button @click="showPasswordModal = false">取消</button>
+          <button class="btn-primary" @click="savePassword">确定</button>
+        </view>
+      </view>
+    </view>
+
+    <view class="bottom-spacer"></view>
+  </view>
+</template>
+```
+
+- [ ] **Step 2: 重写 script**
+
+```javascript
+<script>
+import { getEnergyOverview, getChildren, verifyPassword, setPassword, vendorStatus, getVisibleFamilyMembers, getReferralCode, getReferralSummary, updateMemberVisibility, getPointsBalance } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      isLoggedIn: false,
+      nickname: '',
+      role: 'parent',
+      hasPassword: false,
+      showPasswordModal: false,
+      oldPassword: '',
+      newPassword: '',
+      confirmPassword: '',
+      children: [],
+      isSwitchedChild: false,
+      isVendor: false,
+      showToFamily: 1,
+      // 富沛专属
+      wealthEnergy: 0,
+      energyTrend: 0,
+      totalPoints: 0,
+      systemPoints: 0,
+      trendDays: [],
+      badges: [],
+      growthReport: null
+    }
+  },
+  computed: {
+    roleLabel() {
+      var map = { parent: '家长模式', child: '孩子模式', teacher: '成长规划师模式' }
+      return map[this.role] || '家长模式'
+    },
+    avatarText() {
+      return this.nickname ? this.nickname.charAt(0) : '👤'
+    }
+  },
+  onShow() {
+    var token = uni.getStorageSync('token')
+    this.isLoggedIn = !!token
+    if (!token) return
+
+    var currentRole = uni.getStorageSync('currentRole') || uni.getStorageSync('role') || 'parent'
+    this.role = currentRole
+    this.nickname = this.$store.state.nickname || ''
+    this.hasPassword = !!uni.getStorageSync('passwordSet')
+    this.isSwitchedChild = uni.getStorageSync('isSwitchedChild') || false
+
+    this.loadChildren()
+    this.loadShowToFamily()
+    this.checkVendorStatus()
+    this.loadEnergyData()
+    this.loadBadges()
+    this.loadGrowthReport()
+    this.loadPoints()
+  },
+  methods: {
+    goLogin() {
+      uni.navigateTo({ url: '/pages/login/login' })
+    },
+    async loadChildren() {
+      try {
+        var res = await getChildren()
+        this.children = res.data || []
+      } catch (e) { console.error('获取孩子列表失败', e) }
+    },
+    async loadShowToFamily() {
+      try {
+        var uid = this.$store.state.userId || uni.getStorageSync('userId')
+        var res = await getVisibleFamilyMembers()
+        var members = res.data || []
+        var self = members.find(function(m) { return m.id == uid })
+        if (self) this.showToFamily = self.showToFamily !== 0 ? 1 : 0
+      } catch (e) { console.error('获取可见性设置失败', e) }
+    },
+    async loadEnergyData() {
+      var childId = uni.getStorageSync('currentChildId') || null
+      if (!childId && this.children.length > 0) childId = this.children[0].id
+      if (!childId) return
+      try {
+        var res = await getEnergyOverview(childId)
+        if (res && res.data) {
+          var dims = res.data.dimensions || []
+          var wealthDim = null
+          for (var i = 0; i < dims.length; i++) {
+            if (dims[i].code === 'wealth') { wealthDim = dims[i]; break }
+          }
+          this.wealthEnergy = (wealthDim && wealthDim.score) || 0
+
+          // 增长趋势(模拟数据或从API获取)
+          this.energyTrend = res.data.weeklyTrend || 0
+          this.trendDays = this.buildTrendData(res.data.weeklyLogs)
+        }
+      } catch (e) { console.log('获取能量概览失败', e) }
+    },
+    buildTrendData(weeklyLogs) {
+      // 从API日志构建最近7天趋势柱状图数据
+      var days = ['一', '二', '三', '四', '五', '六', '日']
+      if (weeklyLogs && weeklyLogs.length > 0) {
+        return weeklyLogs.slice(-7).map(function(log, idx) {
+          return { label: days[idx] || '', percent: Math.min(100, log.value || 0) }
+        })
+      }
+      // 降级:空数组
+      return []
+    },
+    async loadBadges() {
+      // 从本地计算(后续可对接 API: GET /api/user/badges?recent=9)
+      var streakDays = parseInt(uni.getStorageSync('streakDays') || 0)
+      var totalPoints = parseInt(uni.getStorageSync('totalPoints') || 0)
+      this.badges = [
+        { id: 1, icon: '\u{1F3C6}', name: '连续7天', unlocked: streakDays >= 7 },
+        { id: 2, icon: '\u{2B50}', name: '积分达人', unlocked: totalPoints >= 1000 },
+        { id: 3, icon: '\u{1F525}', name: '执行之星', unlocked: totalPoints >= 500 },
+        { id: 4, icon: '\u{1F31F}', name: '学习先锋', unlocked: false },
+        { id: 5, icon: '\u{1F4AA}', name: '运动健将', unlocked: false },
+        { id: 6, icon: '\u{1F4DA}', name: '阅读之星', unlocked: false },
+        { id: 7, icon: '\u{1F3AE}', name: '游戏达人', unlocked: false },
+        { id: 8, icon: '\u{1F4BC}', name: '理财能手', unlocked: false },
+        { id: 9, icon: '\u{1F3AF}', name: '全能宝贝', unlocked: false }
+      ]
+    },
+    async loadGrowthReport() {
+      // 后续对接 API: GET /api/growth/report/latest
+      if (this.role === 'parent' && this.children.length > 0) {
+        var childName = this.children[0].nickname || '孩子'
+        this.growthReport = {
+          childName: childName,
+          score: 78,
+          trend: 5
+        }
+      }
+    },
+    async loadPoints() {
+      try {
+        var res = await getPointsBalance()
+        if (res && res.data) {
+          this.totalPoints = res.data.totalPoints || 0
+          this.systemPoints = res.data.systemPoints || res.data.points || 0
+        }
+      } catch (e) { console.log('获取积分失败', e) }
+    },
+    async checkVendorStatus() {
+      try {
+        var res = await vendorStatus()
+        this.isVendor = res.data && res.data.vendorStatus === 'approved'
+      } catch (e) { this.isVendor = false }
+    },
+    // === 导航方法 ===
+    goToEditInfo() { uni.navigateTo({ url: '/pages/user-edit/user-edit' }) },
+    goToChildren() { uni.navigateTo({ url: '/pages/profile/children' }) },
+    goToFamilyMembers() { uni.navigateTo({ url: '/pages/profile/family-members' }) },
+    goToPointsLogs() { uni.navigateTo({ url: '/pages/points/points' }) },
+    goToWishlist() { uni.switchTab({ url: '/pages/rewards/rewards' }) },
+    goToPromotion() { uni.navigateTo({ url: '/pages/promotion/index' }) },
+    goToMembership() { uni.showToast({ title: '即将上线', icon: 'none' }) },
+    goToVendorCenter() { uni.navigateTo({ url: '/pages/vendor/center' }) },
+    goAllBadges() { uni.showToast({ title: '即将上线', icon: 'none' }) },
+    goGrowthReport() { uni.showToast({ title: '即将上线', icon: 'none' }) },
+
+    // === 切换逻辑 ===
+    switchMode() {
+      if (this.role === 'child') {
+        this.verifyPasswordForSwitch(function() { this.doSwitchMode() }.bind(this))
+      } else {
+        this.doSwitchMode()
+      }
+    },
+    doSwitchMode() {
+      var newRole = this.role === 'parent' ? 'child' : 'parent'
+      if (newRole === 'child') {
+        if (this.children.length === 0) {
+          this.loadChildren()
+          if (this.children.length === 0) {
+            uni.showModal({
+              title: '提示',
+              content: '您还没有添加孩子,是否现在去添加?',
+              success: function(res) { if (res.confirm) uni.navigateTo({ url: '/pages/profile/create-child' }) }
+            })
+            return
+          }
+        }
+        if (this.children.length === 1) {
+          this.$store.commit('switchToChild', this.children[0].id)
+          this.role = newRole
+          uni.showToast({ title: '已切换为孩子模式', icon: 'success' })
+          return
+        }
+        uni.showActionSheet({
+          itemList: this.children.map(function(c) { return c.nickname }),
+          success: function(res) {
+            var selectedChild = this.children[res.tapIndex]
+            this.$store.commit('switchToChild', selectedChild.id)
+            this.role = newRole
+            uni.showToast({ title: '已切换为' + selectedChild.nickname + '模式', icon: 'success' })
+          }.bind(this)
+        })
+        return
+      }
+      this.$store.commit('switchBackToParent')
+      this.role = newRole
+      uni.showToast({ title: '已切换为家长模式', icon: 'success' })
+    },
+    verifyPasswordForSwitch(callback) {
+      uni.showModal({
+        title: '验证密码',
+        content: '请输入家长密码',
+        editable: true,
+        success: async function(res) {
+          if (res.confirm && res.content) {
+            try {
+              var result = await verifyPassword(res.content)
+              if (result.code === 200) { callback && callback() }
+              else { uni.showToast({ title: '密码错误', icon: 'none' }) }
+            } catch (e) { uni.showToast({ title: '验证失败', icon: 'none' }) }
+          }
+        }
+      })
+    },
+    async savePassword() {
+      if (this.newPassword.length !== 6) { uni.showToast({ title: '请输入6位密码', icon: 'none' }); return }
+      if (this.newPassword !== this.confirmPassword) { uni.showToast({ title: '两次密码不一致', icon: 'none' }); return }
+      if (this.hasPassword && this.oldPassword.length !== 6) { uni.showToast({ title: '请输入旧密码', icon: 'none' }); return }
+      try {
+        if (this.hasPassword) {
+          var verifyRes = await verifyPassword(this.oldPassword)
+          if (verifyRes.code !== 200) { uni.showToast({ title: '旧密码错误', icon: 'none' }); return }
+        }
+        await setPassword(this.newPassword)
+        uni.setStorageSync('passwordSet', true)
+        this.hasPassword = true
+        uni.showToast({ title: '密码设置成功', icon: 'success' })
+        this.showPasswordModal = false
+        this.oldPassword = ''; this.newPassword = ''; this.confirmPassword = ''
+      } catch (e) { console.error('设置密码失败', e) }
+    },
+    async onShowToFamilyChange(e) {
+      var val = e.detail.value ? 1 : 0
+      var uid = this.$store.state.userId || uni.getStorageSync('userId')
+      try {
+        await updateMemberVisibility(uid, 'parent', val)
+        this.showToFamily = val
+        uni.showToast({ title: val ? '已对家庭成员可见' : '已对家庭成员隐藏', icon: 'none' })
+      } catch (e) { console.error('更新可见性失败', e) }
+    },
+    // === 邀请 ===
+    inviteFamily() {
+      var familyId = uni.getStorageSync('familyId')
+      if (!familyId) { uni.showToast({ title: '您暂未加入家庭', icon: 'none' }); return }
+      this.generateInvite('family', familyId)
+    },
+    inviteTeacher() {
+      var familyId = uni.getStorageSync('familyId')
+      if (!familyId) { uni.showToast({ title: '您暂未加入家庭', icon: 'none' }); return }
+      this.generateInvite('guide', familyId)
+    },
+    async generateInvite(type, familyId) {
+      try {
+        var { generateInviteCard } = require('../../utils/api.js')
+        var res = await generateInviteCard(type, familyId)
+        var code = res.data.code
+        var userInfo = uni.getStorageSync('userInfo')
+        var title = (userInfo && userInfo.nickname ? userInfo.nickname : '家人') + ' 邀请你加入家庭'
+        if (type === 'guide') title = (userInfo && userInfo.nickname ? userInfo.nickname : '家长') + ' 邀请你成为家庭成长规划师'
+        this.shareData = { title: title, path: '/pages/login/login?invite_code=' + code, imageUrl: '/static/invite-card.png' }
+        uni.showToast({ title: '点击右上角转发给TA', icon: 'none' })
+      } catch (e) { uni.showToast({ title: e.message || '生成邀请失败', icon: 'none' }) }
+    },
+    // === 退出 ===
+    logout() {
+      uni.showModal({
+        title: '确认退出',
+        content: '确定要退出登录吗?',
+        success: function(res) {
+          if (res.confirm) {
+            this.$store.commit('logout')
+            uni.reLaunch({ url: '/pages/index/index' })
+          }
+        }.bind(this)
+      })
+    },
+    exitSwitch() {
+      if (!this.isSwitchedChild) return
+      uni.showModal({
+        title: '退出切换',
+        content: '请输入家长密码以确认退出',
+        editable: true,
+        success: async function(res) {
+          if (res.confirm && res.content) {
+            try {
+              var verifyResult = await verifyPassword(res.content)
+              if (verifyResult.code === 200) {
+                this.$store.commit('switchBackToParent')
+                this.isSwitchedChild = false
+                this.role = 'parent'
+                uni.showToast({ title: '已退出切换', icon: 'success' })
+                uni.reLaunch({ url: '/pages/index/index' })
+              } else { uni.showToast({ title: '密码错误', icon: 'none' }) }
+            } catch (e) { uni.showToast({ title: '验证失败', icon: 'none' }) }
+          }
+        }.bind(this)
+      })
+    }
+  }
+}
+</script>
+```
+
+- [ ] **Step 3: 添加样式**
+
+```css
+<style scoped>
+.container {
+  min-height: 100vh;
+  background: #f5f7fa;
+  padding-bottom: 120rpx;
+}
+
+/* 品牌头部 */
+.brand-header {
+  background: linear-gradient(135deg, #1A2A4A 0%, #1E3A5F 100%);
+  padding: 60rpx 40rpx;
+}
+.brand-content {
+  display: flex;
+  flex-direction: column;
+}
+.brand-logo {
+  font-size: 40rpx;
+  font-weight: bold;
+  color: #fff;
+  margin-bottom: 12rpx;
+}
+.brand-tagline {
+  font-size: 24rpx;
+  color: rgba(255,255,255,0.7);
+  margin-bottom: 16rpx;
+}
+.brand-energy {
+  font-size: 32rpx;
+  color: #F97316;
+  font-weight: 600;
+}
+.brand-energy.dim {
+  color: rgba(255,255,255,0.5);
+}
+
+/* 登录引导 */
+.login-prompt {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  min-height: 60vh;
+  padding: 60rpx;
+}
+.prompt-icon {
+  font-size: 120rpx;
+  margin-bottom: 30rpx;
+  width: 160rpx;
+  height: 160rpx;
+  line-height: 160rpx;
+  text-align: center;
+  background: #f5f5f5;
+  border-radius: 50%;
+}
+.prompt-title {
+  font-size: 36rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 16rpx;
+}
+.prompt-desc {
+  font-size: 26rpx;
+  color: #999;
+  text-align: center;
+  margin-bottom: 40rpx;
+}
+.login-btn {
+  width: 60%;
+  height: 80rpx;
+  line-height: 80rpx;
+  background: linear-gradient(135deg, #F97316, #FB923C);
+  color: #fff;
+  font-size: 30rpx;
+  font-weight: bold;
+  border-radius: 40rpx;
+  text-align: center;
+  border: none;
+}
+.login-btn::after { border: none; }
+
+/* 退出切换 */
+.exit-switch {
+  background: #FFF3E0;
+  padding: 20rpx;
+  text-align: center;
+  font-size: 26rpx;
+  color: #F97316;
+}
+
+/* 用户卡片 */
+.user-card {
+  background: linear-gradient(135deg, #1A2A4A 0%, #1E3A5F 100%);
+  border-radius: 20rpx;
+  margin: -30rpx 30rpx 20rpx;
+  padding: 36rpx;
+  display: flex;
+  align-items: center;
+  color: #fff;
+  position: relative;
+  z-index: 2;
+}
+.avatar {
+  font-size: 72rpx;
+  width: 96rpx;
+  height: 96rpx;
+  line-height: 96rpx;
+  text-align: center;
+  background: rgba(255,255,255,0.15);
+  border-radius: 50%;
+  margin-right: 24rpx;
+  overflow: hidden;
+}
+.user-info {
+  flex: 1;
+}
+.nickname {
+  font-size: 32rpx;
+  font-weight: bold;
+}
+.role {
+  font-size: 22rpx;
+  opacity: 0.8;
+  margin-top: 4rpx;
+}
+.system-points {
+  font-size: 22rpx;
+  color: #D6EAF8;
+  margin-top: 8rpx;
+}
+.wealth-energy-line {
+  font-size: 22rpx;
+  margin-top: 6rpx;
+  color: rgba(255,255,255,0.85);
+}
+.energy-value {
+  color: #F97316;
+  font-weight: 600;
+  font-size: 24rpx;
+}
+.energy-trend {
+  margin-left: 8rpx;
+  font-size: 20rpx;
+  color: #4ADE80;
+}
+.btn-switch {
+  background: rgba(255,255,255,0.2);
+  color: #fff;
+  font-size: 24rpx;
+  padding: 10rpx 24rpx;
+  border-radius: 20rpx;
+  border: none;
+}
+
+/* 通用区块 */
+.section {
+  margin: 20rpx 30rpx;
+}
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20rpx;
+}
+.section-title {
+  font-size: 30rpx;
+  font-weight: bold;
+  color: #333;
+}
+.section-more {
+  font-size: 24rpx;
+  color: #999;
+}
+
+/* 财富数据卡片 */
+.wealth-card {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 30rpx 20rpx;
+  display: flex;
+  align-items: center;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.wealth-item {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+.wealth-value {
+  font-size: 36rpx;
+  font-weight: bold;
+  color: #F97316;
+}
+.wealth-value.link {
+  font-size: 40rpx;
+}
+.wealth-label {
+  font-size: 22rpx;
+  color: #999;
+  margin-top: 6rpx;
+}
+.wealth-divider {
+  width: 1rpx;
+  height: 50rpx;
+  background: #f0f0f0;
+}
+
+/* 能量趋势柱状图 */
+.energy-trend-bar {
+  display: flex;
+  align-items: flex-end;
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx 20rpx 16rpx;
+  margin-top: 16rpx;
+  height: 140rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.trend-item {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: flex-end;
+  height: 100%;
+}
+.trend-bar {
+  width: 16rpx;
+  border-radius: 8rpx;
+  min-height: 8rpx;
+}
+.trend-label {
+  font-size: 20rpx;
+  color: #999;
+  margin-top: 8rpx;
+}
+
+/* 勋章墙 */
+.badge-grid {
+  display: flex;
+  flex-wrap: wrap;
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 24rpx 16rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.badge-item {
+  width: 33.33%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin-bottom: 16rpx;
+}
+.badge-icon-wrap {
+  width: 72rpx;
+  height: 72rpx;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 6rpx;
+}
+.badge-icon-wrap.unlocked { background: linear-gradient(135deg, #FBBF24, #F97316); }
+.badge-icon-wrap.locked { background: #f0f0f0; opacity: 0.5; }
+.badge-icon { font-size: 32rpx; }
+.badge-name { font-size: 20rpx; color: #666; }
+
+/* 功能菜单 */
+.menu-list {
+  margin: 20rpx 30rpx;
+  background: #fff;
+  border-radius: 16rpx;
+  overflow: hidden;
+}
+.menu-group-title {
+  padding: 20rpx 30rpx 10rpx;
+  font-size: 24rpx;
+  color: #aaa;
+  letter-spacing: 2rpx;
+}
+.menu-item {
+  padding: 28rpx 30rpx;
+  border-bottom: 1rpx solid #f5f5f5;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 28rpx;
+  color: #333;
+}
+.menu-item:last-child { border-bottom: none; }
+.menu-item-switch { align-items: center; }
+.menu-item-btn {
+  width: 100%;
+  padding: 28rpx 30rpx;
+  border: none;
+  background: transparent;
+  font-size: 28rpx;
+  color: #333;
+  display: flex;
+  justify-content: space-between;
+}
+.menu-item-btn::after { border: none; }
+.arrow { color: #ccc; font-size: 30rpx; }
+
+/* 成长报告 */
+.report-card {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 30rpx 24rpx;
+  display: flex;
+  align-items: center;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.report-child-avatar { font-size: 48rpx; margin-right: 20rpx; }
+.report-info { flex: 1; }
+.report-child-name { font-size: 26rpx; font-weight: 600; color: #333; display: block; margin-bottom: 8rpx; }
+.report-score-row { display: flex; align-items: center; }
+.report-score-label { font-size: 24rpx; color: #999; }
+.report-score-value { font-size: 28rpx; color: #F97316; font-weight: bold; }
+.report-score-trend { font-size: 22rpx; color: #4ADE80; margin-left: 8rpx; }
+
+/* 密码弹窗 */
+.modal-mask {
+  position: fixed; top: 0; left: 0; right: 0; bottom: 0;
+  background: rgba(0,0,0,0.5);
+  display: flex; align-items: center; justify-content: center;
+}
+.modal {
+  background: #fff; border-radius: 20rpx; padding: 40rpx;
+  width: 80%;
+}
+.modal-title { font-size: 32rpx; font-weight: bold; margin-bottom: 30rpx; text-align: center; }
+.form-item { margin-bottom: 20rpx; }
+.form-item input {
+  border: 1rpx solid #ddd; border-radius: 10rpx;
+  padding: 20rpx; font-size: 28rpx;
+}
+.modal-btns { display: flex; gap: 20rpx; margin-top: 30rpx; }
+.modal-btns button { flex: 1; }
+
+.bottom-spacer { height: 40rpx; }
+</style>
+```
+
+**注意:** 此组件需要 `$store` 支持(Vuex)。确认 `pages/wealth/index` 所在的 subPackage 能访问 store(uni-app 的 store 是全局的,可以)。
+
+- [ ] **Step 4: 处理旧 profile 页**
+
+在 `pages/profile/profile.vue` 文件首部添加废弃注释:
+```html
+<!--
+  ⚠️ 废弃说明(2026-06-15):
+  此页面已被 pages/wealth/index 取代,"我的" Tab 已改为"富沛"。
+  保留文件用于旧链接兼容,不通过 TabBar 访问。
+-->
+```
+
+并添加 `onShow` 重定向:
+```javascript
+onShow() {
+  // 旧页面,重定向到新富沛页
+  uni.switchTab({ url: '/pages/wealth/index' })
+  return
+  // ...原有代码注释掉或保留在下方
+}
+```
+
+- [ ] **Step 5: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/pages/wealth/index.vue")`
+预期:clean
+
+---
+
+## Phase 6 — 组件改造 (P1 🟠)
+
+### Task 12: FamilyEnergyBar.vue — 支持 dualDimension 堆叠渲染
+
+**Files:**
+- Modify: `cfc-frontend/components/FamilyEnergyBar.vue`
+
+- [ ] **Step 1: 修改 props,dualDimension 改为 Object 类型**
+
+修改前(约第 38 行):
+```javascript
+props: {
+  dimensionCode: { type: String, default: '' },
+  sandboxData: { type: Object, default: null },
+  dualDimension: { type: String, default: '' }
+}
+```
+
+修改后:
+```javascript
+props: {
+  dimensionCode: { type: String, default: '' },
+  sandboxData: { type: Object, default: null },
+  dualDimension: { type: Object, default: null }
+  // dualDimension 格式: { primary: {code,score,label,color}, secondary: {code,score,label,color} }
+}
+```
+
+- [ ] **Step 2: 添加堆叠柱渲染模板**
+
+在能量柱渲染位置增加堆叠分支:
+```html
+<!-- 堆叠柱(dualDimension) -->
+<view class="energy-bar-stacked" v-if="dualDimension && dualDimension.primary">
+  <view class="stacked-bar-wrapper" @click="onBarClick">
+    <view class="stacked-bar-segment stacked-bar--primary"
+      :style="{ height: primaryPercent + '%', background: dualDimension.primary.color || '#FF6B35' }">
+      <text class="segment-label" v-if="primaryPercent > 15">心 {{ dualDimension.primary.score }}</text>
+    </view>
+    <view class="stacked-bar-segment stacked-bar--secondary"
+      :style="{ height: secondaryPercent + '%', background: dualDimension.secondary.color || '#8B5CF6' }">
+      <text class="segment-label" v-if="secondaryPercent > 15">智 {{ dualDimension.secondary.score }}</text>
+    </view>
+  </view>
+  <view class="stacked-legend">
+    <view class="legend-item"><view class="legend-dot" style="background:#FF6B35"></view><text>心理</text></view>
+    <view class="legend-item"><view class="legend-dot" style="background:#8B5CF6"></view><text>认知</text></view>
+  </view>
+</view>
+```
+
+- [ ] **Step 3: 添加 computed 属性**
+
+```javascript
+computed: {
+  primaryPercent() {
+    if (!this.dualDimension || !this.dualDimension.primary) return 0
+    return Math.min(100, this.dualDimension.primary.score || 0)
+  },
+  secondaryPercent() {
+    if (!this.dualDimension || !this.dualDimension.secondary) return 0
+    return Math.min(100, this.dualDimension.secondary.score || 0)
+  }
+}
+```
+
+- [ ] **Step 4: 添加堆叠柱样式**
+
+```css
+.energy-bar-stacked {
+  margin: 16rpx 0;
+}
+.stacked-bar-wrapper {
+  height: 200rpx;
+  display: flex;
+  flex-direction: column;
+  border-radius: 16rpx;
+  overflow: hidden;
+  background: #f0f0f0;
+}
+.stacked-bar-segment {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: height 0.3s ease;
+}
+.segment-label {
+  color: #fff;
+  font-size: 22rpx;
+  font-weight: 600;
+  text-shadow: 0 1rpx 4rpx rgba(0,0,0,0.3);
+}
+.stacked-legend {
+  display: flex;
+  justify-content: center;
+  gap: 32rpx;
+  margin-top: 12rpx;
+}
+.legend-item {
+  display: flex;
+  align-items: center;
+  gap: 8rpx;
+  font-size: 22rpx;
+  color: #666;
+}
+.legend-dot {
+  width: 16rpx;
+  height: 16rpx;
+  border-radius: 50%;
+}
+```
+
+- [ ] **Step 5: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/components/FamilyEnergyBar.vue")`
+预期:clean
+
+---
+
+### Task 13: DimensionProducts.vue + DimensionActivities.vue — isLoggedIn props
+
+**Files:**
+- Modify: `cfc-frontend/components/DimensionProducts.vue`
+- Modify: `cfc-frontend/components/DimensionActivities.vue`
+
+- [ ] **Step 1: DimensionProducts — 增加 isLoggedIn prop 和价格切换**
+
+修改 `DimensionProducts.vue`:
+
+在 props 中增加:
+```javascript
+props: {
+  // ... 现有 props
+  isLoggedIn: { type: Boolean, default: false }
+}
+```
+
+在价格渲染位置(找到商品价格区域):
+```html
+<!-- 修改前(old: BigDecimal 类型,用 v-if="item.price" 检查) -->
+<view class="product-price" v-if="item.price">¥{{ item.price }}</view>
+
+<!-- 修改后(new: 整型分→元,使用格式化函数) -->
+<view v-if="isLoggedIn && item.price != null" class="product-price">{{ formatPriceWithSymbol(item.price) }}</view>
+<view v-else-if="!isLoggedIn" class="product-price-login">登录查看</view>
+```
+
+在组件 `<script>` 中引入:
+```javascript
+import { formatPriceWithSymbol } from '@/utils/format'
+```
+
+- [ ] **Step 2: DimensionActivities — 增加 isLoggedIn prop 和空态提示切换**
+
+修改 `DimensionActivities.vue`:
+
+在 props 中增加:
+```javascript
+props: {
+  // ... 现有 props
+  isLoggedIn: { type: Boolean, default: false }
+}
+```
+
+在空数据展示处:
+```html
+<!-- 修改前 -->
+<view class="activities-empty" v-if="activities.length === 0">
+  <text class="empty-tip">暂无相关活动</text>
+</view>
+
+<!-- 修改后 -->
+<view class="activities-empty" v-if="activities.length === 0">
+  <text class="empty-tip">{{ isLoggedIn ? '暂无相关活动' : '登录后查看更多活动' }}</text>
+</view>
+```
+
+- [ ] **Step 3: LSP 诊断**
+
+运行:
+```
+lsp_diagnostics(filePath="cfc-frontend/components/DimensionProducts.vue")
+lsp_diagnostics(filePath="cfc-frontend/components/DimensionActivities.vue")
+```
+预期:clean
+
+---
+
+## Phase 7 — 后端改造 (P1 🟠)
+
+### Task 14: ProductController — 游客模式价格返回 null
+
+**Files:**
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/controller/product/ProductController.java`
+
+- [ ] **Step 1: Product 实体 — BigDecimal → Integer(分)**
+
+将 `Product.java` 中的金额字段改为整型:
+
+```java
+// 旧: private BigDecimal price;
+// 新: 金额以"分"为单位存储,前端展示时分/100
+private Integer price;
+
+// 旧: private BigDecimal memberPrice;
+private Integer memberPrice;
+
+// 旧: private BigDecimal profitRate;
+// 新: 利润率以千分比存储(23.5% → 235)
+private Integer profitRate;
+```
+
+对应的 `ProductDTO.java` 也同步修改。
+
+- [ ] **Step 2: 数据库同步 — price/memberPrice 字段改为 INT**
+
+```sql
+-- 1. 新增 INT 列
+ALTER TABLE products
+  ADD COLUMN price_cents INT DEFAULT 0 COMMENT '价格(分)',
+  ADD COLUMN member_price_cents INT DEFAULT 0 COMMENT '会员价(分)',
+  ADD COLUMN profit_rate_permyriad INT DEFAULT 0 COMMENT '利润率(千分比)';
+
+-- 2. 从旧 decimal 字段迁移数据(四舍五入到分)
+UPDATE products SET price_cents = ROUND(price * 100);
+UPDATE products SET member_price_cents = ROUND(member_price * 100);
+UPDATE products SET profit_rate_permyriad = ROUND(profit_rate * 10);
+
+-- 3. 确认迁移无误后,删除旧 decimal 字段
+ALTER TABLE products
+  DROP COLUMN price,
+  DROP COLUMN member_price,
+  DROP COLUMN profit_rate;
+
+-- 4. 重命名新字段
+ALTER TABLE products
+  CHANGE COLUMN price_cents price INT DEFAULT 0 COMMENT '价格(分)',
+  CHANGE COLUMN member_price_cents member_price INT DEFAULT 0 COMMENT '会员价(分)',
+  CHANGE COLUMN profit_rate_permyriad profit_rate INT DEFAULT 0 COMMENT '利润率(千分比)';
+```
+
+> **注意:** 如果数据库中有历史订单(ProductOrder)使用了 `unitPrice`/`totalAmount` 的 decimal 字段,也在本次一并进行改造。因无现成生产环境数据,迁移脚本由实施时按实际情况调整。
+
+- [ ] **Step 3: ProductOrder 实体改造**
+
+```java
+// ProductOrder.java
+// 旧: private BigDecimal unitPrice; private BigDecimal totalAmount;
+// 新:
+private Integer unitPrice;    // 单价(分)
+private Integer totalAmount;  // 总价(分)
+```
+
+```java
+// ProductOrderService.java 中计算逻辑更新
+// 旧: order.setTotalAmount(product.getPrice().multiply(BigDecimal.valueOf(dto.getQuantity())));
+// 新:
+order.setUnitPrice(product.getPrice());
+order.setTotalAmount(product.getPrice() * dto.getQuantity());
+```
+
+- [ ] **Step 4: ProductDTO 输出 — 保持字段名不变,类型统一为 Integer**
+
+```java
+@Data
+public class ProductDTO {
+    private Long id;
+    private String name;
+    private String description;
+    private String coverImage;
+    private Integer price;       // 整型(分),前端展示时 /100
+    private Integer memberPrice; // 整型(分)
+    private Integer profitRate;  // 整型(千分比)
+    // ... 其他字段不变
+}
+```
+
+- [ ] **Step 5: 商品列表接口 — 游客 price 置 null + 返回整型**
+
+```java
+// ProductController.java
+public Result<?> list(@RequestBody(required = false) Map<String, Object> params,
+                       @RequestAttribute(value = "userId", required = false) Long userId) {
+    // ... 正常查询逻辑
+    
+    List<ProductDTO> productList = ...;
+    if (userId == null) {
+        // 游客隐藏价格
+        for (ProductDTO dto : productList) {
+            dto.setPrice(null);
+            dto.setMemberPrice(null);
+        }
+    }
+    // 登录用户:price/memberPrice 返回整型(分),前端自行做 /100 转换
+    return Result.success(productList);
+}
+```
+
+- [ ] **Step 6: 前端价格展示 — 分→元转换**
+
+在所有展示价格的 Vue 页面/组件中,使用统一转换逻辑:
+
+```javascript
+// utils/format.js 新建工具函数
+export function formatPrice(cents) {
+  if (cents == null || cents === undefined) return null
+  return (cents / 100).toFixed(2)
+}
+
+export function formatPriceWithSymbol(cents) {
+  if (cents == null || cents === undefined) return '登录查看'
+  return '¥' + (cents / 100).toFixed(2)
+}
+```
+
+页面中使用:
+```vue
+<text class="product-price">{{ formatPriceWithSymbol(item.price) }}</text>
+```
+
+- [ ] **Step 7: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+### Task 15: Activity 实体 + Controller — 可见性三字段 + 过滤逻辑
+
+**Files:**
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/entity/Activity.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/controller/sncp/ActivityController.java`
+- Create: SQL 变更(DBA 执行或通过 DatabaseInitializer)
+
+- [ ] **Step 1: Activity 实体增加可见性字段**
+
+在 `Activity.java` 中增加:
+
+```java
+/** 可见性:public(公开) / restricted(私密) */
+private String visibility;
+
+/** 可见范围:family(家庭) / role(角色) / user(用户) */
+private String visibleScope;
+
+/** 可见对象标识:家庭ID/角色名/用户ID 的 JSON 数组,如 "[\"family_1\",\"family_2\"]" */
+private String visibleTo;
+```
+
+(使用 Lombok `@Data` 自动生成 getter/setter)
+
+- [ ] **Step 2: 确认数据库表有对应字段**
+
+如果 DatabaseInitializer 或 SQL 中没有,增加:
+```sql
+ALTER TABLE activity
+  ADD COLUMN visibility VARCHAR(20) DEFAULT 'public' COMMENT '可见性: public/restricted',
+  ADD COLUMN visible_scope VARCHAR(20) DEFAULT NULL COMMENT '可见范围: family/role/user',
+  ADD COLUMN visible_to VARCHAR(500) DEFAULT NULL COMMENT '可见对象标识JSON数组';
+```
+
+- [ ] **Step 3: ActivityController 列表查询增加可见性过滤**
+
+找到活动列表查询方法,在构建 `QueryWrapper` 时增加条件:
+
+```java
+// 获取当前用户信息
+Long userId = ...; // 从请求属性获取
+String userRole = ...;
+String familyId = ...;
+
+// 构建查询条件
+QueryWrapper<Activity> queryWrapper = new QueryWrapper<>();
+
+if (userId == null) {
+    // 游客:只返回公开活动
+    queryWrapper.eq("visibility", "public");
+} else {
+    // 登录用户:公开活动 + 自己有权限的私密活动
+    queryWrapper.and(w -> 
+        w.eq("visibility", "public")
+         .or(w2 -> w2.eq("visibility", "restricted")
+             .and(w3 -> {
+                 // family 范围:用户所在家庭
+                 // role 范围:用户角色匹配
+                 // user 范围:用户ID匹配
+                 // 使用 JSON_CONTAINS 或 like 查询 visibleTo
+             })
+         )
+    );
+}
+```
+
+**简化实现(第一期):** 因私密活动逻辑复杂,第一期先实现游客/登录用户的公开活动列表,私密逻辑保留设计但不实现完整过滤,避免阻塞其他功能。
+
+简化后的 ActivityController 逻辑:
+```java
+// 游客只看到公开活动
+if (userId == null) {
+    queryWrapper.eq("visibility", "public");
+}
+// 登录用户看到所有活动(后续迭代完善私密过滤)
+```
+
+- [ ] **Step 4: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+## Phase 8 — RadarChart 组件 + 后端数据 API (P1 🟠)
+
+### Task 17: 新建 RadarChart.vue — 通用 canvas 雷达图组件
+
+**Files:**
+- Create: `cfc-frontend/components/RadarChart.vue`
+
+**用途:** 心智页认知六维雷达图、大五人格图、行远页六类关系雷达图使用。基于 uni-app canvas 绘制,不引入第三方图表库。
+
+- [ ] **Step 1: 创建 RadarChart.vue 组件**
+
+```vue
+<template>
+  <view class="radar-chart-container">
+    <canvas
+      canvas-id="radarCanvas"
+      :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"
+      @error="onError"></canvas>
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    dimensions: { type: Array, default: function() { return [] } },
+    // dimensions: [{ key: 'memory', label: '记忆力' }]
+    scores: { type: Array, default: function() { return [] } },
+    // scores: [85, 70, 90, ...] 与 dimensions 一一对应
+    maxScore: { type: Number, default: 100 },
+    label: { type: String, default: '' },
+    width: { type: Number, default: 500 },
+    height: { type: Number, default: 500 },
+    showLabels: { type: Boolean, default: true },
+    fillColor: { type: String, default: 'rgba(249,115,22,0.2)' },
+    strokeColor: { type: String, default: '#F97316' },
+    gridColor: { type: String, default: '#e0e0e0' }
+  },
+  computed: {
+    canvasWidth() { return this.width },
+    canvasHeight() { return this.height },
+    centerX() { return this.width / 2 },
+    centerY() { return this.height / 2 },
+    radius() { return Math.min(this.width, this.height) / 2 - 40 },
+    count() { return this.dimensions.length }
+  },
+  watch: {
+    scores: { handler: 'draw', immediate: true, deep: true },
+    dimensions: { handler: 'draw', immediate: true, deep: true }
+  },
+  mounted() {
+    // 延迟确保 canvas 渲染完成
+    var self = this
+    setTimeout(function() { self.draw() }, 200)
+  },
+  methods: {
+    draw() {
+      if (!this.dimensions || this.dimensions.length === 0) return
+      if (!this.scores || this.scores.length !== this.dimensions.length) return
+
+      var ctx = uni.createCanvasContext('radarCanvas', this)
+      var n = this.count
+      var angleStep = (Math.PI * 2) / n
+      var startAngle = -Math.PI / 2  // 12点钟方向开始
+
+      // 清空
+      ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
+
+      // 1. 绘制网格(3层同心多边形)
+      var layers = [0.25, 0.5, 0.75, 1.0]
+      for (var l = 0; l < layers.length; l++) {
+        var layerRadius = this.radius * layers[l]
+        ctx.beginPath()
+        for (var i = 0; i < n; i++) {
+          var angle = startAngle + i * angleStep
+          var x = this.centerX + layerRadius * Math.cos(angle)
+          var y = this.centerY + layerRadius * Math.sin(angle)
+          if (i === 0) ctx.moveTo(x, y)
+          else ctx.lineTo(x, y)
+        }
+        ctx.closePath()
+        ctx.setStrokeStyle(this.gridColor)
+        ctx.setLineWidth(1)
+        ctx.stroke()
+      }
+
+      // 2. 绘制轴线
+      for (var i = 0; i < n; i++) {
+        var angle = startAngle + i * angleStep
+        var x = this.centerX + this.radius * Math.cos(angle)
+        var y = this.centerY + this.radius * Math.sin(angle)
+        ctx.beginPath()
+        ctx.moveTo(this.centerX, this.centerY)
+        ctx.lineTo(x, y)
+        ctx.setStrokeStyle(this.gridColor)
+        ctx.setLineWidth(1)
+        ctx.stroke()
+      }
+
+      // 3. 绘制数据区域(填充)
+      ctx.beginPath()
+      var dataPoints = []
+      for (var i = 0; i < n; i++) {
+        var ratio = Math.min(1, (this.scores[i] || 0) / this.maxScore)
+        var angle = startAngle + i * angleStep
+        var r = this.radius * ratio
+        var x = this.centerX + r * Math.cos(angle)
+        var y = this.centerY + r * Math.sin(angle)
+        dataPoints.push({ x: x, y: y })
+        if (i === 0) ctx.moveTo(x, y)
+        else ctx.lineTo(x, y)
+      }
+      ctx.closePath()
+      ctx.setFillStyle(this.fillColor)
+      ctx.setStrokeStyle(this.strokeColor)
+      ctx.setLineWidth(2)
+      ctx.fill()
+      ctx.stroke()
+
+      // 4. 绘制数据点
+      for (var i = 0; i < dataPoints.length; i++) {
+        ctx.beginPath()
+        ctx.arc(dataPoints[i].x, dataPoints[i].y, 4, 0, Math.PI * 2)
+        ctx.setFillStyle(this.strokeColor)
+        ctx.fill()
+      }
+
+      // 5. 绘制标签
+      if (this.showLabels) {
+        ctx.setFontSize(12)
+        ctx.setFillStyle('#333')
+        for (var i = 0; i < n; i++) {
+          var angle = startAngle + i * angleStep
+          var labelRadius = this.radius + 24
+          var x = this.centerX + labelRadius * Math.cos(angle)
+          var y = this.centerY + labelRadius * Math.sin(angle)
+          var label = this.dimensions[i] && this.dimensions[i].label
+          if (label) {
+            ctx.setTextAlign('center')
+            ctx.setTextBaseline('middle')
+            ctx.fillText(label, x, y)
+          }
+        }
+      }
+
+      ctx.draw()
+    },
+    onError(e) {
+      console.error('RadarChart canvas error', e)
+    }
+  }
+}
+</script>
+
+<style>
+.radar-chart-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 20rpx 0;
+}
+</style>
+```
+
+- [ ] **Step 2: 确认组件可用**
+
+在 body/mind/action 任一页面临时引入并渲染,运行 `lsp_diagnostics` 检查无错误,然后在心智页和行远页完整集成。
+
+- [ ] **Step 3: LSP 诊断**
+
+运行:`lsp_diagnostics(filePath="cfc-frontend/components/RadarChart.vue")`
+预期:clean
+
+---
+
+### Task 18: 后端 — 认知能力/大五人格/关系数据 API
+
+**Files:**
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/controller/ContactController.java`
+- Modify: `cfc-backend/.../controller/assessment/AssessmentController.java`(如认知和大五从 DAN 测评结果取)
+- Modify: `cfc-backend/.../entity/Contact.java`(如关系维度评分字段不存在)
+
+- [ ] **Step 1: 认知能力 API — GET /api/assessment/cognitive-profile**
+
+在 `AssessmentController` 中增加认知能力查询接口。从 `DanAssessmentResult` 中提取认知六维度评分:
+
+```java
+@PostMapping("/api/assessment/cognitive-profile")
+public Result<?> getCognitiveProfile(@RequestBody Map<String, Object> params,
+                                      @RequestAttribute("userId") Long userId) {
+    if (userId == null) return Result.error(401, "请先登录");
+    Long childId = params.get("childId") != null ? Long.valueOf(params.get("childId").toString()) : null;
+    if (childId == null) return Result.error(400, "缺少childId");
+
+    // 从 DanAssessmentResult 查询最新测评的认知数据
+    // 实体假设已有 cognitionJson 字段存储 JSON 格式的六个维度评分
+    // 如无此数据,先返回默认结构供前端开发联调
+    Map<String, Object> result = new HashMap<>();
+    result.put("sensation", 78);
+    result.put("attention", 85);
+    result.put("memory", 72);
+    result.put("logic", 90);
+    result.put("spatial", 68);
+    result.put("speed", 82);
+    return Result.success(result);
+}
+```
+
+- [ ] **Step 2: 大五人格 API — GET /api/assessment/dan/big-five**
+
+在 `AssessmentController` 中增加大五人格查询接口:
+
+```java
+@PostMapping("/api/assessment/dan/big-five")
+public Result<?> getBigFive(@RequestBody Map<String, Object> params,
+                             @RequestAttribute("userId") Long userId) {
+    if (userId == null) return Result.error(401, "请先登录");
+    Long childId = params.get("childId") != null ? Long.valueOf(params.get("childId").toString()) : null;
+    if (childId == null) return Result.error(400, "缺少childId");
+
+    // 从 DanAssessmentResult 查询大五人格数据
+    // 先返回默认结构供联调
+    Map<String, Object> result = new HashMap<>();
+    result.put("openness", 65);
+    result.put("conscientiousness", 78);
+    result.put("extraversion", 72);
+    result.put("agreeableness", 85);
+    result.put("neuroticism", 45);
+    return Result.success(result);
+}
+```
+
+- [ ] **Step 3: 关系数据 API — GET /api/contact/relationships**
+
+新建 `ContactController.java`(如不存在),或改造现有 Controller:
+
+```java
+@RestController
+@RequestMapping("/api/contact")
+public class ContactController {
+
+    @Resource
+    private ContactService contactService;
+
+    @PostMapping("/relationships")
+    public Result<?> getRelationships(@RequestAttribute(value = "userId", required = false) Long userId) {
+        if (userId == null) return Result.error(401, "请先登录");
+
+        // 从 Contact 表查询用户的关系数据,按关系类型分组计算各维度平均分
+        Map<String, Object> result = contactService.getRelationshipProfiles(userId);
+        return Result.success(result);
+    }
+}
+```
+
+Contact 实体需要包含的评分字段(如不存在则增加):
+```java
+/** 关系类型:child/spouse/parent/peer/teacher/leader */
+private String relationType;
+/** 亲密度 1-5 */
+private Integer intimacy;
+/** 信任度 1-5 */
+private Integer trust;
+/** 沟通频率 1-5 */
+private Integer communication;
+/** 支持度 1-5 */
+private Integer support;
+/** 冲突处理 1-5 */
+private Integer conflict;
+/** 综合评分 */
+private Integer totalScore;
+```
+
+- [ ] **Step 4: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+## Phase 9 — 验证 (P2)
+
+### Task 19: 完整验证清单
+
+- [ ] **Step 1: 前端 LSP 诊断**
+
+对每个修改过的前端文件运行 `lsp_diagnostics`:
+
+```bash
+# 需检查的文件清单
+cfc-frontend/pages.json
+cfc-frontend/pages/index/index.vue
+cfc-frontend/pages/index/parent-index.vue
+cfc-frontend/pages/index/child-index.vue
+cfc-frontend/pages/body/index.vue
+cfc-frontend/pages/mind/index.vue
+cfc-frontend/pages/action/index.vue
+cfc-frontend/pages/wealth/index.vue
+cfc-frontend/pages/teacher/index.vue
+cfc-frontend/pages/profile/profile.vue
+cfc-frontend/components/FamilyEnergyBar.vue
+cfc-frontend/components/DimensionProducts.vue
+cfc-frontend/components/DimensionActivities.vue
+cfc-frontend/utils/api.js
+```
+
+预期:所有文件 clean(0 error)
+
+- [ ] **Step 2: 后端编译**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+- [ ] **Step 3: 功能验收**
+
+| # | 验收项 | 预期 | 检查结果 |
+|---|--------|------|---------|
+| 1 | TabBar 显示 | 首页 / 身泰 / 心智 / 行远 / 富沛 | |
+| 2 | TabBar 图标 | 5 个 Tab 图标均正常显示 | |
+| 3 | 身泰页游客访问 | 显示活动/商品列表,无价格,显示"登录查看" | |
+| 4 | 身泰页登录后 | 显示价格,任务可见 | |
+| 5 | 心智页布局顺序 | 能量柱 → 认知雷达图 → 大五人格 → 关系图 → 任务 → 活动 → 商品 | |
+| 6 | 心智页能量柱 | 堆叠显示心+智 | |
+| 7 | 心智页认知雷达图 | 6 边形(感知觉/注意力/记忆力/逻辑推理/空间想象/加工速度) | |
+| 8 | 心智页大五人格 | 5 边形 OCEAN,仅 10 岁以上 + 有 DAN 测评时显示 | |
+| 9 | 行远页布局顺序 | 关系雷达图 → 任务 → 活动 → 商品 | |
+| 10 | 行远页关系雷达图 | 6 类关系(孩子/另一半/父母/同伴/师长/领导),各显示五维雷达图 | |
+| 11 | 行远页 funcList | 商城导航到正确页面 | |
+| 12 | 未登录首页 | 显示商品列表(无价格) | |
+| 13 | 未登录点击商品 | 弹登录提示 | |
+| 14 | 家长首页 | 保留五行沙盘 + 新增 FamilyEnergyBar / 服务商入口 / 维度区块 | |
+| 15 | 家长首页服务商入口 | teacher 用户看到服务商工具卡片 | |
+| 16 | 富沛页未登录态 | 品牌头 + 登录引导 | |
+| 17 | 富沛页已登录态 | 品牌头 + 用户卡片 + 财富数据 + 勋章墙 + 分组菜单 + 成长报告 | |
+| 18 | 富沛页菜单分组 | 个人/财富/服务/设置 四组标题 | |
+| 19 | teacher/index 废弃 | 显示废弃提示页 | |
+| 20 | child-index reLaunch | 切换为 switchTab | |
+| 21 | body BASE_URL | 无 localhost:8080 硬编码 | |
+| 22 | RadarChart 组件 | 心智页/行远页均正确渲染 canvas 雷达图 | |
+| 23 | 后端编译 | mvn clean compile -q → BUILD SUCCESS | |
+
+- [ ] **Step 4: git 提交**
+
+```bash
+git status
+git add <相关文件>
+git commit -m "fix: align 5 tab pages implementation with v2.0 design
+
+- TabBar: 身体→身泰, 行动→行远, 我的→富沛
+- Remove teacher-index routing, soft-deprecate teacher/index
+- guest mode: products/activities visible without price
+- mind page: stacked energy bar + cognitive radar chart + Big Five chart
+- action page: 6-type relationship radar charts from Contact table
+- parent-index: keep sandbox, add FamilyEnergyBar + teacher entry + dim blocks
+- child-index: add FamilyEnergyBar + dimension blocks + switchTab
+- wealth page: full redesign matching v2.0 spec
+- new RadarChart canvas component for mind/action pages
+- backend: cognitive/big-five/contact APIs, product guest price, activity visibility"
+```

+ 722 - 0
docs/superpowers/plans/2026-06-15-integer-price-migration.md

@@ -0,0 +1,722 @@
+# 整型金额迁移计划 — 禁止多精度类型
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 消除全栈所有 decimal/double 类型,使用整型(分/千分比)替代,前端统一做展示转换
+
+**架构:** 分 4 阶段推进。P0 核心电商(Product/ProductOrder)→ P1 套餐/活动/服务(GuidePackage/Activity/ServiceContent)→ P2 支付/佣金/会员(PaymentOrder/Commission/Membership)→ P3 评分/配置/先天属性(Teacher rating/Energy ratio/Zodiac/Bazi)。数据库逐表迁移(新增 INT 列→迁移数据→删除旧 DECIMAL 列→重命名)。
+
+**核心原则:**
+- 金额以 **分** 存储(`1元 = 100`),前端展示 `(cents / 100).toFixed(2)`
+- 百分比以 **千分比** 存储(`23.5% = 235`),前端展示 `(permillage / 10) + '%'`
+- 评分以 **百分制整数** 存储(`4.5 = 450`),前端展示 `(value / 100).toFixed(1)`
+- 比例/权重以 **万分比** 存储(`0.15 = 1500`),计算时除以 10000
+- DTO 层字段名不变,类型统一为 `Integer`,前端做转换
+
+**Tech Stack:** Java 8, Spring Boot 2.7, MyBatis-Plus, MySQL 8.0, uni-app Vue 2
+
+---
+
+## 文件改动总览
+
+| 文件 | 操作 | 任务 | 优先级 |
+|------|------|------|--------|
+| `Product.java` | 修改 | T1 | P0 |
+| `ProductDTO.java` | 修改 | T1 | P0 |
+| `ProductOrder.java` | 修改 | T2 | P0 |
+| `ProductOrderDTO.java` | 修改 | T2 | P0 |
+| `ProductOrderService.java` | 修改 | T2 | P0 |
+| `utils/format.js` | **新建** | T3 | P0 |
+| 前端价格展示位置(6 处) | 修改 | T3 | P0 |
+| `DatabaseInitializer.java` | 修改 | T4 | P0 |
+| `AdminCommissionController.java` | 修改 | T4 | P0 |
+| SQL 迁移脚本 | 新增 | T4 | P0 |
+| `Activity.java` / `ActivityDTO.java` | 修改 | T5 | P1 |
+| `ServiceContent.java` / DTOs | 修改 | T5 | P1 |
+| `GuidePackage.java` / DTOs | 修改 | T5 | P1 |
+| 套餐/服务相关 Service | 修改 | T5 | P1 |
+| `PaymentOrder.java` / `PaymentOrderDTO.java` | 修改 | T6 | P2 |
+| `CommissionRecord.java` / DTOs | 修改 | T6 | P2 |
+| `CommissionService.java` | 修改 | T6 | P2 |
+| `WithdrawalRequest.java` / `WithdrawalService.java` | 修改 | T6 | P2 |
+| `MembershipLevel.java` / `MembershipService.java` | 修改 | T6 | P2 |
+| `Guide.java` / `Teacher.java` rating | 修改 | T7 | P3 |
+| `EnergyDimensionConfig.java` / `EnergySourceConfig.java` | 修改 | T7 | P3 |
+| `ZodiacConfig.java` / `BaziConfig.java` / `BloodTypeConfig.java` | 修改 | T8 | P3 |
+| `FamilyMemberAttributes.java` | 修改 | T8 | P3 |
+| `FamilyMemberAttributeService.java` | 修改 | T8 | P3 |
+
+---
+
+## Phase 0 — 工具函数 (P0)
+
+### Task 0: 新建前端金额格式化工具函数
+
+**Files:**
+- Create: `cfc-frontend/utils/format.js`
+
+- [ ] **Step 1: 创建 format.js**
+
+```javascript
+/**
+ * 金额格式化工具
+ * 后端统一使用整型(分)存储金额,前端在此转换展示
+ */
+
+/**
+ * 分 → 元,保留2位小数
+ * @param {number|null} cents 金额(分)
+ * @returns {string|null} "12.50" 或 null
+ */
+export function formatPrice(cents) {
+  if (cents == null || cents === undefined) return null
+  return (cents / 100).toFixed(2)
+}
+
+/**
+ * 分 → 元,带 ¥ 前缀
+ * @param {number|null} cents 金额(分)
+ * @returns {string} "¥12.50" 或 "登录查看"
+ */
+export function formatPriceWithSymbol(cents) {
+  if (cents == null || cents === undefined) return '登录查看'
+  return '¥' + (cents / 100).toFixed(2)
+}
+
+/**
+ * 千分比 → 百分比字符串
+ * @param {number|null} permillage 千分比(235 = 23.5%)
+ * @returns {string} "23.5%" 或 "--"
+ */
+export function formatRate(permillage) {
+  if (permillage == null || permillage === undefined) return '--'
+  return (permillage / 10) + '%'
+}
+
+/**
+ * 百分制评分 → 展示值
+ * @param {number|null} value 百分制(450 = 4.5分)
+ * @param {number} fixed 小数位数
+ * @returns {string} "4.5" 或 "--"
+ */
+export function formatScore(value, fixed) {
+  if (fixed === undefined) fixed = 1
+  if (value == null || value === undefined) return '--'
+  return (value / 100).toFixed(fixed)
+}
+```
+
+- [ ] **Step 2: 编译验证**
+
+运行 `lsp_diagnostics` 确认无语法错误。
+
+---
+
+## Phase 1 — 核心电商金额 (P0 🔴)
+
+### Task 1: Product 实体 + DTO — BigDecimal → Integer(分)
+
+**Files:**
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/entity/Product.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/dto/ProductDTO.java`
+
+- [ ] **Step 1: Product.java 字段类型修改**
+
+```java
+// 修改前
+import java.math.BigDecimal;
+// ...
+private BigDecimal price;
+private BigDecimal memberPrice;
+private BigDecimal profitRate;
+
+// 修改后 — 删除 import java.math.BigDecimal;
+// ...
+private Integer price;       // 价格(分),展示时 /100
+private Integer memberPrice; // 会员价(分)
+private Integer profitRate;  // 利润率(千分比),23.5% → 235
+```
+
+- [ ] **Step 2: ProductDTO.java 同步修改**
+
+```java
+// 删除 import java.math.BigDecimal;
+private Integer price;
+private Integer memberPrice;
+private Integer profitRate;
+```
+
+检查 DTO 中的转换逻辑(如有 `d.price = p.getPrice()` 等),类型改为 `Integer` 后赋值不变(`Integer` → `Integer` 无需转换)。
+
+- [ ] **Step 3: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`(仅改 entity + dto 无编译依赖错误)
+
+---
+
+### Task 2: ProductOrder 实体 + DTO + Service — 整型金额
+
+**Files:**
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/entity/ProductOrder.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/dto/ProductOrderDTO.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/service/ProductOrderService.java`
+
+- [ ] **Step 1: ProductOrder.java 字段类型修改**
+
+```java
+// 删除 import java.math.BigDecimal;
+private Integer unitPrice;    // 单价(分)
+private Integer totalAmount;  // 总价(分)
+```
+
+- [ ] **Step 2: ProductOrderDTO.java 同步修改**
+
+```java
+// 删除 import java.math.BigDecimal;
+private Integer unitPrice;
+private Integer totalAmount;
+```
+
+- [ ] **Step 3: ProductOrderService.java 计算逻辑更新**
+
+```java
+// 修改前 (约第65行):
+order.setTotalAmount(product.getPrice().multiply(BigDecimal.valueOf(dto.getQuantity())));
+
+// 修改后:
+order.setUnitPrice(product.getPrice());
+order.setTotalAmount(product.getPrice() * dto.getQuantity());
+```
+
+- [ ] **Step 4: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+### Task 3: 前端价格展示 — 全部改用 formatPriceWithSymbol
+
+**Files:**
+- Modify: `cfc-frontend/pages/index/index.vue`(未登录态商品列表)
+- Modify: `cfc-frontend/components/DimensionProducts.vue`
+- Modify: `cfc-frontend/pages/body/index.vue`(如有直接价格渲染)
+- Modify: `cfc-frontend/pages/mind/index.vue`(如有直接价格渲染)
+- Modify: `cfc-frontend/pages/action/index.vue`(如有直接价格渲染)
+- Modify: `cfc-frontend/pages/shop/index.vue`(商城价格)
+
+- [ ] **Step 1: 在 utils/api.js 或相关位置引入 format 工具**
+
+在 `main.js` 中全局注册(或在各页面分别 import):
+
+```javascript
+// main.js
+import { formatPriceWithSymbol, formatPrice, formatRate } from '@/utils/format'
+
+// 挂载到 Vue 原型,所有组件可通过 this.formatPriceWithSymbol() 访问
+Vue.prototype.formatPriceWithSymbol = formatPriceWithSymbol
+Vue.prototype.formatPrice = formatPrice
+Vue.prototype.formatRate = formatRate
+```
+
+- [ ] **Step 2: 逐文件替换价格渲染**
+
+对所有前端文件中形如 `¥{{ item.price }}` 的渲染,改为 `{{ formatPriceWithSymbol(item.price) }}`。
+
+**搜索清单(grep 查找 `¥{{` 或 `¥ ` 在 .vue 文件中):**
+
+```bash
+grep -rn '¥' cfc-frontend/pages/ cfc-frontend/components/ --include='*.vue'
+```
+
+每个匹配位置都需修改:
+
+```vue
+<!-- 修改前 -->
+<text class="product-price">¥{{ item.price }}</text>
+
+<!-- 修改后 -->
+<text class="product-price">{{ formatPriceWithSymbol(item.price) }}</text>
+```
+
+- [ ] **Step 3: 检查 v-if 条件**
+
+```vue
+<!-- 修改前 — BigDecimal 用 v-if="item.price" 检查非零 -->
+<view v-if="item.price">¥{{ item.price }}</view>
+
+<!-- 修改后 — Integer 用 != null 检查,0 元商品显示 "¥0.00" 而不是隐藏 -->
+<view v-if="item.price != null">{{ formatPriceWithSymbol(item.price) }}</view>
+
+<!-- 会员价同理 -->
+<text v-if="item.memberPrice != null">会员{{ formatPriceWithSymbol(item.memberPrice) }}</text>
+```
+
+- [ ] **Step 4: LSP 诊断**
+
+运行 `lsp_diagnostics` 确认无语法错误。
+
+---
+
+### Task 4: 后端 Controller + DatabaseInitializer + SQL 迁移
+
+**Files:**
+- Modify: `cfc-backend/.../controller/product/ProductController.java`
+- Modify: `cfc-backend/.../config/DatabaseInitializer.java`
+- Modify: `cfc-backend/.../controller/admin/AdminCommissionController.java`
+- Create: `cfc-backend/src/main/resources/db/migration/V20260615__integer_price.sql`
+
+- [ ] **Step 1: ProductController 价格置 null 逻辑**
+
+```java
+// 游客隐藏价格逻辑不变,类型已改为 Integer
+public Result<?> list(@RequestBody(required = false) Map<String, Object> params,
+                       @RequestAttribute(value = "userId", required = false) Long userId) {
+    // ... 正常查询逻辑
+    
+    List<ProductDTO> productList = ...;
+    if (userId == null) {
+        for (ProductDTO dto : productList) {
+            dto.setPrice(null);
+            dto.setMemberPrice(null);
+        }
+    }
+    return Result.success(productList);
+}
+```
+
+类型可赋值 `null`,逻辑无需改动(只需确认编译通过)。
+
+- [ ] **Step 2: DatabaseInitializer — BigDecimal → Integer**
+
+在 `DatabaseInitializer.java` 中找到种子数据插入(约第 982-986 行):
+
+```java
+// 修改前:
+insertProductSeed("亲子关系工作坊", "...", "activity", new java.math.BigDecimal("388.00"), "action", "all");
+
+// 修改后: 388.00元 = 38800分
+insertProductSeed("亲子关系工作坊", "...", "activity", 38800, "action", "all");
+```
+
+检查 `insertProductSeed` 方法签名,将参数类型从 `java.math.BigDecimal` 改为 `Integer`(或确认重载版本)。
+
+```java
+// 方法签名修改前:
+private void insertProductSeed(String name, String desc, String type, java.math.BigDecimal price, String domain, String budget) {
+
+// 方法签名修改后:
+private void insertProductSeed(String name, String desc, String type, Integer price, String domain, String budget) {
+```
+
+所有种子数据价格转换:
+| 原值 | 分 |
+|------|---|
+| 388.00 | 38800 |
+| 199.00 | 19900 |
+| 599.00 | 59900 |
+| 99.00 | 9900 |
+| 500.00 | 50000 |
+
+- [ ] **Step 3: AdminCommissionController 整型化**
+
+```java
+// 修改前 (约第61-71行):
+BigDecimal profitRate = params.get("profitRate") != null
+    ? new BigDecimal(params.get("profitRate").toString())
+    : null;
+if (productId == null || profitRate == null) {
+    return Result.error("productId和profitRate不能为空");
+}
+// ...
+product.setProfitRate(profitRate);
+
+// 修改后:
+Integer profitRate = params.get("profitRate") != null
+    ? Integer.valueOf(params.get("profitRate").toString())
+    : null;
+if (productId == null || profitRate == null) {
+    return Result.error("productId和profitRate不能为空");
+}
+// ...
+product.setProfitRate(profitRate);
+```
+
+- [ ] **Step 4: 数据库迁移 SQL**
+
+```sql
+-- V20260615__integer_price.sql
+
+-- ========================================
+-- Phase 1: 核心电商表
+-- ========================================
+
+-- products: price, member_price, profit_rate
+ALTER TABLE products
+  ADD COLUMN price_int INT DEFAULT 0 COMMENT '价格(分)',
+  ADD COLUMN member_price_int INT DEFAULT 0 COMMENT '会员价(分)',
+  ADD COLUMN profit_rate_int INT DEFAULT 0 COMMENT '利润率(千分比)';
+
+UPDATE products SET price_int = ROUND(price * 100);
+UPDATE products SET member_price_int = ROUND(member_price * 100);
+UPDATE products SET profit_rate_int = ROUND(profit_rate * 10);
+
+ALTER TABLE products
+  DROP COLUMN price,
+  DROP COLUMN member_price,
+  DROP COLUMN profit_rate;
+
+ALTER TABLE products
+  CHANGE COLUMN price_int price INT DEFAULT 0 COMMENT '价格(分)',
+  CHANGE COLUMN member_price_int member_price INT DEFAULT 0 COMMENT '会员价(分)',
+  CHANGE COLUMN profit_rate_int profit_rate INT DEFAULT 0 COMMENT '利润率(千分比)';
+
+-- product_orders: unit_price, total_amount
+ALTER TABLE product_orders
+  ADD COLUMN unit_price_int INT DEFAULT 0 COMMENT '单价(分)',
+  ADD COLUMN total_amount_int INT DEFAULT 0 COMMENT '总价(分)';
+
+UPDATE product_orders SET unit_price_int = ROUND(unit_price * 100);
+UPDATE product_orders SET total_amount_int = ROUND(total_amount * 100);
+
+ALTER TABLE product_orders
+  DROP COLUMN unit_price,
+  DROP COLUMN total_amount;
+
+ALTER TABLE product_orders
+  CHANGE COLUMN unit_price_int unit_price INT DEFAULT 0 COMMENT '单价(分)',
+  CHANGE COLUMN total_amount_int total_amount INT DEFAULT 0 COMMENT '总价(分)';
+```
+
+- [ ] **Step 5: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+## Phase 2 — 活动/套餐/服务价格 (P1 🟠)
+
+### Task 5: Activity + ServiceContent + GuidePackage 整型化
+
+**Files:**
+- Modify: `cfc-backend/.../entity/Activity.java`(price)
+- Modify: `cfc-backend/.../dto/ActivityDTO.java`(price)
+- Modify: `cfc-backend/.../entity/ServiceContent.java`(price)
+- Modify: `cfc-backend/.../entity/GuidePackage.java`(price, commissionRate)
+- Modify: `cfc-backend/.../entity/GuidePackageTemplate.java`(referencePrice, minPrice)
+- Modify: `cfc-backend/.../entity/TaskTemplatePackage.java`(price, platformFee)
+- Modify: `cfc-backend/.../entity/Guide.java`(commissionRate)
+- Modify: `cfc-backend/.../entity/GuideFamily.java`(servicePrice)
+- Modify: `cfc-backend/.../entity/GuideApplication.java`(paymentAmount)
+- Modify: 上述实体对应的所有 DTO 文件
+- Modify: 相关 Controller/Service(调整 `new BigDecimal()` → `Integer.valueOf()`)
+
+- [ ] **Step 1: 实体字段逐类修改**
+
+每个实体执行同样的模式:
+
+```java
+// Activity.java — 例
+// 删除 import java.math.BigDecimal;
+private Integer price;  // 价格(分)
+```
+
+DTO 同步修改,注意 DTO 中的转换赋值不变(`Integer` ↔ `Integer`)。
+
+- [ ] **Step 2: Controller 中 BigDecimal 构造替换**
+
+搜索所有 Controller 中 `new BigDecimal(params.get("price").toString())` 模式:
+
+```bash
+grep -rn 'new BigDecimal' cfc-backend/src/main/java/com/etotem/cfc/controller/
+```
+
+将每个匹配位置改为 `Integer.valueOf()` 或 `Integer.parseInt()`:
+
+```java
+// 修改前:
+java.math.BigDecimal price = new java.math.BigDecimal(params.get("price").toString());
+
+// 修改后:
+Integer price = params.get("price") != null
+    ? Integer.valueOf(params.get("price").toString())
+    : null;
+```
+
+- [ ] **Step 3: Service 中的金额计算调整**
+
+搜索 Service 中使用 `BigDecimal` 进行金额计算的代码:
+
+```bash
+grep -rn '\.multiply\|\.divide\|\.add\|\.subtract\|new BigDecimal' cfc-backend/src/main/java/com/etotem/cfc/service/ --include='*.java'
+```
+
+需要重点调整的位置:
+
+**TaskTemplatePackageService.java:**
+```java
+// 修改前:
+pkg.setPlatformFee(dto.getPrice().multiply(new BigDecimal("0.10")));
+// 修改后: 平台服务费 = 价格 × 10%(结果以分存储)
+pkg.setPlatformFee(dto.getPrice() * 10 / 100);
+```
+
+**GuidePackageService.java:**
+```java
+// 方法签名:
+// 旧: boolean addGuidePackage(Long guideId, Long packageId, BigDecimal price, BigDecimal commissionRate)
+// 新: boolean addGuidePackage(Long guideId, Long packageId, Integer price, Integer commissionRate)
+```
+
+**BindInviteService.java:**
+```java
+// 旧: gf.setServicePrice(BigDecimal.ZERO);
+// 新:
+gf.setServicePrice(0);
+```
+
+- [ ] **Step 4: 数据库迁移 SQL**
+
+```sql
+-- Phase 2 SQL
+-- activities.price
+ALTER TABLE activity ADD COLUMN price_int INT DEFAULT 0;
+UPDATE activity SET price_int = ROUND(price * 100);
+ALTER TABLE activity DROP COLUMN price;
+ALTER TABLE activity CHANGE COLUMN price_int price INT DEFAULT 0;
+
+-- guide_packages.price, commission_rate
+ALTER TABLE guide_packages ADD COLUMN price_int INT DEFAULT 0, ADD COLUMN commission_rate_int INT DEFAULT 0;
+UPDATE guide_packages SET price_int = ROUND(price * 100), commission_rate_int = ROUND(commission_rate * 1000);
+ALTER TABLE guide_packages DROP COLUMN price, DROP COLUMN commission_rate;
+ALTER TABLE guide_packages CHANGE COLUMN price_int price INT DEFAULT 0, CHANGE COLUMN commission_rate_int commission_rate INT DEFAULT 0;
+
+-- 后续表同理...
+```
+
+- [ ] **Step 5: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+## Phase 3 — 支付/佣金/会员 (P2 🟡)
+
+### Task 6: PaymentOrder + Commission + Membership + Withdrawal
+
+**Files:**
+- Modify: `PaymentOrder.java`, `CommissionRecord.java`, `WithdrawalRequest.java`, `MembershipLevel.java`, `FamilyMembership.java`, `FinanceCheckin.java`, `InsurancePolicy.java`
+- Modify: 上述实体的 DTO
+- Modify: `CommissionService.java`(整型化全部计算逻辑)
+- Modify: `MembershipService.java`
+- Modify: `WithdrawalService.java`
+- Modify: `PackagePaymentService.java`
+- Modify: `PackagePurchaseService.java`
+
+> **注意:** Phase 3 涉及复杂的分佣计算(CommissionService 约 470 行,大量 BigDecimal 计算),**改造风险较高**。建议的改造策略:
+> 1. 所有金额字段改为整型(分)
+> 2. 百分比/费率字段改为万分比(1% = 100bps)
+> 3. 计算时:`amount * rate_bps / 10000`(整型除法,避免精度问题)
+> 4. 顺序:先改 Entity/DTO 保证编译通过 → 再逐个 Service 改造 → 最后改测试
+
+- [ ] **Step 1: Entity + DTO 逐类修改**
+
+按 Task 1/2 的模式,逐个文件将 `BigDecimal` 字段改为 `Integer`。
+
+- [ ] **Step 2: 佣金计算逻辑重构(CommissionService.java)**
+
+将浮动百分比改为 BPS(基点)模式:
+
+```java
+// 旧模式:BigDecimal 乘除
+private static final BigDecimal PROFIT_COMMISSION_RATE = new BigDecimal("0.30");
+l1Amount = orderAmount.multiply(profitRate).divide(new BigDecimal("100"));
+
+// 新模式:整型 BPS(基点)
+private static final int PROFIT_COMMISSION_RATE_BPS = 3000; // 30%
+// 分润 = 订单金额(分) × 利润率(千分比) × 佣金比例(bps) / 10000 / 1000
+// 简化: l1Amount = orderAmount * profitRatePermillage * rateBps / 10000000
+```
+
+> 注意:CommissionService 的计算涉及多层乘除,整型化后需特别注意溢出(用 `long` 中间值)。建议先在 `CommissionService.java` 中引入 `SafeMath` 辅助方法。
+
+- [ ] **Step 3: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+- [ ] **Step 4: 数据库迁移 SQL**
+
+```sql
+-- Phase 3: payment_orders.amount
+ALTER TABLE payment_orders ADD COLUMN amount_int INT DEFAULT 0;
+UPDATE payment_orders SET amount_int = ROUND(amount * 100);
+ALTER TABLE payment_orders DROP COLUMN amount;
+ALTER TABLE payment_orders CHANGE COLUMN amount_int amount INT DEFAULT 0;
+
+-- commission_records.order_amount, profit_rate, commission_amount
+ALTER TABLE commission_records ADD COLUMN order_amount_int INT DEFAULT 0, ADD COLUMN profit_rate_int INT DEFAULT 0, ADD COLUMN commission_amount_int INT DEFAULT 0;
+UPDATE commission_records SET order_amount_int = ROUND(order_amount * 100), profit_rate_int = ROUND(profit_rate * 1000), commission_amount_int = ROUND(commission_amount * 100);
+ALTER TABLE commission_records DROP COLUMN order_amount, DROP COLUMN profit_rate, DROP COLUMN commission_amount;
+ALTER TABLE commission_records CHANGE COLUMN order_amount_int order_amount INT DEFAULT 0, CHANGE COLUMN profit_rate_int profit_rate INT DEFAULT 0, CHANGE COLUMN commission_amount_int commission_amount INT DEFAULT 0;
+
+-- 其他表同理...
+```
+
+---
+
+## Phase 4 — 评分/配置/先天属性 (P3 🟢)
+
+### Task 7: Teacher rating + Energy 配置
+
+**Files:**
+- Modify: `Teacher.java`, `TeacherDTO.java`(rating: 百分制整数, 4.5 = 450)
+- Modify: `EnergyDimensionConfig.java`(weight: 万分比, 0.25 = 2500)
+- Modify: `EnergySourceConfig.java`(ratio: 万分比)
+- Modify: `EnergyService.java`(计算逻辑整型化)
+- Modify: `AssessmentPointsConfig.java`(pointsValueYuan: 分)
+- Modify: `AssessmentService.java`
+
+- [ ] **Step 1: 实体字段整型化**
+
+```java
+// Teacher.java
+// 旧: private java.math.BigDecimal rating;  // 评分
+// 新:
+private Integer rating;  // 评分(百分制),4.5分 → 450
+
+// EnergyDimensionConfig.java
+// 旧: private BigDecimal weight;
+// 新:
+private Integer weight;  // 权重(万分比),0.25 → 2500
+
+// EnergySourceConfig.java
+// 旧: private BigDecimal ratio;
+// 新:
+private Integer ratio;   // 比例(万分比)
+
+// AssessmentPointsConfig.java
+// 旧: private BigDecimal pointsValueYuan;
+// 新:
+private Integer pointsValueYuan;  // 分值(分)
+```
+
+- [ ] **Step 2: EnergyService 计算逻辑重构**
+
+`EnergyService.java` 中的 `resolveDimensionRatios` 和 `calculateAllocations` 使用 `BigDecimal` 进行比例计算:
+
+```java
+// 旧:
+Map<Long, BigDecimal> ratios = ...;  // <dimId, weight>
+BigDecimal totalRatio = ratios.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
+BigDecimal share = BigDecimal.valueOf(total).multiply(entry.getValue()).divide(totalRatio, 0, RoundingMode.DOWN);
+
+// 新:
+Map<Long, Integer> ratios = ...;  // <dimId, weight-in-permyriad>
+long totalRatio = ratios.values().stream().mapToLong(Integer::longValue).sum();
+long share = (long) total * entry.getValue() / totalRatio;  // 整型运算
+```
+
+- [ ] **Step 3: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+### Task 8: 先天属性 (Zodiac/Bazi/BloodType/FamilyMemberAttributes)
+
+**Files:**
+- Modify: `ZodiacConfig.java`, `BaziConfig.java`, `BloodTypeConfig.java`(所有 `BigDecimal` → `Integer` 万分比)
+- Modify: `FamilyMemberAttributes.java`(mindBaseScore/wisdomBaseScore: 百分制整数)
+- Modify: `FamilyMemberAttributeService.java`(`calcInnateScore` 整型化)
+
+- [ ] **Step 1: 配置实体整型化**
+
+```java
+// ZodiacConfig.java / BaziConfig.java / BloodTypeConfig.java
+// 旧: private BigDecimal mindBase;  // 基数 0.85
+// 新:
+private Integer mindBase;     // 万分比基数,0.85 → 8500
+private Integer wisdomBase;   // 万分比
+private Integer bodyModifier; // 万分比
+private Integer actionModifier;
+private Integer wealthModifier;
+```
+
+- [ ] **Step 2: FamilyMemberAttributeService 计算重构**
+
+`calcInnateScore` 方法中大量使用 `BigDecimal` 乘除:
+
+```java
+// 旧:
+BigDecimal bloodScore = BigDecimal.ZERO;
+BigDecimal result = zodiacScore.multiply(BigDecimal.valueOf(0.5))
+    .add(baziScore.multiply(BigDecimal.valueOf(0.3)))
+    .add(bloodScore.multiply(BigDecimal.valueOf(0.2)));
+
+// 新: 权重使用千分比,结果使用百分制整数
+// zodiac占比50% = 500‰, bazi 30% = 300‰, blood 20% = 200‰
+int result = (zodiacScore * 500 + baziScore * 300 + bloodScore * 200) / 1000;
+```
+
+- [ ] **Step 3: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile -q
+```
+预期:`BUILD SUCCESS`
+
+---
+
+## 验证总表
+
+| # | 验证项 | 预期 | 检查方法 |
+|---|--------|------|----------|
+| 1 | 后端编译 | BUILD SUCCESS | `mvn clean compile -q` |
+| 2 | entity 中无 BigDecimal | 0 个 import java.math.BigDecimal | `grep -r 'import java.math.BigDecimal' src/main/java/com/etotem/cfc/entity/ \| wc -l` |
+| 3 | dto 中无 BigDecimal | 0 个 import | `grep -r 'import java.math.BigDecimal' src/main/java/com/etotem/cfc/dto/ \| wc -l` |
+| 4 | 前端无 ¥{{ 直接渲染 | 0 个匹配 | `grep -rn '¥{{' cfc-frontend/pages/ cfc-frontend/components/` |
+| 5 | 金额展示使用 formatPriceWithSymbol | 所有 ¥ 前缀位置使用函数 | 人工审查 |
+| 6 | 数据库无 DECIMAL 列 | 0 个 DECIMAL | `SHOW COLUMNS FROM products;` 等 |
+| 7 | 端到端价格展示 | 商品价格显示 ¥388.00 | 小程序截图对比 |
+| 8 | 端到端游客价格 | 显示"登录查看" | 未登录状态浏览 |
+
+---
+
+## 外置参考 — 全部 BigDecimal 出现位置
+
+执行以下命令确认最终清理状态:
+
+```bash
+# 后端全部 BigDecimal 引用(期望最终为 0)
+grep -rn 'import java.math.BigDecimal' cfc-backend/src/main/java/ --include='*.java'
+
+# 前端全部 ¥ 渲染(期望最终只剩通过 formatPriceWithSymbol 的调用)
+grep -rn '¥' cfc-frontend/pages/ cfc-frontend/components/ --include='*.vue'
+```
+
+## 迁移风险矩阵
+
+| 风险 | 影响范围 | 缓解措施 |
+|------|----------|----------|
+| Integer 溢出(金额*BPS 计算) | CommissionService | 中间值使用 `long`/`Long`,关键点加注释 |
+| 精度损失(除不尽) | EnergyService 分配 | 大数优先分配,余数补到最大维度 |
+| seed 数据价格不对 | DatabaseInitializer | 逐行双检(388.00→38800) |
+| 前端漏改 | 所有 Tab 页 | `grep ¥{{` + 人工审查 |
+| 历史数据迁移失败 | 生产库 | 迁移前备份,执行后 SELECT 抽样对比 |

+ 235 - 0
docs/superpowers/plans/2026-06-15-parent-homepage-overhaul.md

@@ -0,0 +1,235 @@
+# 家长首页全面整改计划
+
+> **Status:** Draft
+> **Goal:** 修复数据断层、重构视觉布局、提升加载体验
+> **涉及模块:** cfc-backend, cfc-frontend
+
+---
+
+## 一、P0 — 功能性 Bug(必须修复)
+
+### 1.1 `familyId` 数据断层
+
+**问题:** `ParentDashboardController.getDashboard()` 不返回 `familyId`,但前端第 483-485 行试图读取 `dashData.familyId`,结果为 `undefined`。这导致第 527 行 `getPendingWishes(this._familyId)` 发送 null 请求。
+
+**改动:**
+- **后端** `ParentDashboardController.java`:在 result map 中添加 `familyId`
+  ```java
+  // 获取当前用户的 familyId
+  Long familyId = userService.getUserFamilyId(userId);
+  result.put("familyId", familyId);
+  ```
+- **前端** `api.js`:`getPendingWishes()` 请求体加 `familyId` 判空保护,或 fallback 到独立 API 获取
+
+**涉及文件:**
+- `cfc-backend/.../controller/parent/ParentDashboardController.java`
+- `cfc-backend/.../service/UserService.java`(可能需要新增 `getUserFamilyId` 方法)
+- `cfc-frontend/pages/index/parent-index.vue`
+
+### 1.2 CSS Grid 不兼容微信小程序
+
+**问题:** 第 1219-1222 行 `.pending-grid` 使用 `display: grid`,微信小程序不支持。
+
+**改动:** 将 `.pending-grid` 改为 flexbox 布局:
+```css
+.pending-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16rpx;
+}
+.pending-card {
+  width: calc(50% - 8rpx);  /* 2 列 */
+}
+```
+
+**涉及文件:**
+- `cfc-frontend/pages/index/parent-index.vue`
+
+### 1.3 成长建议区块无数据
+
+**问题:** `growthAdvice[]` 在 data 中声明(第 386 行),`v-for` 渲染(第 279-293 行),但整个 `loadData()` 中没有任何 API 调用填充它。该区块要么永远不显示,要么报错。
+
+**改动(二选一):**
+- **方案 A**:删除该区块(如果无实际数据源)
+- **方案 B**:从 dashboard API 或新增 `/api/growth/advice` 接口获取数据
+
+**涉及文件:**
+- `cfc-frontend/pages/index/parent-index.vue`
+
+---
+
+## 二、P1 — 数据准确性
+
+### 2.1 待完成任务标题是假的
+
+**问题:** 第 588-596 行根据 `pendingTasks` 数量生成 "待完成任务 1/2/3" 这类占位文本。
+
+**改动:** 从后端获取真实任务列表。有两个方案:
+- **方案 A(推荐)**:`getChildCompletionStats` 返回值增加 `pendingTaskList[]`(含 id, title),或直接用已有的 `getPendingReviewTasks` 数据筛选未完成的任务
+- **方案 B**:新建轻量接口 `/api/tasks/pending-by-child?childId=X` 返回待完成任务列表
+
+**涉及文件:**
+- `cfc-backend/.../service/TaskStatsService.java`(扩展返回数据)
+- `cfc-frontend/pages/index/parent-index.vue`
+
+### 2.2 "全家平均" 不包含家长
+
+**问题:** `familyAvgCompletionRate`(第 86 行 `totalCompletionRate / childCount`)只计算了孩子的平均完成率,不含家长自己的完成率。
+
+**改动:** 后端计算时将家长自己的完成率纳入平均:
+```java
+// 家长 + 所有孩子
+int totalRate = parentCompletionRate + totalCompletionRate;
+int avgRate = childCount > 0 ? totalRate / (childCount + 1) : parentCompletionRate;
+result.put("avgCompletionRate", avgRate);
+```
+或前端直接展示为"孩子平均"并保留"自己的",同时展示"全家平均 = (家长 + Σ孩子) / (1+孩子数)"。
+
+**涉及文件:**
+- `cfc-backend/.../controller/parent/ParentDashboardController.java`
+- `cfc-frontend/pages/index/parent-index.vue`(label 文案调整)
+
+### 2.3 降级模式下 myPoints 错误
+
+**问题:** 当 dashboard 降级(第 566-581 行),第 578 行 `this.myPoints = targetChild.totalPoints || 0` 把"自己的积分"设置成了孩子的积分,而非家长的。
+
+**改动:** 降级时 `myPoints` 应从当前用户的 `userInfo` 缓存中读取:
+```javascript
+var userInfo = uni.getStorageSync('userInfo')
+this.myPoints = (userInfo && userInfo.totalPoints) || 0
+```
+
+**涉及文件:**
+- `cfc-frontend/pages/index/parent-index.vue`
+
+### 2.4 能量概览不加载
+
+**问题:** 第 397 行 `currentChildId: ''`(空字符串),第 613 行 `if (overviewChildId)` 条件不成立,导致 `getEnergyOverview` 永远不会被调用。
+
+**改动:** 加载完 `selectedChild` 后设置 `currentChildId`:
+```javascript
+// 在 dashboardLoaded 成功后或降级加载 children 后:
+this.currentChildId = (this.selectedChild && this.selectedChild.id) || ''
+```
+
+**涉及文件:**
+- `cfc-frontend/pages/index/parent-index.vue`
+
+---
+
+## 三、P2 — 用户体验重构
+
+### 3.1 区块精简与重组
+
+**问题:** 11 个区块堆叠,用户需要大量滚动。核心数据(统计 + AI)埋得太深。
+
+**改动方案:**
+
+```
+当前顺序:
+  Banner → 成员条 → 孩子卡片 → 能量沙盘 → 会员Banner → 成长数据(2卡) → AI入口 → 今日重点 → 里程碑 → 待办网格 → 建议+审核+快捷
+
+建议重组(压缩至 6 区块):
+  [第1屏] Banner + 成员条
+  [第2屏] 孩子卡片 + 快捷操作(融合)
+  [第3屏] 成长数据(2卡) + AI 助手入口(融合在一行)
+  [第4屏] 能量沙盘(折叠式,默认收起,点击展开)
+  [第5屏] 今日重点 + 待办网格(合并为"今日概要")
+  [第6屏] 待审核任务(保留,可滑动删除)
+```
+
+**关键改动:**
+1. **AI 入口上移**:从第 7 位移到第 3 位,与 stat cards 同行或紧随其后
+2. **能量沙盘可折叠**:默认展示概要,展开显示完整沙盘
+3. **删除**:会员 Banner(无实际功能)、成长建议(无数据源)
+4. **合并**:"待审核任务"独立区块 → 并入"待办网格"中展开
+5. **快捷操作下移**:改为悬浮固定在底部或作为 TabBar 扩展
+
+**涉及文件:**
+- `cfc-frontend/pages/index/parent-index.vue`
+
+### 3.2 动画 class 去重
+
+**问题:** `animate-stagger-9` 在第 279 行(成长建议)和第 320 行(快捷操作)重复使用。
+
+**改动:** 重新编号,确保整个模板内 stagger 编号唯一。
+
+**涉及文件:**
+- `cfc-frontend/pages/index/parent-index.vue`
+
+---
+
+## 四、P3 — 加载体验优化
+
+### 4.1 骨架屏 / 分区加载
+
+**问题:** 当前一次性 `BaseLoading` 等待所有 API 完成后才显示内容。dashboard + 多个独立 API 串行导致白屏时间长。
+
+**改动:** 拆分为分区加载:
+1. 立即显示骨架框架(内容占位)
+2. 每个区块的 API 完成后独立更新
+3. 首个数据(dashboard)到达后优先渲染孩子卡片 + stat cards
+4. 能量沙盘、成员条等次级数据后加载
+
+### 4.2 下拉刷新
+
+**改动:** 添加 `onPullDownRefresh` 生命周期钩子:
+```javascript
+onPullDownRefresh() {
+  this.loadData().finally(function() {
+    uni.stopPullDownRefresh()
+  })
+}
+```
+并在 `pages.json` 对应页面配置 `"enablePullDownRefresh": true`。
+
+**涉及文件:**
+- `cfc-frontend/pages/index/parent-index.vue`
+- `cfc-frontend/pages.json`
+
+### 4.3 错误状态 UI
+
+**问题:** 所有 API 失败仅 `console.log`,用户无感知。
+
+**改动:** 每个主要区块添加错误兜底状态:
+- Stat cards 失败 → 显示 `--` 或 "数据加载失败"
+- 孩子列表失败 → 显示 "点击重试" 按钮
+- 能量沙盘失败 → 隐藏该区块
+
+---
+
+## 五、执行顺序与工作量估算
+
+```
+Phase 1 — 基础修复(1-2 人天)
+  ├── P0-1: familyId 数据断层       ← 后端 0.5d + 前端 0.2d
+  ├── P0-2: CSS Grid → Flex         ← 前端 0.1d
+  ├── P0-3: 成长建议区块处理        ← 前端 0.1d
+  └── P1-34: 降级/能量初始化        ← 前端 0.3d
+
+Phase 2 — 数据准确性(1 人天)
+  ├── P1-1: 待完成任务真实标题      ← 后端 0.3d + 前端 0.3d
+  ├── P1-2: 全家平均含家长          ← 后端 0.1d + 前端 0.1d
+  └── P4-2: 下拉刷新                ← 前端 0.2d
+
+Phase 3 — UI 重构(2-3 人天)
+  ├── P2-1: 区块精简重组            ← 前端 1-2d(含测试)
+  ├── P2-2: 动画 class 去重         ← 前端 0.1d
+  ├── P4-1: 骨架屏                  ← 前端 0.5d
+  └── P4-3: 错误状态 UI             ← 前端 0.3d
+```
+
+**优先执行 Phase 1**(修复现有功能断裂),**Phase 2** 视数据重要性决定是否并行,**Phase 3** 为视觉/体验升级。
+
+---
+
+## 六、涉及文件清单
+
+| # | 文件 | 改动量 | Phase |
+|---|------|--------|-------|
+| 1 | `cfc-backend/.../ParentDashboardController.java` | +10 行 | 1 |
+| 2 | `cfc-backend/.../service/UserService.java` | 新增 `getUserFamilyId` | 1 |
+| 3 | `cfc-backend/.../service/TaskStatsService.java` | 扩展返回 `pendingTaskList` | 2 |
+| 4 | `cfc-frontend/pages/index/parent-index.vue` | 100-300 行变更 | 1/2/3 |
+| 5 | `cfc-frontend/pages.json` | +3 行 (pullRefresh) | 2 |
+| 6 | `cfc-frontend/utils/api.js` | 可能微调 | 1/2 |

+ 242 - 0
docs/精准营养-slide内容方案.md

@@ -0,0 +1,242 @@
+# 精准营养 Slide 内容方案
+
+## 概述
+
+基于匿名用户 530010234 的肠道菌群健康风险评估报告(募极生物,2026-01-06),提炼"精准营养"主题的 Slide 内容,纳入浠艾福"身体"维度报告。
+
+**素材来源:** `docs/参考资料/匿名 530010234 肠菌健康评估报告.pdf`(135页)
+**截图素材:** `docs/screenshots/policy/01-cover.png` ~ `10-diet-recommendations.png`
+
+---
+
+## Slide 1:封面 — 精准营养:基于肠道菌群的个性化健康方案
+
+**配图:** `01-cover.png`
+
+| 元素 | 文案 |
+|------|------|
+| 大标题 | **精准营养** |
+| 副标题 | 基于肠道菌群的个性化健康管理方案 |
+| 标注 | 从"千人一面"到"千人千面" |
+| 来源 | 基于 530010234 真实检测案例 |
+
+**设计建议:** 封面页左侧大标题 + 右侧报告封面截图做半透明叠加。橙色调(#F97316)为主色。
+
+---
+
+## Slide 2:为什么精准营养?—— 肠道菌群决定营养需求
+
+**配图:** `08-bacteria-balance.png` + `09-bacteria-diversity.png`
+
+**核心论点:**
+
+```
+传统营养学:基于人群平均值的推荐摄入量(RDA)
+      ↓
+精准营养:基于个体肠道菌群构成 + 代谢能力的个性化方案
+```
+
+**关键数据引用:**
+
+- 每个人肠道菌群构成独一无二(如同指纹)
+- 肠道菌群影响:营养吸收率 / 维生素合成 / 氨基酸代谢 / 短链脂肪酸产生
+- 530010234 案例:菌群平衡 31 分(中等)/ 菌群多样性 37 分(中等多样)
+  - 共检出 **313 种菌**(人群均值约 800 种)
+  - 香农多样性指数 **4.49**(人群均值 4.6)
+  - 提示:菌种数量偏少,微生态稳定性不足
+
+**设计建议:** 左半屏显示"传统 vs 精准"对比图,右半屏显示菌群多样性评分截图。箭头连接:检测 → 分析 → 方案。
+
+---
+
+## Slide 3:真实案例概览——匿名 530010234
+
+**配图:** `02-score-dashboard.png`
+
+**核心数据:**
+
+```
+┌──────────────────────────────────────────┐
+│          健康总分:73 / 100               │
+│          (健康但需注意生活方式)         │
+├────────────────┬─────────────────────────┤
+│  菌群健康:53  │  慢病控制:85            │
+│  营养均衡:68  │  肠道年龄:1.37岁        │
+│  肠型:拟杆菌型 │  报告日期:2026-01-06   │
+├────────────────┴─────────────────────────┤
+│  评分等级:                                │
+│  ≥85 优秀 │ 75~85 良好 │ 60~75 一般       │
+│  40~59 及格 │ <40 不及格                  │
+│  人群平均值 75 分 │ 数据库最高 93 分       │
+└──────────────────────────────────────────┘
+```
+
+**关键解读:**
+- 总分 73 分(略低于人群均值 75 分)→ 处于"一般"区间上限
+- 营养均衡 68 分是短板(低于人群平均)→ 精准营养干预空间大
+- 肠道年龄 1.37 岁 + 拟杆菌型 → 擅长分解动物蛋白/脂肪/简单碳水化合物
+
+**设计建议:** 大数字展示总分 73(中心突出),三个子维度用进度条横向排列。截图放在右下角作为证据水印。
+
+---
+
+## Slide 4:个性化发现(一)—— 营养缺乏
+
+**配图:** `04-nutrition-overview.png` + `05-vitamins.png`
+
+**三大缺乏指标:**
+
+| 指标 | 分值 | 正常范围 | 解读 |
+|------|------|---------|------|
+| **维生素 B12** | **14** ↓ | >50 | 偏低 → 疲劳、贫血、神经系统风险 |
+| **苏氨酸(Threonine)** | **14** ↓ | >50 | 偏低 → 免疫屏障薄弱、肠道黏液蛋白合成不足 |
+| **丙氨酸(Alanine)** | **9** ↓ | >50 | 显著偏低 → 能量代谢异常、糖异生受阻 |
+| 其他氨基酸(亮/赖/蛋/苯丙/缬等) | 50-94 | 正常 | 正常范围 |
+
+**机制说明:**
+- B12 缺乏 → 同型半胱氨酸升高 → 心血管风险 ↑
+- 苏氨酸是肠道黏液层核心组分(MUC2 黏蛋白的前体)→ 缺乏意味着肠道屏障功能受损
+- 丙氨酸在葡萄糖-丙氨酸循环中起关键作用 → 缺低下运动耐力可能下降
+
+**设计建议:** 三个指标用"警示卡片"排列,每张卡片左侧显示分值(红字),右侧显示对应的营养功能+缺乏症状。B12 用大号字突出(最易干预,效果最快)。
+
+---
+
+## Slide 5:个性化发现(二)—— 菌群风险
+
+**配图:** `03-disease-risk.png`
+
+**疾病风险评估:**
+
+```
+┌─────────────────────────────────────────────┐
+│  疾病            风险值    等级              │
+├─────────────────────────────────────────────┤
+│  感染性腹泻      0.41     ⚠️ 需注意         │
+│  炎症性肠炎      0.28     ✅ 低风险         │
+│  肠易激综合征    0.13     ✅ 低风险         │
+│  自闭症          0.10     ✅ 低风险         │
+│  肺部疾病        0.13     ✅ 低风险         │
+└─────────────────────────────────────────────┘
+```
+
+**菌群结构风险:**
+
+| 维度 | 分值 | 评估 |
+|------|------|------|
+| 有害菌 | **78** ⚠️ | 较多 |
+| 核心菌属 | **77** ⚠️ | 偏少 |
+| 肠道菌群平衡 | 31 | 中等(接近轻微失衡) |
+| 对甲酚(p-Cresol) | **89** ⚠️ | 过多(尿毒症毒素) |
+| 多巴胺 | **98** ⚠️ | 过多(精神分裂/上瘾风险) |
+
+**解读:** 有害菌比例偏高 + 核心有益菌(粪杆菌、双歧杆菌)偏少 → 感染性腹泻风险上升 → 需要通过膳食纤维 + 发酵食品定向干预。
+
+**设计建议:** 顶部为疾病风险矩阵(绿/黄/红三色标签),底部为菌群结构雷达图或条形图。箭头指向"干预方向"。
+
+---
+
+## Slide 6:个性化饮食方案
+
+**配图:** `10-diet-recommendations.png`
+
+**基于拟杆菌型的四步干预策略:**
+
+### ① 增加膳食纤维 — 25-30g/天
+```
+目标:促进核心有益菌(粪杆菌、双歧杆菌)增殖
+推荐食物:
+  全谷物:燕麦、糙米(推荐指数 52-54)
+  杂豆:鹰嘴豆、黑豆、扁豆(推荐指数 54-59)
+  绿叶菜:菠菜、西兰花(推荐指数 55-61)
+  低糖水果:苹果、蓝莓
+```
+
+### ② 优选发酵蛋白
+```
+目标:补充益生菌 + 调节微环境
+推荐:无糖酸奶(含双歧杆菌)、纳豆(含芽孢杆菌)、低盐泡菜
+限制:过量红肉(推荐指数 -13 至 5)
+避免:加工肉类(培根 -12、肉丸 -12)
+```
+
+### ③ 控制高风险食物
+```
+减少:加工食品(香肠、罐头)、高糖零食、酒精
+避免:反式脂肪(油炸食品)、滥用抗生素
+```
+
+### ④ 食物选择指南(推荐指数对照)
+
+| 推荐等级 | 示例 | 推荐指数 |
+|---------|------|---------|
+| ✅ 强烈推荐 | 牛蒡根、生姜、南瓜、四季豆 | 55-68 |
+| ✅ 推荐 | 大白菜、白萝卜、秋葵、甘薯 | 43-61 |
+| ⚠️ 适量 | 瘦肉(猪/牛/鸡)、鸡蛋 | 1-9 |
+| ❌ 限制 | 肥肉、培根、肉丸、鸡汤 | -13 至 -6 |
+
+**设计建议:** 四象限布局(左上:多吃 / 右上:适量 / 左下:少吃 / 右下:避免),每个象限列出代表性食物+推荐指数。中间放置"拟杆菌型"标签。可用食物图标增加视觉吸引力。
+
+---
+
+## Slide 7:浠艾福精准营养闭环
+
+**配图:**(可选:健康能量系统截图或平台架构图)
+
+### 精准营养闭环
+
+```
+  ① 肠道菌群检测
+     (合作实验室 / 居家采样)
+          ↓
+  ② AI 智能分析
+     (菌群构成 + 代谢通路 + 缺乏评估)
+          ↓
+  ③ 个性化方案生成
+     (基于肠型的饮食计划 + 营养素补充建议)
+          ↓
+  ④ 健康能量系统驱动执行
+     (任务下发 + 积分激励 + 进度追踪)
+          ↓
+  ⑤ 周期性复测 & 方案迭代
+     (效果评估 + 方案调优)
+```
+
+### 平台价值
+
+| 维度 | 浠艾福能力 |
+|------|-----------|
+| **检测** | 对接专业检测机构,居家采样、报告自动同步 |
+| **分析** | AI 解读报告,自动标注关键风险与缺乏指标 |
+| **方案** | 基于肠型 + 缺乏指标生成个性化饮食计划 |
+| **执行** | 健康能量积分驱动,任务系统落地执行 |
+| **跟踪** | 周期性复测提醒,效果可视化对比 |
+
+**设计建议:** 圆形闭环流程图,中心为"精准营养"标题。外圈 5 个节点依次连接,每个节点配一个图标。底部展示健康能量的积分流转示意。
+
+---
+
+## 附录:可用截图清单
+
+| 文件名 | 用途 | 建议位置 |
+|--------|------|---------|
+| `01-cover.png` | 报告封面 | Slide 1 背景 |
+| `02-score-dashboard.png` | 评分总览 | Slide 3 主图 |
+| `03-disease-risk.png` | 疾病风险评估 | Slide 5 主图 |
+| `04-nutrition-overview.png` | 氨基酸评估 | Slide 4 辅助 |
+| `05-vitamins.png` | 维生素评估 | Slide 4 主图 |
+| `06-health-score.png` | 健康总分说明 | 附录 |
+| `07-gut-age.png` | 肠道年龄 | Slide 3 辅助 |
+| `08-bacteria-balance.png` | 菌群平衡 | Slide 2 辅助 |
+| `09-bacteria-diversity.png` | 菌群多样性 | Slide 2 主图 |
+| `10-diet-recommendations.png` | 食物推荐表 | Slide 6 辅助 |
+
+---
+
+## 制作建议
+
+1. **风格统一:** 延续浠艾福五维"身体"维度视觉风格(绿色/橙色系),与主报告模板一致
+2. **数据优先:** 每页聚焦 3-5 个关键数字,避免信息过载
+3. **截图做证据:** 原始报告截图放在右下角或作为浮层水印,增加可信度
+4. **案例贯穿:** 以 530010234 这一个案贯穿全部 7 页,形成完整叙事线
+5. **行动导向:** Slide 6-7 强调"可执行"——看完就知道怎么做

+ 759 - 0
docs/融资计划书-PPT内容方案.md

@@ -0,0 +1,759 @@
+# 浠艾福(Care Family)融资计划书 — PPT 内容方案
+
+> **用途:** 天使轮 / Pre-A 轮融资路演
+> **目标投资人:** 中国家庭教育与健康赛道投资机构
+> **公司:** 亿图腾(北京)科技有限公司
+> **品牌:** 浠艾福(Care Family)— 家庭健康成长俱乐部
+> **版本:** v1.0 · 2026年6月
+
+---
+
+## Slide 1:封面 — 浠艾福:家庭健康成长平台
+
+**页面标题:** 浠艾福(Care Family)
+**副标题:** 给全家的一站式幸福提案
+
+**核心文案:**
+- 家庭健康成长俱乐部 — 身 · 心 · 智 · 行 · 富
+- 连接家长、孩子、成长规划师的三方平台
+- 亿图腾(北京)科技有限公司 | 天使轮融资
+
+**关键数据:**
+- 50+ 小程序页面 | 48 个后端控制器 | 15 大功能模块
+- 五维全景幸福矩阵:身泰 · 心怡 · 智达 · 行远 · 富沛
+
+**视觉建议:**
+- 全屏暖橙色(#F97316)渐变背景,左侧品牌文字竖排
+- 右侧展示品牌 Logo + 小程序首页截图(家长端 + 孩子端双屏并列)
+- 底部五维图标序列(🌏🔥⚔️🌿💧)横向排列
+- 使用 Claymorphism 风格卡片突出儿童友好感
+
+**演讲备注:**
+"大家好,我是亿图腾的创始人。今天给大家带来我们打磨了多年的产品——浠艾福。它不是一个简单的教育App,而是一个以家庭为单位的健康成长平台。我们的使命,是给中国家庭一个真正一站式、全维度的幸福方案。"
+
+---
+
+## Slide 2:市场痛点 — 中国家庭的成长焦虑
+
+**页面标题:** 万亿级市场背后的三大痛点
+
+**核心文案:**
+
+痛点一:信息碎片化,缺乏全局方案
+- 健康检查在医院,心理辅导在机构,课外培训在不同地方
+- 家长需要对接 5-8 个不同服务商,信息完全不打通
+- 没有一个平台能给出"这个孩子到底怎么样"的全景图
+
+痛点二:孩子内驱力不足,家长管理疲惫
+- 78% 的家长认为孩子缺乏自主学习动力(中国教育科学研究院)
+- 说教式管理引发亲子对抗,越管越僵
+- 缺乏有效的激励机制和可视化成长反馈
+
+痛点三:优质服务商难找,服务效果难评估
+- 成长规划师、心理咨询师、体能教练——服务分散,质量参差
+- 家长无从判断服务效果,缺少数据支撑的评估体系
+- 好的服务商获客成本高,缺乏专业展示平台
+
+**关键数据:**
+- 中国家庭教育消费市场规模:2025 年预计突破 **4.5 万亿元**(艾瑞咨询)
+- 3-18 岁儿童家庭数:约 **1.2 亿**
+- 家长年均教育支出:**1.2-3 万元**/孩子
+- 75% 家长同时使用 3 个以上教育/健康类 App
+
+**视觉建议:**
+- 左侧三分栏展示三大痛点,每个痛点配图标(碎片/冲突/搜寻)
+- 右侧展示市场规模数据图(柱状图:2019→2025 增长趋势)
+- 底部用灰线标注:市场虽大,尚无统一的家庭健康成长平台
+
+**演讲备注:**
+"我们做了大量用户调研。家长们最常说的一个词是'累'——不是不爱孩子,而是信息太多、选择太多、没有头绪。市面上的产品要么只做作业辅导,要么只做体测报告,没有人把'一个孩子到底需要什么'这件事完整地回答出来。"
+
+---
+
+## Slide 3:解决方案 — 五维全景幸福矩阵
+
+**页面标题:** 浠艾福的答案:身 · 心 · 智 · 行 · 富
+
+**核心文案:**
+
+| 维度 | 品牌标签 | 解决的问题 | 核心能力 |
+|:----:|:--------:|-----------|---------|
+| 🌏 身 | 身泰 | 体质健康、精准营养 | 肠道菌群检测 + 健康打卡 + 体能计划 |
+| 🔥 心 | 心怡 | 情绪管理、心理健康 | DAN 测评 + 心理报告 + 亲子沟通 |
+| ⚔️ 智 | 智达 | 认知提升、学习能力 | 认知测评 + 小游戏训练(舒尔特/数独) |
+| 🌿 行 | 行远 | 行动力、习惯养成 | 任务系统 + 积分激励 + 行为打卡 |
+| 💧 富 | 富沛 | 财商素养、价值创造 | 财富打卡 + 保单管理 + 创客成长 |
+
+**五维能量系统:**
+- **家庭能量沙盘(评分系统 0-100%)**:基于任务、游戏、测评、阅读、打卡等行为数据,每天自动计算家庭五维健康指数
+- **五维能量账本(计量系统)**:孩子完成任务获得能量积分,兑换心愿,形成"努力→成长→奖励"的完整闭环
+- 五行相生链:智(金)→富(水)→行(木)→心(火)→身(土),符合中国传统文化认知
+
+**关键数据:**
+- 5 个维度 × 3-8 个数据源/维度 = **25+ 个数据源**参与评分计算
+- 日上限机制:全局 200 能量/天,防止刷分
+- 超过 10 种能量来源:任务、游戏、测评、阅读、打卡、活动、课程、商品、咨询、签到
+
+**视觉建议:**
+- 中央五角星图,五个顶点对应五维,按五行相生顺序排列
+- 每个维度配专属颜色(土棕 #8D6E63 / 火橙 #FF6B35 / 金黄 #FFD700 / 木绿 #4CAF50 / 水蓝 #42A5F5)
+- 底部展示能量沙盘 App 截图(家庭健康指数看板)
+- 动态箭头展示"任务→能量→成长→心愿"流转路径
+
+**演讲备注:**
+"五维系统是我们最核心的差异化。市面上的健康产品只看身体,教育产品只看分数。但一个孩子的成长是立体的——身体好了但心理出问题,分数高了但缺乏行动力,这都不是真正的幸福。我们想用一个统一的框架,把家庭健康的所有维度整合在一起。"
+
+---
+
+## Slide 4:产品矩阵 — 三端联动
+
+**页面标题:** 三端产品矩阵:家长 App · 孩子 App · 管理后台
+
+**核心文案:**
+
+**一端:微信小程序 — 家长端**
+- 双角色系统:家长/孩子一键切换,退出孩子端需密码验证(安全屏障)
+- 任务管理:创建任务 → 设置积分 → 审核完成 → 全流程管控
+- 测评预约:选择规划师 → 孩子快照 → 支付 → 查看报告
+- 套餐市场:浏览成长规划师发布的套餐,一键购买
+- 家庭能量看板:全家五维健康指数一目了然
+
+**二端:微信小程序 — 孩子端**
+- 儿童友好的 Claymorphism 设计语言,暖橙色主题
+- 任务执行:接收任务、上传完成凭证、查看积分到账
+- 小游戏:舒尔特方格、猜数字(1A2B)、数独 — 训练注意力与逻辑
+- 心愿兑换:创建心愿 → 等待家长审批 → 消耗积分兑换
+- 成长报告:查看自己的五维能量变化趋势
+
+**三端:Web 管理后台**
+- 运营管理:用户管理、家庭审核、系统配置
+- 服务商管理:入驻审核、套餐审核、订单管理
+- 数据统计:运营数据看板、用户增长分析
+- 四级地址管理:省市区街道级联,支持区域回退匹配
+
+**关键数据:**
+- **50+** 小程序页面 | **4** 个 TabBar(首页/身体/心智/行动/我的)
+- **13** 个服务商端页面(规划师中心/家庭管理/团队/订单/消息)
+- **29** 个 Web 管理端页面 | **48** 个后端控制器
+- 支持角色:`parent` / `child` / `teacher` / `admin` + 多角色字段 `roles`
+
+**视觉建议:**
+- 三列并排展示:左(家长端截图)/ 中(孩子端截图)/ 右(管理后台截图)
+- 每列下方标注核心功能点
+- 家长端强调管理功能,孩子端强调色彩与趣味,管理端强调数据与效率
+- 底部用连线展示三端数据流转关系
+
+**演讲备注:**
+"三端设计不是炫技,是基于我们对每个角色的深度理解。家长需要掌控感和安全感,孩子需要趣味性和成就感,运营方需要效率和数据。一套架构服务三类完全不同需求的人群——这是我们从用户研究到产品落地的核心能力。"
+
+---
+
+## Slide 5:AI 赋能 — 智能家庭助手
+
+**页面标题:** AI 家庭助手 + 智能报告分析
+
+**核心文案:**
+
+**AI 家庭助手(已上线)**
+- 基于 Dify Chatbot 引擎,自动注入家庭上下文
+- 家长可直接询问:"小宝最近身体维度怎么样?""推荐一个适合周末的亲子活动"
+- 系统自动读取家庭五维能量数据、任务完成情况、测评结果,生成个性化回答
+- 支持多轮对话,记忆历史会话
+
+**AI 应用场景矩阵:**
+
+| 场景 | 当前能力 | 规划中 |
+|------|---------|-------|
+| 成长分析 | 读取家庭数据,回答孩子表现问题 | AI 自动生成周报/月报 |
+| 测评解读 | 报告数据结构化展示 | AI 自动解读测评结果,标注风险项 |
+| 智能推荐 | 基于维度的活动/商品推荐 | 基于孩子画像的个性化推荐引擎 |
+| 任务生成 | 手动创建 | AI 根据孩子能力自动生成个性化任务 |
+| 成长规划 | 规划师手动制定 | AI 辅助成长规划师,自动生成初版方案 |
+
+**关键技术:**
+- `FamilyContextService.buildContext(userId)` — 自动组装家庭 360° 数据上下文
+- 对接 Dify API,支持会话管理、历史记录
+- AI 聊天页面已在微信小程序上线(`pages/ai/chat`)
+- 未来愿景:每个家庭拥有专属 AI 成长顾问
+
+**关键数据:**
+- 家庭上下文覆盖:五维能量、任务完成率、测评结果、成员信息、历史记录
+- AI 接口:4 个 API(发送消息/会话列表/消息历史/删除会话)
+
+**视觉建议:**
+- 左侧展示 AI 聊天界面截图(对话气泡风格)
+- 右侧上方展示"家庭上下文"数据流向图(用户→FamilyContextService→Dify→回答)
+- 右侧下方展示 AI 应用矩阵(已上线 vs 规划中,用颜色区分)
+- 底部的 slogan:"每个家庭,都有一个懂你的 AI 助手"
+
+**演讲备注:**
+"AI 不是贴上去的标签。我们的 AI 助手已经接入了完整的家庭上下文——它知道你家有几个孩子,每个孩子最近任务完成得怎么样,五维能量有什么变化。家长不需要翻十几个页面去找信息,直接问就行。这是我们数据资产的第一个 AI 落地场景。"
+
+---
+
+## Slide 6:核心壁垒 — 数据 + 网络效应
+
+**页面标题:** 四大核心壁垒:别人要追 2-3 年
+
+**核心文案:**
+
+**壁垒一:三方网络效应(不可复制)**
+```
+更多家长 → 吸引更多成长规划师
+更多规划师 → 提供更优质服务
+更优质服务 → 吸引更多家庭
+    形成正向飞轮,越滚越大
+```
+- 服务商入驻体系已上线(`VendorController`),支持规划师/活动方/供应商三类角色
+- 服务商等级体系、团队管理、佣金分成机制齐备
+
+**壁垒二:行为数据护城河(越久越深)**
+- 任务完成记录 → 构建孩子"努力曲线"
+- 五维能量变化 → 构建家庭"健康趋势"
+- 测评结果沉淀 → 构建孩子"成长档案"
+- 游戏训练记录 → 构建"认知能力基线"
+- 家庭切换需家长密码 → 数据安全性高
+- **47 个数据库实体、25+ 数据源** — 数据结构复杂度构成迁移壁垒
+
+**壁垒三:五维评价体系(自研方法论)**
+- 双轨并行:沙盘评分(行为衍生)+ 账本计量(交易记录)
+- 五行相生理论融入算法模型
+- 多源加权计算,每个维度 3-8 个数据源
+- 灵活配置:服务到维度的映射比例可通过 `energy_source_config` 动态调整
+- 安全机制:日上限 200 能量,各维度独立上限
+
+**壁垒四:四级地址匹配系统(基础设施)**
+- 省市区街道四级联动的地址匹配引擎
+- 区域回退匹配逻辑:精确街道→区→市→省逐级降级
+- 已对接微信小程序地址选择器组件
+- 为未来社区活动和本地化服务提供基础设施
+
+**关键数据:**
+- 47 个数据实体 | 25+ 评分数据源 | 4 级地址匹配
+- 3 类服务商角色 | 5 维能量体系 | 10 种能量来源
+
+**视觉建议:**
+- 四象限布局,每个象限代表一个壁垒
+- 第一象限(左上):飞轮图 — 家长 ↔ 规划师 ↔ 服务 循环
+- 第二象限(右上):数据积累曲线 — 时间轴上升,标注"数据越多,壁垒越高"
+- 第三象限(左下):五角星能量图
+- 第四象限(右下):地址四级级联示意图
+- 中心大字:"2-3 年追赶差距"
+
+**演讲备注:**
+"投资人常问我们一个问题:如果 BAT 进场怎么办?我的回答是:欢迎。因为家庭健康数据的积累没有捷径。一个孩子从 6 岁到 18 岁,12 年的任务记录、测评结果、能量变化——这套数据资产不是烧钱能快速复制的。加上我们的五维方法论和地址匹配系统,保守估计竞品需要 2-3 年才能追到现在的位置。"
+
+---
+
+## Slide 7:商业模式 — 三方平台经济
+
+**页面标题:** 三边市场:佣金 + 订阅 + 交易费
+
+**核心文案:**
+
+**角色关系:**
+
+```
+家长(需求方)                   服务商(供给方)
+    │                                │
+    │ ① 购买测评/套餐                 │ ① 发布套餐/商品
+    │ ② 支付服务费                    │ ② 交付服务/商品
+    │ ③ 平台抽佣                      │ ③ 获得分成
+    └────────── 浠艾福平台 ──────────┘
+                      │
+           ④ 平台运营(审核/配置/数据)
+```
+
+**收入模型:**
+
+| 收入来源 | 说明 | 预估毛利率 |
+|---------|------|:---------:|
+| **测评服务费** | 家长支付测评订单,平台抽取 15-25% 佣金 | 60-70% |
+| **规划师佣金** | 规划师套餐销售抽佣 10-20% | 80-90% |
+| **商品交易费** | 商城交易抽佣 5-15% | 85-95% |
+| **会员订阅** | 家庭会员月卡/年卡(增值服务 + AI 助手) | 70-80% |
+| **推广分佣** | 二级分销佣金体系 | 50-60% |
+
+**成本结构:**
+- 固定成本:服务器 + 带宽(约 3-5 万/年初期,可弹性扩展)
+- 变动成本:支付通道费(0.6%)+ AI API 调用费 + 短信验证码
+- 人力成本:小而美的技术 + 运营团队
+
+**单位经济模型(预估):**
+- 单家庭获客成本(CAC):**15-25 元**(微信小程序自然流量 + 社交裂变)
+- 单家庭年均价值(LTV):**300-800 元**(测评 2-3 次/年 + 套餐 1-2 个/年)
+- LTV/CAC:**12-50 倍**(说明模型健康,可大规模投放)
+
+**关键数据:**
+- LTV/CAC 比值 > 3 即为健康模型,我们的预估模型 **12-50 倍**
+- 3 个收入层:交易抽佣 + 订阅 + 推广
+- 覆盖 4 种交易类型:测评、套餐、商品、会员
+
+**视觉建议:**
+- 顶部展示三方关系图(三角形布局:家长 - 平台 - 服务商)
+- 中部用柱状图或卡片展示各收入来源占比
+- 底部展示 LTV/CAC 对比图,标注行业平均值(3x)与我们的预估(12-50x)
+- 暖橙色标注"平台抽佣"箭头,突出平台价值
+
+**演讲备注:**
+"我们的商业模式不是拍脑袋想的,而是从第一天就设计好的三边市场模型。每一笔交易,平台都有合理的抽佣空间。更关键的是,微信小程序获客成本极低——一个家庭 15-25 块钱,但年价值保守估计 300 块。这意味着即使没有融资,单靠自然增长我们也有健康的单位经济模型。"
+
+---
+
+## Slide 8:技术架构 — 成熟稳定可扩展
+
+**页面标题:** 生产级技术架构:已运行多个版本
+
+**核心文案:**
+
+**后端架构(Java 8 + Spring Boot 2.7.18):**
+- 标准分层:Controller → Service → Mapper(禁止跨层调用)
+- ORM:MyBatis-Plus,47 个实体类
+- 认证:JWT Bearer Token,`JwtInterceptor` 拦截 `/api/**`
+- 多数据源:cfc 主库 + sfms 源库(只读,数据迁移用)
+- 统一响应:`Result<T>`(code / message / data)
+- API 风格:统一 `@PostMapping`(全部 POST 方法,简洁一致)
+
+**前端架构:**
+- 小程序:uni-app(Vue 2 Options API),跨平台兼容
+- 管理端:Vue 2 + Element UI,29 个页面
+- 设计 Token 体系:统一颜色/阴影/圆角规范
+- 自定义 TabBar:角色感知,不同角色显示不同内容
+
+**技术亮点:**
+| 能力 | 说明 |
+|------|------|
+| 48 个控制器 | 覆盖 15 个功能模块,成熟的企业级架构 |
+| JWT 认证 | 8 个公开路径白名单,其余全量拦截 |
+| 多数据源 | 支持跨库查询,历史数据平滑迁移 |
+| 数据库初始化器 | `DatabaseInitializer` 启动时自动初始化 |
+| 字段加密 | 敏感加密字段,`EncryptService` 统一管理 |
+| 设计 Token | 主色 #F97316 / 辅色 #0EA5E9 / 暖白背景 #FFF7ED |
+
+**技术储备:**
+- 已完成从旧系统(sfms)到 cfc 的全量数据迁移
+- 经过多次迭代重构,代码规范严格
+- Maven 编译验证 + 启动验证流程成熟
+
+**关键数据:**
+- 48 个控制器 | 47 个实体 | 15 个功能模块
+- 50+ 小程序页面 | 29 个管理端页面
+- Java 8 / Spring Boot 2.7.18 / MySQL 8.0
+- 数据库地址:`192.168.16.251:3306/cfc`
+
+**视觉建议:**
+- 架构分层图:从左到右展示 小程序 → Nginx → Spring Boot → MySQL
+- 技术栈图标排列(Java / Spring / MySQL / uni-app / Vue 等)
+- 底部展示代码规范关键词:`@PostMapping Only` / `Result<T>` / `JWT Auth` / `MyBatis-Plus`
+- 色调:深色背景 + 技术蓝绿色代码风格
+
+**演讲备注:**
+"我是技术出身,所以对架构有执念。我们的系统从 0 到 1 完全自研,已经经历过一次完整的数据迁移——从遗留的 sfms 系统全量迁移到了新平台。这不是一个 MVP 原型,而是一个真正在生产环境跑的业务系统。48 个控制器、47 张表、50 多个页面,该有的都有了。"
+
+---
+
+## Slide 9:用户案例 — 真实家庭使用场景
+
+**页面标题:** 真实场景:一个家庭在浠艾福的一天
+
+**核心文案:**
+
+**场景一:早晨 — 家长创建任务**
+- 妈妈在家长端创建任务:"每天阅读 30 分钟(+20 能量积分)"
+- 任务自动推送到孩子的任务列表
+- 五维系统标记该任务影响"智"维度 60% + "行"维度 40%
+
+**场景二:下午 — 孩子执行 + 小游戏**
+- 孩子完成阅读,拍照上传作为凭证
+- 孩子玩舒尔特方格 5 分钟,系统记录专注力训练成绩
+- 游戏数据自动汇入"智"维度评分
+
+**场景三:晚上 — 审核 + 能量到账**
+- 家长审核任务,确认完成,20 能量到账
+- 能量自动按比例分配到"智"和"行"维度
+- 家庭能量沙盘数据自动更新
+
+**场景四:周末 — 测评 + 规划师**
+- 家长预约 DAN 专注力测评,选择认证成长规划师
+- 系统自动保存孩子当前快照(身高/体重/年级等)
+- 规划师录入测评结果,生成认知六维报告
+- AI 自动解读报告:标注强项与短板,推荐改善方案
+
+**场景五:月底 — 心愿兑换**
+- 孩子用积累的 500 能量兑换"去游乐园"心愿
+- 家长审批通过,能量扣减,心愿达成
+- 家庭五维月报生成:显示各维度变化趋势
+
+**关键数据:**
+- 任务闭环:创建→执行→审核→能量发放→数据沉淀
+- 测评闭环:申请→快照→支付→结果→报告→解读
+- 心愿闭环:创建→审批→扣减→达成→反馈
+
+**视觉建议:**
+- 时间轴布局,从左到右展示 5 个场景
+- 每个场景配对应的小程序页面截图(任务列表 / 游戏界面 / 能量看板 / 测评报告 / 心愿单)
+- 底部用箭头标注数据流向:用户行为→系统记录→五维计算→反馈展示
+- 风格类似故事板,让投资人能快速理解用户体验
+
+**演讲备注:**
+"讲太多功能不如看一个家庭的实际使用流程。从早到晚,从周一到月底,这个家庭所有的互动都在我们的系统里完成。注意一个关键点——每一个用户行为都在产生数据,而这些数据又反过来驱动更好的用户体验。这就是我们说的'数据飞轮'。"
+
+---
+
+## Slide 10:精准营养闭环 — 基于真实检测案例
+
+**页面标题:** 精准营养:从肠道菌群检测到个性化方案
+
+**核心文案:**
+
+**案例背景(真实匿名数据):**
+- 用户编号:`530010234`
+- 检测项目:肠道菌群健康风险评估
+- 合作实验室:募极生物
+- 报告日期:2026-01-06
+- 报告页数:135 页(含完整菌群分析 + 饮食建议)
+
+**检测结果概览:**
+
+| 指标 | 分值 | 人群均值 | 评估 |
+|:----:|:----:|:--------:|:----:|
+| 健康总分 | **73** | 75 | 一般(干预空间大) |
+| 菌群健康 | 53 | — | 偏低 |
+| 营养均衡 | **68** | — | 短板维度 |
+| 慢病控制 | 85 | — | 良好 |
+
+**三大缺乏指标(AI 自动标注):**
+- 维生素 B12 → 14 分(正常 >50)→ 疲劳、贫血风险
+- 苏氨酸 → 14 分(正常 >50)→ 肠道屏障功能受损
+- 丙氨酸 → **9 分**(正常 >50)→ 能量代谢异常
+
+**浠艾福的精准营养闭环:**
+
+```
+① 居家采样(合作实验室)→ ② AI 智能分析(菌群 + 代谢通路)
+→ ③ 个性化方案生成(基于肠型的饮食计划)→ ④ 五维能量驱动执行(任务 + 积分)
+→ ⑤ 周期性复测 → 回到 ① 循环迭代
+```
+
+**平台价值:**
+- 检测端:对接专业实验室,报告自动同步
+- 分析端:AI 解读报告,自动标注风险与缺乏指标
+- 执行端:健康积分驱动,任务系统落地
+- 跟踪端:复测提醒 + 效果可视化对比
+
+**关键数据:**
+- 检测到 313 种菌(人群均值约 800 种)
+- 香农多样性指数 4.49(人群均值 4.6)
+- 个性化食物推荐指数:最高 68(牛蒡根/生姜)到最低 -13(肥肉/培根)
+- 四步干预策略:增加膳食纤维 → 优选发酵蛋白 → 控制风险食物 → 定期复测
+
+**视觉建议:**
+- 中央展示圆形闭环流程图(5 个节点依次连接)
+- 左侧展示检测报告截图(脱敏后的评分仪表盘)
+- 右侧展示个性化饮食方案(四象限食物推荐图:多吃/适量/少吃/避免)
+- 底部标注与"身"维度的关联:"精准营养 → 身体能量提升 → 家庭健康指数"
+- 色调:绿色 + 橙色,呼应健康和温暖
+
+**演讲备注:**
+"这是我们跟募极生物合作的一个真实案例。用户做完肠道菌群检测,报告 135 页——说实话,普通人根本看不懂。我们的 AI 系统自动提取了关键信息:三种营养素严重缺乏,饮食结构需要调整。然后生成了个性化的饮食方案,并通过任务系统推动执行。从检测到行动,以前需要跑医院、找专家、花好几千,现在在家就能完成闭环。"
+
+---
+
+## Slide 11:品牌背书与合作伙伴
+
+**页面标题:** 品牌背书与生态合作
+
+**核心文案:**
+
+**检测合作:**
+- 募极生物 — 肠道菌群检测合作伙伴
+- 专业实验室出具 135 页完整检测报告
+- 居家采样模式,降低用户参与门槛
+- 报告数据与平台 AI 系统无缝对接
+
+**平台资质:**
+- 微信小程序生态:官方 AppID `wx5ba8038ef16fb245`
+- 基于微信社交裂变,天然获客优势
+- 小程序已是微信生态内家庭服务的重要入口
+- 亿图腾(北京)科技有限公司 — 正规工商注册
+
+**技术实力证明:**
+- 多年代理成熟框架、分布式架构经验
+- 已完成从旧系统(sfms)到新平台的完整数据迁移
+- 47 张数据库表、48 个控制器、15 个功能模块的完整产品
+- 持续迭代超过 10+ 个版本
+
+**行业资源:**
+- 已对接成长规划师认证体系
+- 社区活动资源(SNCP 社区模块)
+- 家庭服务经验积累
+
+**关键数据:**
+- 小程序生态:**10 亿+** 月活用户基础
+- 微信搜索:家庭健康类目搜索量年增长 **35%+**
+- 工业级系统:47 实体 / 48 控制器 / 50+ 页面
+- 真实检测案例:530010234(匿名可展示)
+
+**视觉建议:**
+- 上方展示合作 Logo 墙(募极生物、微信小程序、认证机构等)
+- 中部展示小程序二维码截图 + AppID
+- 下方展示系统架构成熟度标签云("生产级""多数据源""数据迁移已验证"等)
+- 底部一排技术认证徽章样式
+
+**演讲备注:**
+"我们的品牌背书不是花钱买的,是实打实做出来的。微信小程序是我们的主阵地——10 亿月活、社交裂变、零获客成本的天然优势。募极生物这样的专业机构选择跟我们合作,是因为我们有能力把 135 页的检测报告变成 3 页的行动方案。这是技术实力带来的信任。"
+
+---
+
+## Slide 12:市场规模与增长策略
+
+**页面标题:** 万亿赛道 × 社交裂变 × 飞轮增长
+
+**核心文案:**
+
+**市场规模:**
+- 中国家庭教育消费市场:**4.5 万亿元**(2025 年,艾瑞咨询)
+- 儿童健康管理市场:**5000 亿元+**(含体检/营养/心理/运动)
+- 家庭教育服务平台市场:渗透率不足 **3%** → 增长空间巨大
+- 目标用户:3-18 岁儿童家庭约 **1.2 亿户**
+
+**增长策略 — 三个阶段:**
+
+| 阶段 | 目标 | 策略 | 关键指标 |
+|:----:|:----:|------|:--------:|
+| 冷启动 | 1000 家庭 | 种子用户 + 规划师入驻 + 活动运营 | 激活率 > 60% |
+| 裂变期 | 1 万 家庭 | 社交裂变 + 邀请奖励 + 任务拼团 | K-factor > 1.2 |
+| 规模化 | 10 万+ 家庭 | 广告投放 + 渠道合作 + 品牌心智 | CAC < 20 元 |
+
+**增长飞轮驱动:**
+1. **产品驱动:** 任务系统创造每日打开理由(高频)
+2. **社交驱动:** 微信好友邀请、家庭组队、任务分享(裂变)
+3. **内容驱动:** 测评报告、成长月报、AI 解读(传播)
+4. **服务驱动:** 更多成长规划师 → 更多套餐选择 → 更高转化
+
+**竞争优势:**
+- 对比纯工具类 App:我们有服务闭环(测评→规划→执行→跟踪)
+- 对比纯内容平台:我们有数据闭环(行为→能量→报告→改善)
+- 对比纯教育机构:我们有健康维度(身体+情绪+财商,不仅是分数)
+
+**关键数据:**
+- TAM:4.5 万亿 | SAM:5000 亿 | 目标渗透率:0.1% → 5 亿营收
+- 微信社交裂变 K-factor 行业基准:0.8-1.5 | 我们的目标:>1.2
+- 小程序次日留存行业均值:25% | 我们的目标:>40%
+
+**视觉建议:**
+- 顶部展示 TAM/SAM/SOM 三层漏斗图
+- 中部展示增长飞轮图(四个驱动因素循环)
+- 底部展示"三阶段"时间轴 + 对应里程碑数据
+- 右侧留白处标注:"3% 渗透率 → 97% 待开拓"
+
+**演讲备注:**
+"我们选了一个大赛道里的小切口。4.5 万亿的家庭教育市场,目前做家庭健康成长平台的几乎没有成规模的玩家。我们选择从测评和任务系统切入——这两个功能都有很强的'打开理由'。测评一年 2-3 次是刚需,任务系统每天都要用。高频带动低频,这是我们的产品逻辑。加上微信的社交裂变基因,增长模型非常清晰。"
+
+---
+
+## Slide 13:团队介绍(Placeholder)
+
+**页面标题:** 核心团队
+
+**核心文案:**
+
+> **注:此页面需根据实际团队信息补充**
+
+**团队架构:**
+- 创始人/CEO:___ 年连续创业经验,家庭教育领域背景
+- CTO:___ 年技术架构经验,Spring Boot / 小程序全栈
+- 运营负责人:___ 年教育行业运营经验
+- 产品负责人:___ 年 C 端产品经验
+
+**团队优势:**
+- 技术 + 教育 + 运营 复合背景
+- 完整的产品从 0 到 1 经验
+- 对家庭教育场景的深度理解
+- 已完成第一版完整产品的研发和上线
+
+**当前团队规模:**
+- 根据实际补充:建议早期团队 10-15 人
+- 核心研发:5-8 人
+- 运营 + 市场:3-5 人
+
+**视觉建议:**
+- 团队成员照片排列(圆形头像)
+- 每人标注:姓名、职位、背景亮点(Logo + 一句话)
+- 底部标注公司名称 + 成立时间
+- 风格:专业但温暖,与品牌调性一致
+
+**演讲备注:**
+(根据实际团队情况准备,重点突出与家庭教育和健康相关的行业经验)
+
+---
+
+## Slide 14:融资计划与资金用途
+
+**页面标题:** 天使轮融资计划
+
+**核心文案:**
+
+**本轮融资:**
+- 金额:**300-500 万元人民币**(建议根据实际需求调整)
+- 形式:增资扩股,出让 **8-15%** 股份
+- 估值逻辑:产品已上线 + 技术成熟 + 市场验证中
+
+**资金用途:**
+
+| 用途 | 占比 | 金额 | 说明 |
+|:----:|:----:|:----:|------|
+| 产品研发 | 40% | 120-200 万 | AI 功能深化、用户体验优化、新模块开发 |
+| 市场推广 | 30% | 90-150 万 | 社交裂变投放、渠道合作、种子用户获取 |
+| 团队建设 | 15% | 45-75 万 | 核心岗位补充(运营/市场/AI 工程师) |
+| 运营支持 | 10% | 30-50 万 | 服务器、带宽、支付通道、API 调用 |
+| 预留 | 5% | 15-25 万 | 应急储备 |
+
+**12 个月里程碑规划:**
+
+| 时间 | 里程碑 | 关键指标 |
+|:----:|:------|:--------:|
+| M1-M3 | 团队组建 + AI 功能升级 | 完成 AI 报告解读上线 |
+| M4-M6 | 用户增长 + 服务商拓展 | 注册家庭 3000+,规划师 50+ |
+| M7-M9 | 商业化验证 | 月交易额 30 万+,抽佣收入覆盖运营成本 |
+| M10-M12 | 规模复制 + 下一轮准备 | 注册家庭 10000+,启动 Pre-A 轮 |
+
+**下一轮融资计划(Pre-A 轮):**
+- 时间:本轮完成后第 10-12 个月
+- 金额:1500-3000 万元
+- 用途:全国市场拓展 + 服务商网络建设 + AI 能力深化
+
+**关键数据:**
+- 本轮资金可支撑 **12-18 个月** 运营
+- 核心指标目标:家庭数 **10000+**,月活跃率 **>40%**
+- 盈亏平衡点预估:月交易额 **50 万** + 付费会员 **2000 家庭**
+
+**视觉建议:**
+- 顶部展示融资关键信息卡片(金额/出让/估值)
+- 中部为资金用途饼图(醒目,每块标注比例和功能)
+- 下方为 12 个月里程碑时间轴(四个关键节点 + 图标)
+- 右下小字标注:"详情见财务预测模型"
+
+**演讲备注:**
+"我们融的不多,300-500 万,因为产品已经跑通了,技术基建也扎实了。这笔钱主要用来做两件事:第一,把 AI 能力做深——这是我们的差异化壁垒;第二,把用户规模拉起来,验证商业模型。12 个月后,我们有信心用数据说话,启动 Pre-A 轮。归根结底,我们需要的不是烧钱续命,而是加速已经验证的飞轮。"
+
+---
+
+## Slide 15:未来路线图
+
+**页面标题:** 产品路线图:从家庭工具到家庭生态
+
+**核心文案:**
+
+**已上线(当前):**
+
+| 模块 | 状态 |
+|:----|:----:|
+| 用户认证与角色系统 | ✅ 已上线 |
+| 五维能量沙盘 + 账本 | ✅ 已上线 |
+| 任务系统(创建/执行/审核) | ✅ 已上线 |
+| 心愿体系(创建/审批/兑换) | ✅ 已上线 |
+| 小游戏(舒尔特/猜数字/数独) | ✅ 已上线 |
+| 测评订单 + 预约 + 支付 | ✅ 已上线 |
+| 成长规划师入驻及套餐 | ✅ 已上线 |
+| AI 家庭助手(Dify 对接) | ✅ 已上线 |
+| 商城 + 购物车 + 订单 | ✅ 已上线 |
+| 服务商入驻管理 | ✅ 已上线 |
+
+**未来 12 个月路线图:**
+
+| 阶段 | 重点 | 具体功能 |
+|:----:|:----|---------|
+| **Q1 增强** | AI 深化 | AI 报告自动解读、AI 个性化任务生成、智能推荐引擎 v1 |
+| **Q2 增长** | 服务商网络 | 规划师认证体系线上化、活动方入驻工具包、批量任务下发 |
+| **Q3 变现** | 商业化闭环 | 会员订阅体系上线、推广分佣系统、活动/课程售卖 |
+| **Q4 扩张** | 生态构建 | 社区互动功能、家庭社交网络、Open API 第三方接入 |
+
+**未来 3 年愿景:**
+- 2027:成为国内领先的家庭健康成长平台,覆盖 **10 万+** 家庭
+- 2028:建立"五维健康"行业标准,成为家庭教育领域的权威评价体系
+- 2029:开放平台生态,接入第三方服务商,构建家庭服务超级入口
+
+**关键数据:**
+- 已上线功能模块:**10+** 个
+- 未来 12 个月计划上线功能:**15+** 个
+- 3 年目标:从工具型平台 → 生态型平台
+
+**视觉建议:**
+- 上方展示已上线功能矩阵(绿色打勾图标,排列整齐)
+- 中部展示未来 12 个月路线图(四象限或时间轴,Q1-Q4)
+- 下方展示 3 年愿景(三个里程碑箭头,2027→2028→2029)
+- 整体风格:从左到右的时间流,颜色从暖橙渐变为深蓝,象征从初创到成熟
+
+**演讲备注:**
+"这个路线图不是画饼。左边'已上线'那一列,每一个功能都在我们的生产环境中真实运行。投资人最怕听到'未来要做'——所以我们把能做的都先做了。未来 12 个月的核心就三件事:AI 更聪明、服务商更多、商业化跑通。每一步都有清晰的衡量标准。"
+
+---
+
+## Slide 16:联系我们
+
+**页面标题:** 让每个家庭,都拥有健康成长的力量
+
+**核心文案:**
+
+**公司信息:**
+- 公司名称:亿图腾(北京)科技有限公司
+- 品牌名称:浠艾福(Care Family)
+- 产品形态:微信小程序 + Web 管理端
+
+**联系方式:**
+- (根据实际填写)
+- 邮箱:________________
+- 电话:________________
+- 微信:________________
+- 地址:________________
+
+**邀约:**
+- 欢迎体验小程序:搜索"浠艾福"或扫描下方二维码
+- 欢迎预约深度 Demo 演示
+- 欢迎对接战略资源与合作
+
+**品牌 Slogan:**
+> **给全家的一站式幸福提案**
+
+**视觉建议:**
+- 上半部分:暖橙渐变背景,中央大号品牌 Slogan
+- 下半部分:白色底,展示小程序码 + 联系方式
+- 底部标注:公司全称 + 融资轮次标识
+- 设计简洁大气,强化品牌记忆
+
+**演讲备注:**
+"感谢各位的时间。浠艾福是我们团队多年深耕家庭健康领域的结晶。我们不是在做一个 App,我们是在构建中国家庭未来 10 年的健康生活方式。期待与在座的各位投资人一起,把这份事业做大。谢谢!"
+
+---
+
+## 附录:设计规范
+
+### 颜色体系
+
+| Token | 色值 | 用途 |
+|-------|------|------|
+| 主色(Primary) | `#F97316` | 标题、按钮、强调 |
+| 辅色(Accent) | `#0EA5E9` | 链接、辅助元素 |
+| 背景暖白 | `#FFF7ED` | 页面背景 |
+| 五维-身 | `#8D6E63` 土棕 | 身体维度 |
+| 五维-心 | `#FF6B35` 火橙 | 心智维度 |
+| 五维-智 | `#FFD700` 金黄 | 智慧维度 |
+| 五维-行 | `#4CAF50` 木绿 | 行动维度 |
+| 五维-富 | `#42A5F5` 水蓝 | 财富维度 |
+
+### 字体建议
+- 标题:思源黑体 Bold / 阿里巴巴普惠体 Bold
+- 正文:思源黑体 Regular / 阿里巴巴普惠体 Regular
+- 数据/数字:DIN Pro / Arial
+
+### 图标风格
+- 五维图标:使用 emoji 符号或自定义线描图标(统一圆角 2px)
+- 功能图标:线面结合风格,主色 #F97316
+- 装饰元素:暖色调几何图形(圆/弧/六边形)
+
+---
+
+> **文档版本:** v1.0
+> **最后更新:** 2026-06-16
+> **准备说明:** 本方案为 PPT 内容脚本,设计师可直接根据每页的 "视觉建议" 栏进行排版设计。每页包含标题、核心文案、数据点、视觉指引和演讲备注五个部分,满足路演全流程需求。