Browse Source

fix(mp-weixin): 修复下单地址切换/编译报错与搜索入口,恢复登录精减版

业务修复:
- shop/address + shop/checkout: 跨页回传改用 prevPage.$vm || prevPage,补一次性选中标记应对 onShow 重拉;id 比较统一 String();编辑地址后刷新最新对象
- shop/order-detail: 补齐「订单信息」区块缺失的 view.section 开标签,消除 scroll-view 未闭合与多根编译错误
- article-center: 搜索栏常驻可见、移除 emoji 放大镜,新增 focus/cancel 逻辑(契约 C1-C6 保持 17/17)
- login-sheet: 恢复 4bf5117b0 精减版并补 standalone prop 声明;精减版不含 str-replace/ls-icon,dart-sass SassError 随之消除

文档与工具:
- docs/redesign: 补充 CF 与分享机制边界(STRATEGY.md),新增 prototype-redesign.html
- 纳入知识库合并与截图标注辅助脚本(docs/参考资料/知识库、web/img/ref-screenshots)

验证:vue-template-compiler 模板 0 errors、dart-sass 编译通过、文章搜索契约 17/17、地址行为断言 14/14、完整 build:mp-weixin BUILD_EXIT=0(1570 产物)、QA 独立回归 38/38 NoOne。全量 Jest 602 通过,4 个既有失败与本轮无关。
asus 19 hours ago
parent
commit
72c4bbc378

+ 213 - 373
cfc-frontend/components/login-sheet.vue

@@ -2,68 +2,29 @@
   <view class="login-sheet-overlay" :class="{ 'login-sheet-overlay--visible': visible }" @click="handleOverlayClick">
     <view class="login-sheet-panel" :class="{ 'login-sheet-panel--show': panelShow }" @click.stop>
       <view class="login-sheet-header">
-        <view class="login-sheet-handle" aria-hidden="true"></view>
         <view class="login-sheet-title">欢迎回来</view>
       </view>
       <scroll-view scroll-y class="login-sheet-scroll" :show-scrollbar="false">
         <view class="login-sheet-content">
-          <!-- 品牌区:logo / 品牌名 / 身份 / 主打句(记忆点) -->
-          <view class="brand-section">
-            <image class="logo" src="/static/logo.png" mode="aspectFit" @error="handleImageError"></image>
-            <text class="app-name">浠艾福</text>
-            <text class="identity">您的专属家庭主动健康管理师</text>
-            <text class="slogan">家里每个人的状态,你真的<text class="slogan-accent">看见了?</text></text>
+          <!-- 品牌区(左上角锁定版式,与首页一致) -->
+          <view class="brand-bar">
+            <image class="brand-logo" src="/static/logo.png" mode="aspectFit" @error="handleImageError"></image>
+            <view class="brand-texts">
+              <text class="brand-name">浠艾福</text>
+              <text class="brand-tagline">您的专属家庭主动健康陪跑</text>
+            </view>
           </view>
 
-          <!-- 理念卡:四步闭环(细边框表面区分,无单侧彩条) -->
+          <!-- 理念卡 -->
           <view class="concept-card">
-            <view class="concept-desc">
-              不等问题发生,先一步<text class="concept-highlight">识别风险 · 主动干预 · 持续评估</text>,把全家健康管理融入每一天。
-            </view>
             <view class="loop-row">
-              <view class="loop-step">
-                <view class="loop-glyph loop-glyph-wisdom" aria-hidden="true"></view>
-                <text class="loop-step-text">提前识别</text>
-              </view>
-              <view class="loop-connector" aria-hidden="true"></view>
-              <view class="loop-step">
-                <view class="loop-glyph loop-glyph-success" aria-hidden="true"></view>
-                <text class="loop-step-text">风险管控</text>
-              </view>
-              <view class="loop-connector" aria-hidden="true"></view>
-              <view class="loop-step">
-                <view class="loop-glyph loop-glyph-wealth" aria-hidden="true"></view>
-                <text class="loop-step-text">主动干预</text>
-              </view>
-              <view class="loop-connector" aria-hidden="true"></view>
-              <view class="loop-step">
-                <view class="loop-glyph loop-glyph-danger" aria-hidden="true"></view>
-                <text class="loop-step-text">效果评估</text>
-              </view>
-            </view>
-          </view>
-
-          <!-- 五维标签带:统一描边容器 + 细分割线,色块仅作身份记号(非等权重填充 pills) -->
-          <view class="dims-band">
-            <view class="dim-cell">
-              <view class="dim-glyph dim-glyph-body" aria-hidden="true"></view>
-              <text class="dim-label">身·健康</text>
-            </view>
-            <view class="dim-cell">
-              <view class="dim-glyph dim-glyph-wisdom" aria-hidden="true"></view>
-              <text class="dim-label">智·认知</text>
-            </view>
-            <view class="dim-cell">
-              <view class="dim-glyph dim-glyph-wealth" aria-hidden="true"></view>
-              <text class="dim-label">富·创富</text>
-            </view>
-            <view class="dim-cell">
-              <view class="dim-glyph dim-glyph-action" aria-hidden="true"></view>
-              <text class="dim-label">行·和谐</text>
-            </view>
-            <view class="dim-cell">
-              <view class="dim-glyph dim-glyph-mind" aria-hidden="true"></view>
-              <text class="dim-label">心·大爱</text>
+              <view class="loop-pill loop-pill-indigo"><text>🔍</text><text class="loop-pill-text">提前识别</text></view>
+              <text class="loop-arrow">→</text>
+              <view class="loop-pill loop-pill-green"><text>🛡️</text><text class="loop-pill-text">风险管控</text></view>
+              <text class="loop-arrow">→</text>
+              <view class="loop-pill loop-pill-amber"><text>⚡</text><text class="loop-pill-text">主动干预</text></view>
+              <text class="loop-arrow">→</text>
+              <view class="loop-pill loop-pill-red"><text>📊</text><text class="loop-pill-text">效果评估</text></view>
             </view>
           </view>
 
@@ -72,15 +33,15 @@
             <view class="phone-section">
               <button
                 class="phone-btn"
-                :class="{ 'phone-btn--error': errorState, 'phone-btn--success': successState }"
                 open-type="getPhoneNumber"
                 @getphonenumber="getPhoneNumber"
                 @tap="onPhoneBtnTap"
                 :loading="loading"
                 :disabled="!agreed || loading || tapLock"
               >
-                <view class="phone-btn-icon" aria-hidden="true"></view>
-                <text>手机号快捷登录</text>
+                <text class="phone-btn-icon">📱</text>
+                <text class="phone-btn-text">手机号快捷登录</text>
+                <text class="phone-arrows">›››</text>
               </button>
             </view>
 
@@ -111,6 +72,11 @@ import { wechatPhoneLogin } from '../utils/api.js'
 
 export default {
   name: 'login-sheet',
+  // 兜底整页登录壳 pages/login/login.vue 会传 :standalone="true";
+  // 显式声明以收敛接口契约,避免未知 attribute 透传到 WXML 根节点(不改变行为)
+  props: {
+    standalone: { type: Boolean, default: false }
+  },
   data() {
     return {
       visible: false,
@@ -122,10 +88,7 @@ export default {
       nickname: '',
       avatar: '',
       redirectUrl: '',
-      _tapLockTimer: null,
-      // 八状态视觉位(R2 修复):默认均为布尔 false,不触发任何 class;登录逻辑与外部契约不变
-      errorState: false,
-      successState: false
+      _tapLockTimer: null
     }
   },
   beforeDestroy() {
@@ -268,67 +231,6 @@ export default {
 </script>
 
 <style scoped lang="scss">
-/* =====================================================================
-   login-sheet 视觉令牌(局部)
-   来源:全部复用 uni.scss 共享令牌;仅本组件专有的色值以 $ls- 前缀声明,
-   组件体内零裸 hex / 裸 px / 裸毫秒。
-   注意:mp-weixin WXML 不支持模板内联 <svg>(仓库既有约定,
-   components/wuxing-sandbox.vue 注释在案),故图标一律用
-   data:image/svg+xml 作为 CSS background-image,颜色显式写入 SVG 文档。
-   ===================================================================== */
-
-// ---- 图标工具:构建 data-URI SVG(颜色显式编码为 %23hex)----
-// str-replace 使维度色令牌可注入 data-URI(# 编码为 %23),令牌成为唯一事实源
-@function str-replace($string, $search, $replace) {
-  $index: str-index($string, $search);
-  @if $index {
-    @return str-slice($string, 1, $index - 1) + $replace +
-      str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
-  }
-  @return $string;
-}
-@function ls-icon($color, $inner) {
-  @return url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='#{str-replace($color, '#', '%23')}' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E#{$inner}%3C/svg%3E");
-}
-// 图标路径(feather 线性几何,24×24;只编码 < > 与 #,空格/单引号合法)
-$ls-i-search:   "%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E";
-$ls-i-shield:   "%3Cpath d='M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'/%3E%3Cpath d='m9 12 2 2 4-4'/%3E";
-$ls-i-zap:      "%3Cpath d='M13 2 3 14h9l-1 8 10-12h-9l1-8z'/%3E";
-$ls-i-chart:    "%3Cline x1='18' y1='20' x2='18' y2='10'/%3E%3Cline x1='12' y1='20' x2='12' y2='4'/%3E%3Cline x1='6' y1='20' x2='6' y2='14'/%3E";
-$ls-i-user:     "%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E";
-$ls-i-bulb:     "%3Cpath d='M9 18h6'/%3E%3Cpath d='M10 22h4'/%3E%3Cpath d='M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5.76.76 1.23 1.52 1.41 2.5'/%3E";
-$ls-i-trend:    "%3Cpolyline points='23 6 13.5 15.5 8.5 10.5 1 18'/%3E%3Cpolyline points='17 6 23 6 23 12'/%3E";
-$ls-i-activity: "%3Cpolyline points='22 12 18 12 15 21 9 3 6 12 2 12'/%3E";
-$ls-i-heart:    "%3Cpath d='M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z'/%3E";
-$ls-i-chev:     "%3Cpolyline points='9 18 15 12 9 6'/%3E";
-$ls-i-phone:    "%3Cpath d='M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.08 4.18 2 2 0 0 1 4.06 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z'/%3E";
-
-// ---- 局部令牌:底色 / 卡片 / 文字 / 描边 ----
-$ls-bg-top:      #FFFDFB;                       // 面板暖白·顶(较原 #fffaf3 更干净)
-$ls-bg-bottom:   #FBF4EA;                       // 面板暖白·底(降饱和)
-$ls-card:        #FFFFFF;                       // 卡片表面
-$ls-card-border: #F0E7DB;                       // 暖发丝描边
-$ls-divider:     #F1EAE0;                       // 发丝分割线
-$ls-overlay:     rgba(20, 15, 8, 0.48);         // 暖调遮罩,非纯黑
-$ls-ink-strong:  $facade-text-strong;           // 主文字 #333
-$ls-ink-body:    $facade-text-body;             // 正文/次级 #666
-$ls-ink-slate:   $facade-text-slate-600;        // 弱文字 #5A6478(全面板 ≥4.5:1,含底部 #FBF4EA)
-$ls-handle:      #8A8A8A;                       // 抓握条(UI affordance,对面板顶 3.40:1 ≥3:1)
-$ls-connector:   $facade-text-slate;            // 步骤连接箭头 #6B7280(对白卡 4.83:1 ≥3:1)
-$ls-brand-deep:  $brand-primary-deep;           // 品牌橙·文字/主CTA #C2410C(对白 5.18:1,AA)
-$ls-ink-on-brand:$ink-on-dark;                  // 橙底白字 #FFFFFF
-$ls-radius-card: $radius-lg;                    // 24rpx 卡片档
-$ls-radius-btn:  $radius-lg;                    // 24rpx 大按钮档
-// 维度标识色(深档,保证白底 ≥3:1;与 uni.scss §12 深档一一对应)
-$ls-dim-body:   #9C4A1A;   // 身·赭石
-$ls-dim-wisdom: $dim-wisdom-deep; // 智·靛蓝(实际编译值 #4F46E5,见 uni.scss L588 二次声明覆盖;对白 6.29:1)
-$ls-dim-wealth: $dim-wealth-deep; // 富·黄土 #92400E
-$ls-dim-action: $dim-action-deep; // 行·森林绿深档 #047857(token 复用)
-$ls-dim-mind:   $dim-mind-deep;   // 心·玫红 #9D174D
-// 维度浅底(uni.scss §13 tint;四步闭环用)
-$ls-dim-wisdom-tint: $dim-wisdom-tint; // 智浅底 #F0F0FF
-$ls-dim-wealth-tint: $dim-wealth-tint; // 富浅底 #FFFBEB
-
 /* ===== 浮层遮罩 ===== */
 .login-sheet-overlay {
   position: fixed;
@@ -336,11 +238,11 @@ $ls-dim-wealth-tint: $dim-wealth-tint; // 富浅底 #FFFBEB
   left: 0;
   right: 0;
   bottom: 0;
-  background: $ls-overlay; // 暖调遮罩,非纯黑
+  background: rgba(0, 0, 0, 0.5);
   z-index: 99999;
   opacity: 0;
   visibility: hidden;
-  transition: opacity $uni-transition-base;
+  transition: opacity 0.25s ease;
 }
 .login-sheet-overlay--visible {
   opacity: 1;
@@ -353,13 +255,13 @@ $ls-dim-wealth-tint: $dim-wealth-tint; // 富浅底 #FFFBEB
   left: 0;
   right: 0;
   bottom: 0;
-  height: 85vh;
-  max-height: 85vh;
-  background: linear-gradient(180deg, $ls-bg-top 0%, $ls-bg-bottom 100%);
-  border-radius: $ls-radius-card $ls-radius-card 0 0;
+  height: 60vh;
+  max-height: 65vh;
+  background: linear-gradient(180deg, #fffaf3 0%, #fff3e4 100%);
+  border-radius: 24rpx 24rpx 0 0;
   overflow: hidden;
   transform: translateY(100%);
-  transition: transform $uni-transition-slow;
+  transition: transform 0.3s ease;
   display: flex;
   flex-direction: column;
 }
@@ -368,25 +270,14 @@ $ls-dim-wealth-tint: $dim-wealth-tint; // 富浅底 #FFFBEB
 }
 
 .login-sheet-header {
-  padding: $uni-space-lg $uni-space-xl $uni-space-xs;
+  padding: 24rpx 32rpx 8rpx;
   display: flex;
-  flex-direction: column;
-  align-items: center;
-}
-// 抓握条:底部浮层的拖拽 affordance(居中,独立于下方左对齐内容)
-.login-sheet-handle {
-  width: 64rpx;
-  height: 8rpx;
-  border-radius: 999rpx;
-  background: $ls-handle;
+  justify-content: center;
 }
 .login-sheet-title {
-  margin-top: $uni-space-md;
-  align-self: flex-start; // R5:标题与品牌区同左锚点
-  font-size: calc($uni-font-size-sm * var(--font-scale, 1));
-  font-weight: $uni-font-weight-medium;
-  letter-spacing: 6rpx;
-  color: $ls-ink-slate;
+  font-size: calc(32rpx * var(--font-scale, 1));
+  font-weight: bold;
+  color: $facade-text-strong;
 }
 
 /* ===== 滚动区域 ===== */
@@ -398,290 +289,239 @@ $ls-dim-wealth-tint: $dim-wealth-tint; // 富浅底 #FFFBEB
 }
 
 .login-sheet-content {
-  padding: $uni-space-lg $uni-space-xl $uni-space-4xl;
+  padding: 24rpx 48rpx 60rpx;
   box-sizing: border-box;
 }
 
-/* ===== 品牌区:清晰层级(R5 不对称锚点:左对齐,打破万物居中) ===== */
-.brand-section {
+/* ===== 品牌区(左上角锁定版式,与首页一致) ===== */
+.brand-bar {
   display: flex;
-  flex-direction: column;
-  align-items: flex-start;
-  padding: $uni-space-md 0 $uni-space-xs;
-}
-.logo {
-  width: 120rpx;
-  height: 120rpx;
-  border-radius: $radius-md;
-}
-.app-name {
-  margin-top: $uni-space-lg;
-  font-size: calc($uni-font-size-3xl * var(--font-scale, 1)); // 52rpx 主标题(正文 28rpx 的 1.86x + 收窄行宽)
-  font-weight: $uni-font-weight-black;
-  color: $ls-ink-strong;
-  letter-spacing: 2rpx;
-}
-.identity {
-  margin-top: $uni-space-xs;
-  font-size: calc($uni-font-size-base * var(--font-scale, 1)); // 28rpx 副标语
-  font-weight: $uni-font-weight-medium;
-  color: $ls-brand-deep;
-  letter-spacing: 2rpx;
+  align-items: center;
+  gap: 20rpx;
+  padding: 4rpx 0;
+  box-sizing: border-box;
 }
-.slogan {
-  margin-top: $uni-space-md;
-  max-width: 460rpx; // 行宽收窄,形成视觉记忆点
-  font-size: calc($uni-font-size-lg * var(--font-scale, 1)); // 32rpx 主打句
-  line-height: 1.6;
-  color: $ls-ink-body;
-  text-align: left; // 与品牌区左对齐(R5)
+.brand-logo {
+  width: 80rpx;
+  height: 80rpx;
+  border-radius: 20rpx;
+  background: $app-surface;
+  box-shadow: 0 6rpx 16rpx rgba($brand-primary, 0.18);
+  flex-shrink: 0;
 }
-.slogan-accent {
-  color: $ls-brand-deep;
-  font-weight: $uni-font-weight-bold;
+.brand-texts {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  min-width: 0;
+  padding-left: 20rpx;
+  border-left: 2rpx solid rgba($brand-primary, 0.28);
+}
+.brand-name {
+  font-size: calc(38rpx * var(--font-scale, 1));
+  font-weight: bold;
+  color: $facade-text-strong;
+  letter-spacing: 3rpx;
+  line-height: 1.2;
+}
+.brand-tagline {
+  display: block;
+  font-size: calc(22rpx * var(--font-scale, 1));
+  font-weight: 500;
+  color: $brand-primary-deep;
+  margin-top: 6rpx;
+  letter-spacing: 1rpx;
+  line-height: 1.2;
+  white-space: nowrap;
 }
 
-/* ===== 理念卡(细边框表面区分,无单侧彩条) ===== */
+/* ===== 理念卡 ===== */
 .concept-card {
-  background: $ls-card;
-  border: 1rpx solid $ls-card-border;
-  border-radius: $ls-radius-card;
-  padding: $uni-space-xl;
-  margin-top: $uni-space-2xl;
-}
-.concept-desc {
-  font-size: calc($uni-font-size-base * var(--font-scale, 1));
-  color: $ls-ink-body;
-  line-height: 1.7;
-}
-.concept-highlight {
-  color: $ls-brand-deep;
-  font-weight: $uni-font-weight-bold;
+  background: rgba(249, 115, 22, 0.08);
+  border-radius: 24rpx;
+  padding: 24rpx;
+  margin-top: 28rpx;
 }
 .loop-row {
   display: flex;
   flex-direction: row;
   align-items: center;
   justify-content: space-between;
-  margin-top: $uni-space-xl;
-  flex-wrap: nowrap; // 保持四步一行
+  margin-top: 16rpx;
+  flex-wrap: nowrap;  /* 禁止换行,确保四步/五维在一行 */
+  margin-bottom: 16rpx;
 }
-.loop-step {
+.loop-pill {
   display: flex;
-  flex-direction: column;
+  flex-direction: row;
   align-items: center;
-  flex-shrink: 0;
-}
-.loop-glyph {
-  width: 56rpx;
-  height: 56rpx;
-  border-radius: 50%;
-  background-repeat: no-repeat;
-  background-position: center;
-  background-size: 28rpx 28rpx;
-}
-.loop-step-text {
-  margin-top: $uni-space-xs;
-  font-size: calc($uni-font-size-sm * var(--font-scale, 1));
-  color: $ls-ink-slate;
+  border-radius: 999rpx;
+  padding: 6rpx 12rpx;
+  font-size: calc(22rpx * var(--font-scale, 1));
+  flex-shrink: 0;  /* 防止被压缩导致文本换行 */
   white-space: nowrap;
 }
-// 四步闭环的色相:仅图标圆底着色,文字统一中性(去彩虹)
-.loop-glyph-wisdom {
-  background-color: $ls-dim-wisdom-tint;
-  background-image: ls-icon($ls-dim-wisdom, $ls-i-search);
+.loop-pill-indigo { background: rgba(99, 102, 241, 0.12); color: $dim-wisdom; }
+.loop-pill-green { background: rgba(16, 185, 129, 0.12); color: $status-success; }
+.loop-pill-amber { background: rgba(245, 158, 11, 0.12); color: $dim-wealth; }
+.loop-pill-red { background: rgba(239, 68, 68, 0.12); color: $status-danger; }
+.loop-pill-text {
+  margin-left: 4rpx;
+  white-space: nowrap;  /* 禁止四字步骤文本换行 */
 }
-.loop-glyph-success {
-  background-color: $status-success-tint;
-  background-image: ls-icon($status-success-deep, $ls-i-shield);
-}
-.loop-glyph-wealth {
-  background-color: $ls-dim-wealth-tint;
-  background-image: ls-icon($ls-dim-wealth, $ls-i-zap);
-}
-.loop-glyph-danger {
-  background-color: $status-danger-tint-2;
-  background-image: ls-icon($status-danger-strong, $ls-i-chart);
-}
-.loop-connector {
-  width: 20rpx;
-  height: 20rpx;
-  flex-shrink: 0;
-  background-repeat: no-repeat;
-  background-position: center;
-  background-size: 16rpx 16rpx;
-  background-image: ls-icon($ls-connector, $ls-i-chev);
+.loop-arrow {
+  font-size: calc(24rpx * var(--font-scale, 1));
+  color: $facade-text-ghost-light;
+  padding: 0 2rpx;
+  flex-shrink: 0;  /* 箭头不被压缩 */
 }
 
-/* ===== 五维标签带:统一容器 + 发丝分割 + 色块记号 ===== */
-.dims-band {
-  display: flex;
-  flex-direction: row;
-  align-items: stretch;
-  background: $ls-card;
-  border: 1rpx solid $ls-card-border;
-  border-radius: $ls-radius-card;
-  margin-top: $uni-space-lg;
-  padding: $uni-space-md 0;
-}
-.dim-cell {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
+/* 轨道环:缓慢旋转,呼应 Web 端虚线轨道 */
+.orbit-ring {
+  position: absolute;
+  left: 50%; top: 50%;
+  width: 240rpx; height: 240rpx;
+  margin-left: -120rpx; margin-top: -120rpx;
+  border: 2rpx dashed rgba(194, 112, 61, 0.35);
+  border-radius: 50%;
+  box-sizing: border-box;
+  animation: orbitRingSpin 24s linear infinite;
+}
+@keyframes orbitRingSpin {
+  0%   { transform: rotate(0deg); }
+  100% { transform: rotate(360deg); }
+}
+/* 中心"家"徽章:金色脉冲,呼应 Web 端中心节点 */
+.orbit-center {
+  position: absolute;
+  left: 50%; top: 50%;
+  width: 84rpx; height: 84rpx;
+  margin-left: -42rpx; margin-top: -42rpx;
+  border-radius: 50%;
+  background: #F59E0B;
+  display: flex; align-items: center; justify-content: center;
+  color: #fff; font-size: calc(36rpx * var(--font-scale, 1)); font-weight: 700;
+  box-shadow: 0 0 20rpx rgba(245, 158, 11, 0.4);
+  animation: orbitCenterPulse 3s ease-in-out infinite;
+  z-index: 3;
+}
+@keyframes orbitCenterPulse {
+  0%, 100% { box-shadow: 0 0 20rpx rgba(245, 158, 11, 0.4); transform: scale(1); }
+  50%      { box-shadow: 0 0 36rpx rgba(245, 158, 11, 0.6); transform: scale(1.06); }
+}
+/* 五维公转节点:绕中心旋转,文字保持正立 */
+.orbit-node {
+  position: absolute;
+  left: 50%; top: 50%;
+  width: 76rpx; height: 76rpx;
+  margin-left: -38rpx; margin-top: -38rpx;
+  border-radius: 50%;
+  background: var(--node-color);
+  display: flex; align-items: center; justify-content: center;
+  color: #fff; font-size: calc(30rpx * var(--font-scale, 1)); font-weight: 700;
+  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
+  z-index: 2;
+  animation-duration: 15s;
+  animation-timing-function: linear;
+  animation-iteration-count: infinite;
+}
+.orbit-node-char { line-height: 1; }
+/* 五节点:颜色 + 起始角度 + 错峰延迟,与 Web 端一致 */
+.node-0 { --node-color: #FF8C42; animation-name: orbitNode0; animation-delay: 0s; }    /* 身 */
+.node-1 { --node-color: #1677FF; animation-name: orbitNode1; animation-delay: -3s; }   /* 智 */
+.node-2 { --node-color: #F59E0B; animation-name: orbitNode2; animation-delay: -6s; }   /* 富 */
+.node-3 { --node-color: #10B981; animation-name: orbitNode3; animation-delay: -9s; }   /* 行 */
+.node-4 { --node-color: #FF6B9D; animation-name: orbitNode4; animation-delay: -12s; }  /* 心 */
+
+@keyframes orbitNode0 {
+  0%   { transform: rotate(0deg)   translateX(120rpx) rotate(0deg); }
+  100% { transform: rotate(360deg) translateX(120rpx) rotate(-360deg); }
 }
-.dim-cell + .dim-cell {
-  border-left: 1rpx solid $ls-divider;
+@keyframes orbitNode1 {
+  0%   { transform: rotate(72deg)   translateX(120rpx) rotate(-72deg); }
+  100% { transform: rotate(432deg)  translateX(120rpx) rotate(-432deg); }
 }
-.dim-glyph {
-  width: 36rpx;
-  height: 36rpx;
-  background-repeat: no-repeat;
-  background-position: center;
-  background-size: 30rpx 30rpx;
+@keyframes orbitNode2 {
+  0%   { transform: rotate(144deg)  translateX(120rpx) rotate(-144deg); }
+  100% { transform: rotate(504deg)  translateX(120rpx) rotate(-504deg); }
 }
-.dim-label {
-  margin-top: $uni-space-xs;
-  font-size: calc($uni-font-size-sm * var(--font-scale, 1));
-  color: $ls-ink-body;
-  white-space: nowrap;
+@keyframes orbitNode3 {
+  0%   { transform: rotate(216deg)  translateX(120rpx) rotate(-216deg); }
+  100% { transform: rotate(576deg)  translateX(120rpx) rotate(-576deg); }
+}
+@keyframes orbitNode4 {
+  0%   { transform: rotate(288deg)  translateX(120rpx) rotate(-288deg); }
+  100% { transform: rotate(648deg)  translateX(120rpx) rotate(-648deg); }
 }
-.dim-glyph-body   { background-image: ls-icon($ls-dim-body, $ls-i-user); }
-.dim-glyph-wisdom { background-image: ls-icon($ls-dim-wisdom, $ls-i-bulb); }
-.dim-glyph-wealth { background-image: ls-icon($ls-dim-wealth, $ls-i-trend); }
-.dim-glyph-action { background-image: ls-icon($ls-dim-action, $ls-i-activity); }
-.dim-glyph-mind   { background-image: ls-icon($ls-dim-mind, $ls-i-heart); }
-
-// 主CTA(白字需 4.5:1:品牌橙 #F97316 仅 2.80:1 不达标,取深橙档;渐变更暗端保证全段达标)
-$ls-cta-from: #D1440B;              // 深橙·亮端(白字 4.62:1 达标)
-$ls-cta-to:   $brand-primary-deep;  // 深橙·暗端 #C2410C(白字 5.18:1 达标)
-// hover / active 降档(白字 ≥6.0:1)
-$ls-cta-hover-from: #B93D0B;
-$ls-cta-hover-to:   #A9330A;
-$ls-cta-press-from: #A9330A;
-$ls-cta-press-to:   #962D08;
-// 禁用态:共享 $btn-disabled-bg(#D9D9D9) 对面板仅 1.39:1 不可辨、$btn-disabled-ink 3.78:1 不达 4.5
-// 故在组件局部覆盖为「暖灰底+深字」(不改全局令牌):底 #8E857A 对面板顶/底 3.58/3.32:1 达 ≥3、字 #1A1A1A 达 ≥4.5
-$ls-btn-disabled-bg:  #8E857A;
-$ls-btn-disabled-ink: $text-charcoal;   // #1A1A1A(uni.scss 既有令牌)
-// error/success 视觉态底色(R3 修复:白字需 ≥4.5)
-$ls-btn-error-bg:     $status-danger-strong;   // #B91C1C(白字 6.47:1 达标)
-$ls-btn-success-bg:   $status-success-strong;  // #065F46(白字 7.68:1 达标)
 
 /* ===== 登录表单 ===== */
 .login-form {
-  margin-top: $uni-space-2xl;
-}
-.phone-section {
-  display: flex;
-  justify-content: center;
+  margin-top: 32rpx;
 }
 .phone-btn {
   width: 100%;
-  height: 96rpx;
-  border-radius: $ls-radius-btn;
-  background: linear-gradient(135deg, $ls-cta-from 0%, $ls-cta-to 100%);
-  color: $ls-ink-on-brand;
-  font-size: calc($uni-font-size-lg * var(--font-scale, 1));
-  font-weight: $uni-font-weight-bold;
+  height: 82rpx;
+  border-radius: 24rpx;
+  background: linear-gradient(135deg, #FFC24A 0%, #FFA726 100%);
+  color: #14458F;
+  font-size: calc(32rpx * var(--font-scale, 1));
+  font-weight: 600;
   display: flex;
   flex-direction: row;
   align-items: center;
   justify-content: center;
   border: none;
-  box-sizing: border-box;
-  // 八态:default/hover/active/disabled/loading 走令牌;error/success 为待接线 class
-  transition: background-color $duration-fast $ease-standard,
-              transform $duration-fast $ease-standard,
-              opacity $duration-fast $ease-standard;
+  box-shadow: 0 8rpx 20rpx rgba(255, 167, 38, 0.32);
 }
 .phone-btn::after {
   border: none;
 }
-.phone-btn:hover {
-  background: linear-gradient(135deg, $ls-cta-hover-from 0%, $ls-cta-hover-to 100%); // H5 悬停:再降一档
-}
-.phone-btn:active {
-  transform: scale(0.98);
-  background: linear-gradient(135deg, $ls-cta-press-from 0%, $ls-cta-press-to 100%);
-}
-// R2 修复:键盘焦点可见环(H5 端)。mp-weixin 无键盘焦点,不影响小程序端。
-// 用品牌深橙描边对面板顶 5.10:1 ≥3:1(不纯黑);2px 描边 + 外扩,非内凹模糊
-.phone-btn:focus-visible {
-  outline: 4rpx solid $ls-brand-deep;
-  outline-offset: 4rpx;
-}
-.phone-btn[loading] {
-  background: linear-gradient(135deg, $ls-cta-from 0%, $ls-cta-to 100%); // 保持与 default 一致,spinner 由组件自带
-}
 .phone-btn[disabled] {
-  background: $ls-btn-disabled-bg !important;
-  color: $ls-btn-disabled-ink !important;
+  background: #FFEFD0 !important;
+  color: #B57E2A !important;
 }
-// error/success 视觉态(R2/R3 接线:模板 :class 绑定 errorState/successState,默认 false 不触发)
-.phone-btn--error {
-  background: $ls-btn-error-bg !important;
+.phone-btn-icon {
+  margin-right: 8rpx;
 }
-.phone-btn--success {
-  background: $ls-btn-success-bg !important;
+.phone-btn-text {
+  color: #000000;
 }
-.phone-btn[disabled].phone-btn--error,
-.phone-btn[disabled].phone-btn--success {
-  background: $ls-btn-disabled-bg !important; // 禁用态优先,error/success 不覆盖
+.phone-arrows {
+  margin-left: 10rpx;
+  font-weight: bold;
+  letter-spacing: 2rpx;
+  color: #000000;
+  animation: arrowBlink 1s ease-in-out infinite;
 }
-.phone-btn[disabled] .phone-btn-icon {
-  background-image: ls-icon($ls-btn-disabled-ink, $ls-i-phone);
+@keyframes arrowBlink {
+  0%, 100% { opacity: 1; }
+  50% { opacity: 0.15; }
 }
-.phone-btn--error .phone-btn-icon,
-.phone-btn--success .phone-btn-icon {
-  background-image: ls-icon($ls-ink-on-brand, $ls-i-phone); // 深底白图标,与白字一致
+
+/* ===== 暂不登录 ===== */
+.skip-section {
+  display: flex;
+  justify-content: center;
+  margin-top: 24rpx;
 }
-.phone-btn-icon {
-  width: 36rpx;
-  height: 36rpx;
-  margin-right: $uni-space-sm;
-  background-repeat: no-repeat;
-  background-position: center;
-  background-size: 32rpx 32rpx;
-  background-image: ls-icon($ls-ink-on-brand, $ls-i-phone);
+.skip-btn {
+  font-size: calc(26rpx * var(--font-scale, 1));
+  color: $facade-text-muted;
+  text-decoration: underline;
 }
 
-/* ===== 协议(R5:与品牌区同左锚点,不再居中) ===== */
+/* ===== 协议 ===== */
 .agreement {
   display: flex;
   flex-direction: row;
   align-items: center;
-  justify-content: flex-start;
+  justify-content: center;
   flex-wrap: wrap;
-  margin-top: $uni-space-lg;
-  font-size: calc($uni-font-size-sm * var(--font-scale, 1));
-  color: $ls-ink-slate;
+  margin-top: 24rpx;
+  font-size: calc(24rpx * var(--font-scale, 1));
+  color: $facade-text-muted;
 }
 .link {
-  color: $ls-brand-deep;
-  font-weight: $uni-font-weight-medium;
-}
-
-/* ===== 暂不登录(R5:左对齐) ===== */
-.skip-section {
-  display: flex;
-  justify-content: flex-start;
-  margin-top: $uni-space-lg;
-}
-.skip-btn {
-  font-size: calc($uni-font-size-base * var(--font-scale, 1));
-  color: $ls-ink-slate;
-  text-decoration: underline;
-  padding: $uni-space-xs $uni-space-md; // 扩大点击区
-}
-
-/* ===== 动效降级:尊重 reduced-motion ===== */
-@media (prefers-reduced-motion: reduce) {
-  .login-sheet-overlay,
-  .login-sheet-panel,
-  .phone-btn {
-    transition-duration: 0.01ms;
-  }
+  color: $brand-primary;
 }
-</style>
+</style>

+ 14 - 0
cfc-frontend/docs/redesign/STRATEGY.md

@@ -130,6 +130,20 @@ Task 页(健康报告、任务详情、测评…隐藏 TabBar,返回上级
 
 > 分享文案红线:所有分享 CTA 禁止"得积分/领奖励/解锁得积分"等利益诱导措辞,统一走情感与真实成果动机。
 
+#### CF 与分享的机制边界(合规优先 · 2026-11-15 定稿)
+- **原则**:CF(积分)仅作为"真实成长行为"的回报,与"点击分享"动作**零耦合**;分享是成就的自然外溢(情感动机),不得设为获取 CF 的门槛或回报,否则构成《微信小程序运营规范》禁止的诱导分享——利益通证(CF)绑定分享,风险为拒审 / 分享功能封禁 / 下架。
+- **CF 获取点(真实成长行为 → 给 CF)** 与 **分享触点(情感动机,零 CF 绑定)** 映射:
+
+| 维度 | CF 获取点(真实成长行为) | 分享触点(情感动机,零 CF 绑定) |
+|---|---|---|
+| 日常 | 每日单维打卡完成 | 打卡达成后「邀请朋友们一起看」分享卡 |
+| 里程碑 | 连续打卡 7 天 / 21 天 | 21 天庆祝页一键分享 |
+| 成就 | 五维达标 / 首次全达标 | 五维里程碑卡分享 |
+| 测评 | 完成 10 题肠道问卷 / 自检 | 周·月成长报告卡分享 |
+| 边界 | 任何"不分享就拿不到"的设定一律禁止 | 不得出现"得积分 / 解锁 / 领奖励"字眼 |
+
+- **正向强化建议**:在打卡完成态 / 达标页明确展示"CF +N 已入账"的记分牌式提示,做实"CF = 真实行为回报"心智,间接提升分享自驱力(用户分享的是已得成就,而非为 CF 去分享)。
+
 ---
 
 ## 8. 研发交接要点(接 `mobile-hifi-prototype` 交付)

+ 643 - 0
cfc-frontend/docs/redesign/prototype-redesign.html

@@ -0,0 +1,643 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
+<title>浠艾福 · 核心家庭体验闭环重设计(暖橙系 · 成就驱动)</title>
+<style>
+  /* =========================================================
+     核心家庭体验闭环重设计 —— 暖橙 Warm Calm Wellness 体系
+     品牌橙 #F97316 | 桃白底 #FFF7F1 | 深棕橙 #7C2D12
+     五维色严格取 flash 页:身 #FF8C42 / 心 #FF6B9D / 智 #6366F1 / 行 #10B981 / 富 #F59E0B
+     设计原则:成就驱动的家庭成长叙事 —— 看见成果 → 想继续 → 愿分享
+     动效克制:呼吸/脉冲/滑动/闪烁/生长,尊重 prefers-reduced-motion
+     ========================================================= */
+  :root{
+    --brand:#F97316; --brand-2:#EA580C; --brand-soft:#FFF1E6; --peach:#FFF7ED;
+    --hero-a:#FFF7ED; --hero-b:#FFEDD5; --hero-c:#FED7AA;
+    --dim-wisdom:#6366F1; --dim-mind:#FF6B9D; --dim-body:#FF8C42;
+    --dim-action:#10B981; --dim-wealth:#F59E0B;
+    --ink:#432918; --ink-2:#9A8473; --line:#F0E4D8; --surface-2:#FFF3E9;
+    --r:14px; --r-lg:16px;
+  }
+  *{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent;}
+  body{background:#EDE4DA;font-family:"PingFang SC","Microsoft YaHei",system-ui,sans-serif;
+       display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px;}
+  .phone{position:relative;width:375px;height:812px;background:var(--peach);border-radius:40px;
+         overflow:hidden;box-shadow:0 20px 60px rgba(80,45,15,.22);border:1px solid #F0E4D8;}
+  .screen{position:absolute;inset:0;display:flex;flex-direction:column;padding:0 16px;
+          overflow-y:auto;padding-bottom:74px;animation:fadeUp .5s ease-out both;}
+  .screen::-webkit-scrollbar{display:none;}
+  .hidden{display:none!important;}
+
+  /* 状态栏 */
+  .statusbar{height:44px;display:flex;align-items:center;justify-content:space-between;
+             padding:0 12px;color:var(--ink);font-size:15px;font-weight:600;flex-shrink:0;}
+  .battery{width:22px;height:11px;border:1.5px solid var(--ink);border-radius:3px;position:relative;}
+  .battery::after{content:"";position:absolute;right:-3px;top:3px;width:2px;height:5px;background:var(--ink);border-radius:1px;}
+  .battery::before{content:"";position:absolute;left:1.5px;top:1.5px;bottom:1.5px;width:13px;background:var(--ink);border-radius:1px;}
+
+  .apptitle{font-size:18px;font-weight:700;color:var(--ink);margin-top:16px;}
+  .date{text-align:right;color:var(--ink-2);font-size:13px;margin-top:-21px;margin-left:auto;width:120px;}
+
+  /* 能量英雄(桃橙渐变卡 · 保留 flash 页暖色基因) */
+  .hero{margin-top:18px;height:108px;border-radius:var(--r-lg);
+        background:linear-gradient(135deg,var(--hero-a),var(--hero-b) 55%,var(--hero-c));
+        padding:18px 20px;position:relative;overflow:hidden;flex-shrink:0;
+        border:1px solid #F6D9BE;}
+  .hero .label{color:var(--brand-ink);font-size:13px;font-weight:600;}
+  .hero .score{color:var(--brand-ink);font-size:40px;font-weight:800;line-height:1;margin-top:6px;
+               animation:breathe 3.2s ease-in-out infinite;}
+  .hero .unit{color:var(--brand-ink);font-size:14px;margin-left:6px;font-weight:600;}
+  .hero .dots{position:absolute;right:20px;top:64px;display:flex;gap:9px;}
+  .hero .dots i{width:12px;height:12px;border-radius:50%;display:block;animation:dotBreath 3.2s ease-in-out infinite;}
+  .hero .dots i:nth-child(2){animation-delay:.3s}
+  .hero .dots i:nth-child(3){animation-delay:.6s}
+  .hero .dots i:nth-child(4){animation-delay:.9s}
+  .hero .dots i:nth-child(5){animation-delay:1.2s}
+
+  /* 本周成果条(强对比记分牌式,突出"已获得的成果") */
+  .achieve{display:flex;gap:10px;margin-top:14px;flex-shrink:0;align-items:stretch;}
+  .ach{flex:1;background:#fff;border:1px solid var(--line);border-radius:12px;padding:12px 4px;text-align:center;
+       display:flex;flex-direction:column;box-shadow:0 2px 8px rgba(67,41,24,.05);animation:popIn .5s ease-out both;}
+  .ach:nth-child(2){animation-delay:.08s}
+  .ach:nth-child(3){animation-delay:.16s}
+  .ach-n{display:block;font-size:25px;font-weight:800;color:var(--ink);line-height:1.05;letter-spacing:-.5px;margin-top:auto;flex-shrink:0;}
+  .ach-l{display:block;font-size:11px;color:var(--ink-2);padding-top:7px;font-weight:500;flex-shrink:0;}
+
+  .ach.trophy{background:linear-gradient(180deg,#FFF8E8,#FCEFD2);border:1px solid #F5D98A;}
+  .ach.trophy .ach-n{color:#8A5A00;}
+  .ach.trophy .tro{width:24px;height:24px;margin:0 auto 3px;display:block;filter:drop-shadow(0 0 2px rgba(245,165,36,.35));
+       animation:trophyIn .7s .5s cubic-bezier(.22,1,.36,1) both,trophyFloat 3s 1.2s ease-in-out infinite,trophyGlow 2.4s 1.5s ease-in-out infinite;}
+
+  .sectitle{font-size:15px;font-weight:700;color:var(--ink);margin:26px 0 12px;flex-shrink:0;}
+  .sectitle.brand{color:var(--brand);font-size:13px;letter-spacing:.5px;font-weight:700;}
+
+  /* 本周主线卡 */
+  .mainline{border:1px solid var(--line);border-radius:var(--r);padding:14px 16px;background:#fff;}
+  .mainline .row{display:flex;justify-content:space-between;align-items:center;}
+  .mainline .topic{font-size:15px;font-weight:700;color:var(--ink);}
+  .mainline .prog{font-size:13px;color:var(--ink-2);}
+  .mainline .prog b{color:var(--brand);font-weight:800;}
+  .mainline .guide{font-size:12px;color:var(--brand);font-weight:600;margin-top:10px;animation:blink 2.4s ease-in-out infinite;}
+  .track{height:8px;border-radius:4px;background:var(--surface-2);margin-top:12px;overflow:hidden;}
+  .fill{height:100%;border-radius:4px;background:var(--brand);width:0;animation:barGrow 1.1s .25s cubic-bezier(.22,1,.36,1) forwards;}
+
+  /* 今日一站(橙色脉冲 + 箭头滑动,引导下一步) */
+  .nba{margin-top:18px;height:92px;border-radius:var(--r);background:linear-gradient(135deg,#FFAE6B,var(--brand) 70%,var(--brand-2));
+       padding:16px;position:relative;color:#fff;animation:pulseGlow 2.4s ease-in-out infinite;}
+  .nba .kicker{font-size:12px;color:#FFF1E0;font-weight:600;}
+  .nba .title{font-size:19px;font-weight:800;margin-top:8px;letter-spacing:.3px;}
+  .nba .sub{font-size:12px;color:#FFF1E0;margin-top:6px;}
+  .nba .sub .gain{background:rgba(255,255,255,.28);padding:1px 8px;border-radius:7px;font-weight:800;}
+  .nba .arrow{position:absolute;right:18px;top:34px;font-size:24px;font-weight:700;animation:slideX 1.6s ease-in-out infinite;}
+
+  /* 家庭动态 */
+  .dyn{border:1px solid var(--line);border-radius:var(--r);padding:6px 16px;background:#fff;}
+  .dyn .item{display:flex;align-items:center;height:36px;font-size:14px;color:#5A4736;border-bottom:1px solid var(--surface-2);}
+  .dyn .item:last-child{border-bottom:none;}
+  .dyn .news{width:8px;height:8px;border-radius:50%;background:var(--dim-mind);margin-right:8px;animation:dotBlink 1.6s ease-in-out infinite;}
+
+  /* 成长卡 */
+  .growth{border:1px solid var(--line);border-radius:var(--r);background:var(--surface-2);padding:16px;margin-top:18px;}
+  .growth .mini{background:linear-gradient(135deg,#FFAE6B,var(--brand) 70%,var(--brand-2));border-radius:10px;padding:16px 20px;color:#fff;margin-bottom:14px;}
+  .growth .mini .t{font-size:15px;font-weight:800;}
+  .growth .mini .gain{font-size:18px;}
+  .growth .mini .s{font-size:12px;color:#FFF1E0;margin-top:4px;}
+  /* 分享 CTA:与今日一站橙色同源渐变 */
+  .growth .btn{border:none;color:#fff;background:linear-gradient(135deg,#FFBE7A 0%,var(--brand) 48%,var(--brand-2) 100%);
+               border-radius:999px;height:48px;display:flex;align-items:center;justify-content:center;gap:8px;
+               font-size:16px;font-weight:800;width:100%;margin:0 auto;cursor:pointer;letter-spacing:.3px;position:relative;
+               overflow:hidden;box-shadow:0 8px 22px rgba(249,115,22,.30);border:1px solid rgba(255,255,255,.3);
+               animation:sharePulse 2.4s ease-in-out infinite;}
+  .growth .btn svg{width:18px;height:18px;flex-shrink:0;position:relative;z-index:1;}
+  .growth .btn .txt{position:relative;z-index:1;text-shadow:0 1px 4px rgba(124,45,18,.4);padding-bottom:1px;}
+  .growth .btn .shine{position:absolute;top:0;left:-60%;width:50%;height:100%;z-index:0;background:linear-gradient(100deg,transparent,rgba(255,255,255,.55),transparent);transform:skewX(-18deg);animation:shareShine 3s ease-in-out infinite;}
+  .growth .btn:active{transform:scale(.97);}
+
+  /* TabBar */
+  .tabbar{position:absolute;left:0;right:0;bottom:0;height:54px;background:#fff;border-top:1px solid var(--line);
+          display:flex;align-items:center;padding:0 26px;justify-content:space-between;z-index:6;}
+  .tab{font-size:12px;color:var(--ink-2);cursor:pointer;}
+  .tab.active{color:var(--brand);font-weight:700;}
+  .tab-center{width:42px;height:42px;border-radius:50%;background:linear-gradient(135deg,#FFAE6B,var(--brand) 70%,var(--brand-2));
+              color:#fff;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;
+              position:absolute;left:50%;transform:translateX(-50%);bottom:8px;cursor:pointer;box-shadow:0 6px 16px rgba(249,115,22,.4);}
+
+  /* 抽屉 + 遮罩 */
+  .overlay{position:absolute;inset:0;background:rgba(67,41,24,.42);opacity:0;transition:opacity .3s ease;pointer-events:none;z-index:7;}
+  .overlay.show{opacity:1;pointer-events:auto;}
+  .sheet{position:absolute;left:0;right:0;bottom:0;height:430px;background:#fff;border-radius:20px 20px 0 0;padding:10px 16px 16px;
+         transform:translateY(100%);transition:transform .34s cubic-bezier(.22,1,.36,1);z-index:8;}
+  .sheet.show{transform:translateY(0);}
+  .handle{width:41px;height:4px;border-radius:2px;background:#E5D6C8;margin:6px auto 14px;}
+  .sheet h3{font-size:18px;font-weight:700;color:var(--ink);}
+  .drow{display:flex;align-items:center;height:50px;border-bottom:1px solid var(--surface-2);cursor:pointer;}
+  .drow .dot{width:12px;height:12px;border-radius:50%;margin-right:14px;flex-shrink:0;}
+  .drow .name{font-size:14px;color:#5A4736;flex:1;}
+  .drow .val{font-size:15px;font-weight:700;color:var(--ink);}
+  .drow .go{color:var(--ink-2);font-size:18px;margin-left:6px;}
+  .expand{margin-top:18px;height:46px;border-radius:999px;background:linear-gradient(135deg,#FFAE6B,var(--brand) 70%,var(--brand-2));color:#fff;
+          display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:700;cursor:pointer;box-shadow:0 6px 16px rgba(249,115,22,.3);}
+
+  /* 全屏沙盘页 / 维度详情 / 报告分享(横向滑入层) */
+  .layer{position:absolute;inset:0;background:var(--peach);padding:0 16px;transform:translateX(100%);
+         transition:transform .34s cubic-bezier(.22,1,.36,1);z-index:9;overflow-y:auto;padding-bottom:24px;}
+  .layer::-webkit-scrollbar{display:none;}
+  .layer.show{transform:translateX(0);}
+  .sb-head{height:44px;display:flex;align-items:center;justify-content:space-between;margin-top:14px;}
+  .sb-back{font-size:26px;font-weight:700;color:var(--ink);cursor:pointer;}
+  .sb-title{font-size:18px;font-weight:700;color:var(--ink);}
+  .radarcard{border:1px solid var(--line);border-radius:var(--r-lg);height:300px;margin-top:18px;display:flex;align-items:center;justify-content:center;background:#fff;}
+  .radarcard svg{animation:radarIn .8s ease-out both;cursor:pointer;}
+  .trend{display:flex;gap:12px;margin-top:18px;}
+  .tcard{flex:1;border:1px solid var(--line);border-radius:var(--r);padding:14px;background:#fff;cursor:pointer;}
+  .tcard .n{font-size:14px;font-weight:700;}
+  .tcard .d{font-size:26px;font-weight:800;color:var(--dim-action);margin-top:8px;}
+  .sug{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface-2);padding:18px 20px;margin-top:18px;margin-bottom:10px;}
+  .sug .si{position:relative;padding-left:12px;margin-bottom:16px;}
+  .sug .si:last-child{margin-bottom:0;}
+  .sug .si::before{content:"";position:absolute;left:0;top:2px;width:4px;height:42px;border-radius:2px;}
+  .sug .si.w::before{background:var(--dim-wealth);}
+  .sug .si.b::before{background:var(--dim-body);}
+  .sug .si .st{font-size:15px;font-weight:700;color:var(--ink);}
+  .sug .si .sd{font-size:13px;color:var(--ink-2);margin-top:6px;line-height:1.5;}
+
+  /* —— 客户名突出 + 图标 —— */
+  .name-hl{display:inline-flex;align-items:center;gap:5px;font-weight:700;line-height:1.2;}
+  .name-hl svg{width:16px;height:16px;flex-shrink:0;}
+  .name-hl .nm{color:var(--brand);}
+  .name-hl.gold .nm{color:var(--dim-wealth);}
+
+  /* —— 维度字突出 —— */
+  .dim{color:var(--brand);font-weight:700;}
+  /* —— 焦点维度「智」增强 —— */
+  .dim-w{color:var(--dim-wisdom);font-weight:700;}
+  .dim-pill{display:inline-block;padding:1px 9px;border-radius:8px;background:#ECEBFD;color:var(--dim-wisdom);font-weight:700;margin:0 1px;animation:wisdomGlow 2.6s ease-in-out infinite;}
+  @keyframes wisdomGlow{0%,100%{box-shadow:0 0 0 rgba(99,102,241,0)}50%{box-shadow:0 0 12px rgba(99,102,241,.5)}}
+
+  /* —— + 数字突出 —— */
+  .gain{color:var(--dim-action);font-weight:800;font-variant-numeric:tabular-nums;}
+  .ach .gain{font-size:25px;animation:gainPulse 2.4s ease-in-out infinite;}
+  @keyframes gainPulse{0%,100%{text-shadow:0 0 0 rgba(16,185,129,0)}50%{text-shadow:0 0 12px rgba(16,185,129,.45)}}
+  .tcard .d{animation:popIn .5s ease-out both;}
+
+  /* —— 连续打卡「21」突出 —— */
+  .streak-num{font-size:17px;font-weight:800;color:var(--dim-wealth);font-variant-numeric:tabular-nums;padding:0 1px;animation:streakPop .6s .45s cubic-bezier(.22,1,.36,1) both;}
+  @keyframes streakPop{0%{opacity:0;transform:scale(.5)}60%{transform:scale(1.18)}100%{opacity:1;transform:none}}
+
+  /* —— 全家五维首次全达标 · 庆典 —— */
+  .celebrate{margin-top:12px;border-radius:14px;padding:14px 16px;display:flex;align-items:center;gap:12px;position:relative;overflow:hidden;color:#6B4A00;
+             background:linear-gradient(115deg,#FFF6DC,#FFE2A0,#FFF6DC);background-size:220% 100%;border:1px solid #F2D07A;
+             box-shadow:0 6px 18px rgba(245,165,36,.28);animation:celeIn .7s .3s cubic-bezier(.22,1,.36,1) both,celeGlow 2.6s 1s ease-in-out infinite;}
+  .celebrate .shine{position:absolute;top:0;left:-60%;width:55%;height:100%;z-index:0;background:linear-gradient(100deg,transparent,rgba(255,255,255,.9),transparent);transform:skewX(-18deg);animation:shine 2.8s 1.1s ease-in-out infinite;}
+  .celebrate .medal{width:32px;height:32px;flex-shrink:0;position:relative;z-index:1;filter:drop-shadow(0 2px 3px rgba(180,120,0,.35));animation:medalFloat 3s ease-in-out infinite;}
+  .celebrate .ct{font-size:14px;font-weight:700;line-height:1.45;position:relative;z-index:1;}
+  .celebrate .first{display:inline-block;background:#fff;color:#D9880C;font-size:11px;font-weight:800;padding:1px 7px;border-radius:6px;margin:0 3px;vertical-align:middle;box-shadow:0 1px 2px rgba(180,120,0,.25);animation:firstPulse 2s ease-in-out infinite;}
+
+  /* ===== 健康屏 ===== */
+  .hg-grid{display:flex;gap:12px;margin-top:14px;}
+  .hg-ent{flex:1;border:1px solid var(--line);border-radius:var(--r);padding:16px 14px;background:#fff;cursor:pointer;text-align:center;}
+  .hg-ent .ic{width:34px;height:34px;margin:0 auto 8px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:var(--brand-soft);}
+  .hg-ent .ic svg{width:20px;height:20px;}
+  .hg-ent .t{font-size:13px;font-weight:700;color:var(--ink);}
+  .hg-ent .s{font-size:11px;color:var(--ink-2);margin-top:3px;}
+  .checkin{display:flex;gap:10px;margin-top:14px;overflow-x:auto;padding-bottom:4px;}
+  .checkin::-webkit-scrollbar{display:none;}
+  .ck{flex:0 0 auto;width:84px;border:1px solid var(--line);border-radius:12px;padding:12px 0;background:#fff;text-align:center;cursor:pointer;}
+  .ck .ic{width:28px;height:28px;margin:0 auto 6px;}
+  .ck .t{font-size:12px;color:#5A4736;font-weight:600;}
+  .rec{margin-top:14px;border:1px solid var(--line);border-radius:var(--r);background:#fff;overflow:hidden;}
+  .rec .ri{display:flex;align-items:center;height:50px;padding:0 16px;border-bottom:1px solid var(--surface-2);font-size:13px;color:#5A4736;}
+  .rec .ri:last-child{border-bottom:none;}
+  .rec .ri .dot{width:8px;height:8px;border-radius:50%;margin-right:10px;}
+  .rec .ri .tm{margin-left:auto;color:var(--ink-2);font-size:11px;}
+
+  /* ===== 成长屏 · 成就 Hero ===== */
+  .ach-hero{margin-top:16px;border-radius:var(--r-lg);padding:18px;background:linear-gradient(135deg,var(--hero-a),var(--hero-b) 55%,var(--hero-c));border:1px solid #F6D9BE;display:flex;align-items:center;gap:16px;}
+  .ring{width:84px;height:84px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:conic-gradient(var(--brand) 0% 71%,#F0D9C2 71% 100%);position:relative;}
+  .ring::after{content:"";position:absolute;inset:9px;border-radius:50%;background:linear-gradient(135deg,#FFF7ED,#FFEDD5);}
+  .ring .rt{position:relative;z-index:1;text-align:center;}
+  .ring .rt b{font-size:22px;font-weight:800;color:var(--brand-ink);}
+  .ring .rt span{display:block;font-size:10px;color:var(--brand-ink);}
+  .ach-stats{flex:1;display:flex;flex-direction:column;gap:8px;}
+  .ach-stats .as{display:flex;justify-content:space-between;font-size:13px;color:var(--brand-ink);}
+  .ach-stats .as b{font-weight:800;font-size:16px;}
+  .fire{display:flex;gap:5px;margin-top:4px;}
+  .fire i{width:20px;height:20px;border-radius:5px;background:#F0D9C2;display:flex;align-items:center;justify-content:center;font-size:11px;}
+  .fire i.on{background:linear-gradient(135deg,#FFAE6B,var(--brand));color:#fff;animation:firePop .4s ease-out both;}
+  .fire i.today{box-shadow:0 0 0 2px var(--brand);}
+  .tasks{margin-top:16px;}
+  .tk{display:flex;align-items:center;height:54px;border:1px solid var(--line);border-radius:12px;background:#fff;padding:0 14px;margin-bottom:10px;}
+  .tk .cb{width:22px;height:22px;border-radius:7px;border:2px solid var(--line);flex-shrink:0;cursor:pointer;display:flex;align-items:center;justify-content:center;}
+  .tk.done .cb{background:var(--dim-action);border-color:var(--dim-action);}
+  .tk.done .cb svg{width:13px;height:13px;}
+  .tk .ti{flex:1;margin-left:12px;font-size:14px;color:#5A4736;font-weight:600;}
+  .tk.done .ti{color:var(--ink-2);text-decoration:line-through;}
+  .tk .tg{font-size:12px;color:var(--dim-action);font-weight:800;}
+  .unlock{margin-top:6px;border-radius:14px;padding:14px 16px;display:flex;align-items:center;gap:12px;color:#6B4A00;
+          background:linear-gradient(115deg,#FFF6DC,#FFE2A0,#FFF6DC);background-size:220% 100%;border:1px solid #F2D07A;
+          box-shadow:0 6px 18px rgba(245,165,36,.28);animation:celeIn .7s cubic-bezier(.22,1,.36,1) both,celeGlow 2.6s 1s ease-in-out infinite;}
+  .unlock .medal{width:30px;height:30px;flex-shrink:0;}
+  .unlock .ut{font-size:13px;font-weight:700;line-height:1.4;}
+  .unlock .usub{font-size:11px;color:#8A6A20;margin-top:2px;}
+  .unlock .ug{margin-left:auto;font-size:12px;font-weight:800;color:#D9880C;}
+
+  /* ===== 我的屏 ===== */
+  .idcard{margin-top:16px;border-radius:var(--r-lg);padding:16px;background:linear-gradient(135deg,#FFAE6B,var(--brand) 70%,var(--brand-2));color:#fff;display:flex;align-items:center;gap:14px;}
+  .idcard .av{width:52px;height:52px;border-radius:50%;background:rgba(255,255,255,.25);display:flex;align-items:center;justify-content:center;font-size:22px;font-weight:800;}
+  .idcard .nm{font-size:18px;font-weight:800;}
+  .idcard .rl{font-size:12px;color:#FFF1E0;margin-top:3px;}
+  .members{display:flex;gap:0;margin-top:14px;}
+  .mb{flex:1;text-align:center;cursor:pointer;}
+  .mb .av{width:46px;height:46px;border-radius:50%;margin:0 auto 6px;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:16px;}
+  .mb .nm{font-size:12px;color:#5A4736;font-weight:600;}
+  .mb .sc{font-size:11px;color:var(--ink-2);}
+  .menu{margin-top:18px;border:1px solid var(--line);border-radius:var(--r);background:#fff;overflow:hidden;}
+  .menu .mi{display:flex;align-items:center;height:50px;padding:0 16px;border-bottom:1px solid var(--surface-2);font-size:14px;color:#5A4736;cursor:pointer;}
+  .menu .mi:last-child{border-bottom:none;}
+  .menu .mi .ic{width:22px;height:22px;margin-right:12px;color:var(--brand);}
+  .menu .mi .go{margin-left:auto;color:var(--ink-2);font-size:18px;}
+
+  /* ===== 维度详情 ===== */
+  .dim-hero{border-radius:var(--r-lg);padding:20px;margin-top:16px;color:#fff;display:flex;align-items:center;gap:16px;}
+  .dim-hero .big{font-size:44px;font-weight:800;line-height:1;}
+  .dim-hero .lab{font-size:14px;font-weight:600;opacity:.92;}
+  .dim-hero .desc{font-size:12px;opacity:.85;margin-top:4px;}
+  .kpi{display:flex;gap:12px;margin-top:16px;}
+  .kpi .k{flex:1;border:1px solid var(--line);border-radius:12px;padding:12px;background:#fff;text-align:center;}
+  .kpi .k .v{font-size:18px;font-weight:800;color:var(--ink);}
+  .kpi .k .l{font-size:11px;color:var(--ink-2);margin-top:3px;}
+  .acts{margin-top:16px;border:1px solid var(--line);border-radius:var(--r);background:#fff;padding:6px 16px;}
+  .act{display:flex;align-items:center;height:48px;border-bottom:1px solid var(--surface-2);font-size:13px;color:#5A4736;}
+  .act:last-child{border-bottom:none;}
+  .act .cb{width:20px;height:20px;border-radius:6px;border:2px solid var(--line);margin-right:12px;flex-shrink:0;}
+  .act.done .cb{background:var(--dim-action);border-color:var(--dim-action);display:flex;align-items:center;justify-content:center;}
+  .act.done .cb svg{width:12px;height:12px;}
+  .act.done{color:var(--ink-2);text-decoration:line-through;}
+
+  /* ===== 成长报告分享卡 ===== */
+  .report{position:absolute;inset:0;background:rgba(67,41,24,.55);z-index:11;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .3s ease;}
+  .report.show{opacity:1;pointer-events:auto;}
+  .rcard{width:300px;border-radius:20px;background:linear-gradient(160deg,#FFFBF5,#FFF1E0);padding:22px 20px;box-shadow:0 20px 50px rgba(80,45,15,.4);transform:scale(.9);transition:transform .3s cubic-bezier(.22,1,.36,1);overflow:hidden;}
+  .report.show .rcard{transform:scale(1);}
+  .rcard .rt{font-size:17px;font-weight:800;color:var(--brand-ink);text-align:center;}
+  .rcard .rs{font-size:12px;color:var(--ink-2);text-align:center;margin-top:4px;}
+  .rscore{display:flex;justify-content:space-around;margin-top:16px;}
+  .rscore .rsi{text-align:center;}
+  .rscore .rsi b{font-size:22px;font-weight:800;}
+  .rscore .rsi span{display:block;font-size:11px;color:var(--ink-2);margin-top:2px;}
+  .rfire{display:flex;justify-content:center;gap:5px;margin-top:14px;}
+  .rfire i{width:22px;height:22px;border-radius:6px;background:linear-gradient(135deg,#FFAE6B,var(--brand));color:#fff;display:flex;align-items:center;justify-content:center;font-size:11px;}
+  .rsum{display:flex;justify-content:space-around;margin-top:14px;border-top:1px solid var(--line);padding-top:14px;}
+  .rsum .rm{text-align:center;}
+  .rsum .rm b{font-size:18px;font-weight:800;color:var(--brand-ink);}
+  .rsum .rm span{display:block;font-size:11px;color:var(--ink-2);margin-top:2px;}
+  .rbtn{margin-top:18px;height:46px;border-radius:999px;background:linear-gradient(135deg,#FFBE7A,var(--brand) 48%,var(--brand-2));color:#fff;display:flex;align-items:center;justify-content:center;font-size:15px;font-weight:800;gap:8px;box-shadow:0 8px 22px rgba(249,115,22,.35);position:relative;overflow:hidden;}
+  .rbtn svg{width:18px;height:18px;}
+  .rclose{text-align:center;color:var(--ink-2);font-size:13px;margin-top:12px;cursor:pointer;}
+
+  /* ===== 关键帧 ===== */
+  @keyframes fadeUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
+  @keyframes popIn{from{opacity:0;transform:translateY(8px) scale(.96)}to{opacity:1;transform:none}}
+  @keyframes breathe{0%,100%{text-shadow:0 0 0 rgba(124,45,18,0)}50%{text-shadow:0 0 14px rgba(124,45,18,.35)}}
+  @keyframes dotBreath{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(.82);opacity:.7}}
+  @keyframes pulseGlow{0%,100%{box-shadow:0 8px 22px rgba(249,115,22,.3)}50%{box-shadow:0 8px 34px rgba(249,115,22,.6)}}
+  @frames blink{0%,100%{opacity:1}50%{opacity:.55}}
+  @keyframes dotBlink{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(.55);opacity:.45}}
+  @keyframes slideX{0%,100%{transform:translateX(0)}50%{transform:translateX(7px)}}
+  @keyframes barGrow{from{width:0}to{width:60%}}
+  @keyframes radarIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:none}}
+  @keyframes trophyIn{0%{opacity:0;transform:translateY(-12px) scale(.4) rotate(-30deg)}70%{transform:translateY(2px) scale(1.08) rotate(6deg)}100%{opacity:1;transform:none}}
+  @keyframes trophyFloat{0%,100%{transform:translateY(0) rotate(-3deg)}50%{transform:translateY(-3px) rotate(3deg)}}
+  @keyframes trophyGlow{0%,100%{filter:drop-shadow(0 0 2px rgba(245,165,36,.35))}50%{filter:drop-shadow(0 0 8px rgba(245,165,36,.75))}}
+  @keyframes sharePulse{0%,100%{transform:scale(1);box-shadow:0 8px 22px rgba(249,115,22,.3)}50%{transform:scale(1.025);box-shadow:0 8px 34px rgba(249,115,22,.6)}}
+  @keyframes shareShine{0%{left:-60%}55%,100%{left:135%}}
+  @keyframes celeIn{0%{opacity:0;transform:translateY(12px) scale(.94)}100%{opacity:1;transform:none}}
+  @keyframes celeGlow{0%,100%{box-shadow:0 6px 18px rgba(245,165,36,.28)}50%{box-shadow:0 8px 30px rgba(245,165,36,.6)}}
+  @keyframes shine{0%{left:-60%}55%,100%{left:135%}}
+  @keyframes medalFloat{0%,100%{transform:translateY(0) rotate(-4deg)}50%{transform:translateY(-3px) rotate(4deg)}}
+  @keyframes firstPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.08)}}
+  @keyframes firePop{0%{transform:scale(.6)}100%{transform:none}}
+  @media (prefers-reduced-motion: reduce){
+    *{animation:none!important;transition:none!important;}
+    .fill{width:60%!important;}
+  }
+</style>
+</head>
+<body>
+  <div class="phone">
+    <!-- ============ 首页 ============ -->
+    <div class="screen" id="home">
+      <div class="statusbar"><span>9:41</span><div class="battery"></div></div>
+      <div class="apptitle">浠艾福</div>
+      <div class="date">10月6日 周二</div>
+
+      <div class="hero">
+        <div class="label">全家五<span style="color:#C2410C;font-weight:800">维</span>能量</div>
+        <div><span class="score">78</span><span class="unit">分</span></div>
+        <div class="dots">
+          <i style="background:var(--dim-wisdom)"></i><i style="background:var(--dim-mind)"></i>
+          <i style="background:var(--dim-body)"></i><i style="background:var(--dim-action)"></i><i style="background:var(--dim-wealth)"></i>
+        </div>
+      </div>
+
+      <div class="achieve">
+        <div class="ach"><span class="ach-n"><span class="gain">+62</span></span><span class="ach-l">本周成长值</span></div>
+        <div class="ach"><span class="ach-n">×3</span><span class="ach-l">解锁徽章</span></div>
+        <div class="ach trophy"><svg class="tro" viewBox="0 0 24 24"><defs><linearGradient id="tg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#FCE08A"/><stop offset="1" stop-color="#F5A524"/></linearGradient></defs><path d="M7 4h10v4a5 5 0 0 1-10 0V4z" fill="url(#tg)"/><path d="M7 5H4v2a3 3 0 0 0 3 3" fill="none" stroke="#E0920F" stroke-width="1.3"/><path d="M17 5h3v2a3 3 0 0 1-3 3" fill="none" stroke="#E0920F" stroke-width="1.3"/><path d="M12 13v3" stroke="#E0920F" stroke-width="1.3"/><rect x="10" y="15" width="4" height="2.4" rx=".5" fill="url(#tg)"/><path d="M8.6 17.4h6.8l.8 3.2H7.8z" fill="url(#tg)"/></svg><span class="ach-n">5/5</span><span class="ach-l">全达标</span></div>
+      </div>
+
+      <div class="sectitle brand">本周成长主线</div>
+      <div class="mainline">
+        <div class="row"><span class="topic"><span class="name-hl"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="#F97316"/><circle cx="9" cy="10" r="1.4" fill="#fff"/><circle cx="15" cy="10" r="1.4" fill="#fff"/><path d="M8 14.5c1.2 1.6 6.8 1.6 8 0" stroke="#fff" stroke-width="1.6" fill="none" stroke-linecap="round"/></svg><span class="nm">小宇</span></span> 的<span class="dim-pill">智</span>维突破</span><span class="prog"><b>3</b>/5</span></div>
+        <div class="guide">还差 2 站 · 解锁本周<span class="dim-w">智</span>维徽章 ›</div>
+        <div class="track"><div class="fill"></div></div>
+      </div>
+
+      <div class="nba">
+        <div class="kicker">下一步 · 今日一站</div>
+        <div class="title">完成数学挑战</div>
+        <div class="sub">完成后 <span class="gain">+20</span> 成长值</div>
+        <div class="arrow">›</div>
+      </div>
+
+      <div class="sectitle">家庭动态</div>
+      <div class="dyn">
+        <div class="item"><span class="news"></span><span style="color:var(--dim-wealth);font-weight:700">小柚</span> 连续打卡 <span class="streak-num">21</span> 天</div>
+      </div>
+      <div class="celebrate">
+        <svg class="medal" viewBox="0 0 24 24"><defs><linearGradient id="mg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#FCE08A"/><stop offset="1" stop-color="#F5A524"/></linearGradient></defs><path d="M9.2 4l1.6 3.2M14.8 4l-1.6 3.2" stroke="#E0920F" stroke-width="1.4" stroke-linecap="round"/><circle cx="12" cy="13.5" r="7" fill="url(#mg)" stroke="#E0920F" stroke-width="1"/><path d="M12 9.8l1.35 2.8 3.05.45-2.2 2.15.52 3.05L12 18.9l-2.72 1.45.52-3.05-2.2-2.15 3.05-.45z" fill="#fff"/></svg>
+        <span class="ct">本周 · 全家五维 <span class="first">首次</span> 全达标</span>
+        <span class="shine"></span>
+      </div>
+
+      <div class="sectitle">本周家庭成长卡</div>
+      <div class="growth">
+        <div class="mini"><div class="t">五维全达标 · <span style="color:#fff;font-weight:800">智</span> <span class="gain">+12</span></div><div class="s">本周家庭成长报告 · 朋友们都在期待这一刻</div></div>
+        <div class="btn" id="shareFromHome"><svg viewBox="0 0 24 24"><path d="M2 21l21-9L2 3v7l15 2-15 2z" fill="#fff"/></svg><span class="txt">成果分享 · 邀请朋友们一起看 ›</span><span class="shine"></span></div>
+      </div>
+    </div>
+
+    <!-- ============ 健康 ============ -->
+    <div class="screen hidden" id="health">
+      <div class="statusbar"><span>9:41</span><div class="battery"></div></div>
+      <div class="apptitle">健康</div>
+      <div class="date">数据中心</div>
+
+      <div class="hg-grid">
+        <div class="hg-ent" id="toReport"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="#F97316" stroke-width="1.8"><path d="M12 3v12m0 0l-4-4m4 4l4-4M5 17v2a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-2"/></svg></div><div class="t">上传报告</div><div class="s">拍照/相册</div></div>
+        <div class="hg-ent" id="toAssess"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="#F97316" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></div><div class="t">在线测评</div><div class="s">认知/身心</div></div>
+      </div>
+
+      <div class="sectitle brand">五维能量一眼看</div>
+      <div class="radarcard" id="miniRadar" style="height:240px;cursor:pointer">
+        <svg width="210" height="210" viewBox="0 0 260 260" fill="none">
+          <polygon points="130,30 213,99 174,201 86,201 47,99" fill="none" stroke="#F0E4D8"/>
+          <polygon points="130,80 176,124 153,170 107,170 84,124" fill="none" stroke="#F6E9DC"/>
+          <line x1="130" y1="130" x2="130" y2="30" stroke="#F0E4D8"/><line x1="130" y1="130" x2="213" y2="99" stroke="#F0E4D8"/><line x1="130" y1="130" x2="174" y2="201" stroke="#F0E4D8"/><line x1="130" y1="130" x2="86" y2="201" stroke="#F0E4D8"/><line x1="130" y1="130" x2="47" y2="99" stroke="#F0E4D8"/>
+          <polygon points="130,48 201,107 170,185 88,187 55,106" fill="#F97316" fill-opacity="0.18" stroke="#F97316" stroke-width="2"/>
+        </svg>
+      </div>
+
+      <div class="sectitle">今日打卡</div>
+      <div class="checkin">
+        <div class="ck"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="#FF8C42" stroke-width="1.8"><path d="M5 12h14M12 5v14"/></svg></div><div class="t">饮食</div></div>
+        <div class="ck"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="1.8"><circle cx="12" cy="12" r="8"/><path d="M12 8v4l3 2"/></svg></div><div class="t">运动</div></div>
+        <div class="ck"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="#6366F1" stroke-width="1.8"><path d="M21 12a9 9 0 1 1-9-9"/></svg></div><div class="t">睡眠</div></div>
+        <div class="ck"><div class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="#FF6B9D" stroke-width="1.8"><path d="M12 21s-7-4.5-7-10a4 4 0 0 1 7-2 4 4 0 0 1 7 2c0 5.5-7 10-7 10z"/></svg></div><div class="t">情绪</div></div>
+      </div>
+
+      <div class="sectitle">最近记录</div>
+      <div class="rec">
+        <div class="ri"><span class="dot" style="background:var(--dim-body)"></span>小宇 · 饮食打卡<div class="tm">10分钟前</div></div>
+        <div class="ri"><span class="dot" style="background:var(--dim-action)"></span>小柚 · 运动 30 分钟<div class="tm">1小时前</div></div>
+        <div class="ri"><span class="dot" style="background:var(--dim-mind)"></span>妈妈 · 情绪记录<div class="tm">今天</div></div>
+      </div>
+    </div>
+
+    <!-- ============ 成长记录 ============ -->
+    <div class="screen hidden" id="growth">
+      <div class="statusbar"><span>9:41</span><div class="battery"></div></div>
+      <div class="apptitle">成长记录</div>
+      <div class="date">本周成就</div>
+
+      <div class="ach-hero">
+        <div class="ring"><div class="rt"><b>5/7</b><span>本周完成</span></div></div>
+        <div class="ach-stats">
+          <div class="as"><span>完成任务</span><b>12</b></div>
+          <div class="as"><span>成长分</span><b>340</b></div>
+          <div class="as"><span>解锁徽章</span><b>8</b></div>
+          <div class="fire">
+            <i class="on">🔥</i><i class="on">🔥</i><i class="on">🔥</i><i class="on">🔥</i><i class="on">🔥</i><i class="on">🔥</i><i class="today on">🔥</i>
+          </div>
+        </div>
+      </div>
+
+      <div class="sectitle brand">今日成长 · 还差 2 项解锁本周全勤</div>
+      <div class="tasks" id="taskList">
+        <div class="tk done"><div class="cb"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><path d="M5 12l4 4 10-10"/></svg></div><div class="ti">小宇 · 数学挑战</div><div class="tg">+20</div></div>
+        <div class="tk done"><div class="cb"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><path d="M5 12l4 4 10-10"/></svg></div><div class="ti">全家 · 晚餐共餐</div><div class="tg">+15</div></div>
+        <div class="tk" data-gain="20"><div class="cb"></div><div class="ti">小柚 · 亲子阅读</div><div class="tg">+20</div></div>
+        <div class="tk" data-gain="15"><div class="cb"></div><div class="ti">妈妈 · 冥想 10 分钟</div><div class="tg">+15</div></div>
+      </div>
+
+      <div class="unlock">
+        <svg class="medal" viewBox="0 0 24 24"><defs><linearGradient id="mg2" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#FCE08A"/><stop offset="1" stop-color="#F5A524"/></linearGradient></defs><path d="M9.2 4l1.6 3.2M14.8 4l-1.6 3.2" stroke="#E0920F" stroke-width="1.4" stroke-linecap="round"/><circle cx="12" cy="13.5" r="7" fill="url(#mg2)" stroke="#E0920F" stroke-width="1"/><path d="M12 9.8l1.35 2.8 3.05.45-2.2 2.15.52 3.05L12 18.9l-2.72 1.45.52-3.05-2.2-2.15 3.05-.45z" fill="#fff"/></svg>
+        <div><div class="ut">再完成 2 项 · 解锁「本周全勤」</div><div class="usub">解锁后自动生成成长报告</div></div>
+        <div class="ug" id="shareFromGrowth">分享 ›</div>
+      </div>
+    </div>
+
+    <!-- ============ 我的 ============ -->
+    <div class="screen hidden" id="profile">
+      <div class="statusbar"><span>9:41</span><div class="battery"></div></div>
+      <div class="apptitle">我的</div>
+      <div class="date">家庭中心</div>
+
+      <div class="idcard">
+        <div class="av">妈</div>
+        <div><div class="nm">晓琳</div><div class="rl">家长 · 家庭管理员</div></div>
+      </div>
+
+      <div class="sectitle brand">我的家庭</div>
+      <div class="members">
+        <div class="mb"><div class="av" style="background:var(--dim-wisdom)">宇</div><div class="nm">小宇</div><div class="sc">智 82</div></div>
+        <div class="mb"><div class="av" style="background:var(--dim-wealth)">柚</div><div class="nm">小柚</div><div class="sc">富 75</div></div>
+        <div class="mb"><div class="av" style="background:var(--dim-body)">爷</div><div class="nm">爷爷</div><div class="sc">身 70</div></div>
+        <div class="mb"><div class="av" style="background:var(--brand)">妈</div><div class="nm">妈妈</div><div class="sc">心 79</div></div>
+      </div>
+
+      <div class="sectitle">我的</div>
+      <div class="menu">
+        <div class="mi"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg>我的成就<div class="go">›</div></div>
+        <div class="mi"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M6 2h9l5 5v15H6z"/><path d="M14 2v6h6"/></svg>我的订单<div class="go">›</div></div>
+        <div class="mi"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 15a4 4 0 0 0 4-4V6a4 4 0 0 0-8 0v5a4 4 0 0 0 4 4z"/><path d="M8 19h8"/></svg>健康档案<div class="go">›</div></div>
+        <div class="mi"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M19 5l-2 2M7 17l-2 2"/></svg>设置<div class="go">›</div></div>
+      </div>
+    </div>
+
+    <!-- ============ TabBar ============ -->
+    <div class="tabbar">
+      <span class="tab active" data-target="home">首页</span>
+      <span class="tab" data-target="health">健康</span>
+      <span class="tab" data-target="growth">成长</span>
+      <span class="tab" data-target="profile">我的</span>
+    </div>
+    <div class="tab-center" id="openDrawer">五维</div>
+
+    <!-- ============ 五维抽屉 ============ -->
+    <div class="overlay" id="overlay"></div>
+    <div class="sheet" id="sheet">
+      <div class="handle"></div>
+      <h3>全家五维</h3>
+      <div class="drow" data-dim="智" data-color="#6366F1" data-score="82"><span class="dot" style="background:var(--dim-wisdom)"></span><span class="name"><span class="dim-w">智</span> · 思维</span><span class="val">82</span><span class="go">›</span></div>
+      <div class="drow" data-dim="心" data-color="#FF6B9D" data-score="79"><span class="dot" style="background:var(--dim-mind)"></span><span class="name">心 · 情绪</span><span class="val">79</span><span class="go">›</span></div>
+      <div class="drow" data-dim="身" data-color="#FF8C42" data-score="71"><span class="dot" style="background:var(--dim-body)"></span><span class="name">身 · 体魄</span><span class="val">71</span><span class="go">›</span></div>
+      <div class="drow" data-dim="行" data-color="#10B981" data-score="75"><span class="dot" style="background:var(--dim-action)"></span><span class="name">行 · 行动</span><span class="val">75</span><span class="go">›</span></div>
+      <div class="drow" data-dim="富" data-color="#F59E0B" data-score="68"><span class="dot" style="background:var(--dim-wealth)"></span><span class="name">富 · 财富</span><span class="val">68</span><span class="go">›</span></div>
+      <div class="expand" id="openSandbox">展开五维总览</div>
+    </div>
+
+    <!-- ============ 五维总览 ============ -->
+    <div class="layer" id="sandbox">
+      <div class="sb-head"><span class="sb-back" id="closeSandbox">‹</span><span class="sb-title">五维总览</span><span></span></div>
+      <div class="radarcard" id="fullRadar">
+        <svg width="260" height="260" viewBox="0 0 260 260" fill="none">
+          <polygon points="130,30 213,99 174,201 86,201 47,99" fill="none" stroke="#F0E4D8"/>
+          <polygon points="130,80 176,124 153,170 107,170 84,124" fill="none" stroke="#F6E9DC"/>
+          <line x1="130" y1="130" x2="130" y2="30" stroke="#F0E4D8"/><line x1="130" y1="130" x2="213" y2="99" stroke="#F0E4D8"/><line x1="130" y1="130" x2="174" y2="201" stroke="#F0E4D8"/><line x1="130" y1="130" x2="86" y2="201" stroke="#F0E4D8"/><line x1="130" y1="130" x2="47" y2="99" stroke="#F0E4D8"/>
+          <polygon points="130,48 201,107 170,185 88,187 55,106" fill="#F97316" fill-opacity="0.18" stroke="#F97316" stroke-width="2"/>
+          <circle cx="130" cy="48" r="4" fill="#6366F1"/><circle cx="201" cy="107" r="4" fill="#10B981"/><circle cx="170" cy="185" r="4" fill="#F59E0B"/><circle cx="88" cy="187" r="4" fill="#FF8C42"/><circle cx="55" cy="106" r="4" fill="#FF6B9D"/>
+          <text x="130" y="22" font-size="13" font-weight="700" fill="#6366F1" text-anchor="middle">智 82</text>
+          <text x="228" y="100" font-size="13" font-weight="700" fill="#10B981" text-anchor="middle">行 75</text>
+          <text x="182" y="224" font-size="13" font-weight="700" fill="#F59E0B" text-anchor="middle">富 68</text>
+          <text x="78" y="224" font-size="13" font-weight="700" fill="#FF8C42" text-anchor="middle">身 71</text>
+          <text x="30" y="132" font-size="13" font-weight="700" fill="#FF6B9D" text-anchor="middle">心 79</text>
+        </svg>
+      </div>
+      <div class="sectitle">各维趋势(点击查看详情)</div>
+      <div class="trend">
+        <div class="tcard" data-dim="智" data-color="#6366F1" data-score="82"><div class="n dim-w">智</div><div class="d">+6</div></div>
+        <div class="tcard" data-dim="心" data-color="#FF6B9D" data-score="79"><div class="n" style="color:var(--dim-mind)">心</div><div class="d">+3</div></div>
+        <div class="tcard" data-dim="行" data-color="#10B981" data-score="75"><div class="n" style="color:var(--dim-action)">行</div><div class="d">+4</div></div>
+      </div>
+      <div class="sectitle">改善建议</div>
+      <div class="sug">
+        <div class="si w"><div class="st">富 · 理财习惯偏弱</div><div class="sd">本周「富」<span class="dim">维</span>仅 68 分,建议安排一次家庭记账小互动。</div></div>
+        <div class="si b"><div class="st">身 · 运动待加强</div><div class="sd">「身」<span class="dim">维</span> 71 分,可把晚饭后散步设为本周家庭任务。</div></div>
+      </div>
+    </div>
+
+    <!-- ============ 维度详情 ============ -->
+    <div class="layer" id="dimDetail">
+      <div class="sb-head"><span class="sb-back" id="closeDim">‹</span><span class="sb-title" id="dimTitle">智 · 思维</span><span></span></div>
+      <div class="dim-hero" id="dimHero">
+        <div class="big" id="dimScore">82</div>
+        <div><div class="lab" id="dimLab">智 · 思维</div><div class="desc">本周 +6,近 30 天稳步提升</div></div>
+      </div>
+      <div class="sectitle">关键指标</div>
+      <div class="kpi">
+        <div class="k"><div class="v">优</div><div class="l">逻辑推理</div></div>
+        <div class="k"><div class="v">良</div><div class="l">记忆力</div></div>
+        <div class="k"><div class="v">中</div><div class="l">专注力</div></div>
+      </div>
+      <div class="sectitle">建议动作(完成后成长 +)</div>
+      <div class="acts" id="dimActs">
+        <div class="act done"><div class="cb"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><path d="M5 12l4 4 10-10"/></svg></div>完成数学挑战</div>
+        <div class="act"><div class="cb"></div>亲子共读 20 分钟</div>
+        <div class="act"><div class="cb"></div>玩一局舒尔特方格</div>
+      </div>
+      <div class="sectitle">最近记录</div>
+      <div class="rec">
+        <div class="ri"><span class="dot" style="background:var(--dim-wisdom)"></span>数学挑战 完成<div class="tm">今天</div></div>
+        <div class="ri"><span class="dot" style="background:var(--dim-wisdom)"></span>舒尔特方格 完成<div class="tm">昨天</div></div>
+      </div>
+    </div>
+
+    <!-- ============ 成长报告分享卡 ============ -->
+    <div class="report" id="report">
+      <div class="rcard">
+        <div class="rt">小宇家的本周成长</div>
+        <div class="rs">全家五维首次全达标 · 朋友们都在期待这一刻</div>
+        <div class="rscore">
+          <div class="rsi"><b style="color:var(--dim-wisdom)">82</b><span>智</span></div>
+          <div class="rsi"><b style="color:var(--dim-mind)">79</b><span>心</span></div>
+          <div class="rsi"><b style="color:var(--dim-body)">71</b><span>身</span></div>
+          <div class="rsi"><b style="color:var(--dim-action)">75</b><span>行</span></div>
+          <div class="rsi"><b style="color:var(--dim-wealth)">68</b><span>富</span></div>
+        </div>
+        <div class="rfire"><i>🔥</i><i>🔥</i><i>🔥</i><i>🔥</i><i>🔥</i><i>🔥</i><i>🔥</i></div>
+        <div class="rsum">
+          <div class="rm"><b>+62</b><span>成长值</span></div>
+          <div class="rm"><b>×3</b><span>徽章</span></div>
+          <div class="rm"><b>5/5</b><span>全达标</span></div>
+        </div>
+        <div class="rbtn"><svg viewBox="0 0 24 24"><path d="M2 21l21-9L2 3v7l15 2-15 2z" fill="#fff"/></svg>成果分享 · 邀请朋友们一起看</div>
+        <div class="rclose" id="closeReport">关闭</div>
+      </div>
+    </div>
+  </div>
+
+<script>
+  const overlay=document.getElementById('overlay'),sheet=document.getElementById('sheet'),
+        sandbox=document.getElementById('sandbox'),dimDetail=document.getElementById('dimDetail'),
+        report=document.getElementById('report');
+
+  // Tab 切换
+  document.querySelectorAll('.tab').forEach(t=>{
+    t.addEventListener('click',()=>{
+      document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));
+      t.classList.add('active');
+      const target=t.dataset.target;
+      ['home','health','growth','profile'].forEach(id=>{
+        document.getElementById(id).classList.toggle('hidden',id!==target);
+      });
+    });
+  });
+
+  // 五维抽屉
+  function openDrawer(){overlay.classList.add('show');sheet.classList.add('show');}
+  function closeDrawer(){overlay.classList.remove('show');sheet.classList.remove('show');}
+  document.getElementById('openDrawer').addEventListener('click',openDrawer);
+  overlay.addEventListener('click',closeDrawer);
+  document.getElementById('openSandbox').addEventListener('click',()=>{closeDrawer();sandbox.classList.add('show');});
+
+  // 维度详情(从抽屉或趋势卡点开,按维度注入)
+  function openDim(dim,color,score){
+    document.getElementById('dimTitle').textContent=dim+' · '+(dim==='智'?'思维':dim==='心'?'情绪':dim==='身'?'体魄':dim==='行'?'行动':'财富');
+    document.getElementById('dimScore').textContent=score;
+    document.getElementById('dimLab').textContent=dim+' · '+(dim==='智'?'思维':dim==='心'?'情绪':dim==='身'?'体魄':dim==='行'?'行动':'财富');
+    const hero=document.getElementById('dimHero');
+    hero.style.background='linear-gradient(135deg,'+color+'cc,'+color+' 70%,'+color+'dd)';
+    sandbox.classList.remove('show');
+    dimDetail.classList.add('show');
+  }
+  document.querySelectorAll('[data-dim]').forEach(el=>{
+    el.addEventListener('click',()=>openDim(el.dataset.dim,el.dataset.color,el.dataset.score));
+  });
+  document.getElementById('closeDim').addEventListener('click',()=>{dimDetail.classList.remove('show');sandbox.classList.add('show');});
+
+  // 五维总览 / 健康 mini 雷达 → 总览
+  document.getElementById('miniRadar').addEventListener('click',()=>sandbox.classList.add('show'));
+  document.getElementById('closeSandbox').addEventListener('click',()=>sandbox.classList.remove('show'));
+
+  // 成长任务勾选
+  document.querySelectorAll('.tk[data-gain]').forEach(tk=>{
+    tk.addEventListener('click',()=>{
+      if(tk.classList.contains('done'))return;
+      tk.classList.add('done');
+      tk.querySelector('.cb').innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="3"><path d="M5 12l4 4 10-10"/></svg>';
+    });
+  });
+
+  // 分享卡
+  function openReport(){report.classList.add('show');}
+  function closeReport(){report.classList.remove('show');}
+  document.getElementById('shareFromHome').addEventListener('click',openReport);
+  document.getElementById('shareFromGrowth').addEventListener('click',openReport);
+  document.getElementById('closeReport').addEventListener('click',closeReport);
+  report.addEventListener('click',e=>{if(e.target===report)closeReport();});
+</script>
+</body>
+</html>

+ 48 - 10
cfc-frontend/pages/article-center/index.vue

@@ -9,23 +9,26 @@
       </view>
       <text class="ac-nav-title">知识分享</text>
       <view class="ac-nav-right">
-        <text class="ac-nav-search-icon" @click="focusSearch">&#x1F50D;</text>
+        <!-- 搜索栏已常驻,此处仅作「聚焦输入框 + 回全量」快捷方式,与常驻输入框是同一入口,非独立搜索 -->
+        <text class="ac-nav-search-icon" @click="focusSearch">搜索</text>
         <text class="ac-nav-add" @click="goEdit">+</text>
       </view>
     </view>
 
-    <!-- 搜索栏(聚焦时展开) -->
-    <view v-if="searchFocused || searchKeyword" class="ac-search-bar">
+    <!-- 搜索栏(常驻可见):默认即展示输入框,聚焦时展开搜索历史;右上角「搜索」为聚焦快捷方式 -->
+    <view class="ac-search-bar">
       <input
         class="ac-search-input"
         v-model="searchKeyword"
+        :focus="searchFocused"
         placeholder="搜索文章..."
         confirm-type="search"
         @confirm="onSearch"
         @input="onSearchInput"
+        @focus="onSearchFocus"
         @blur="onSearchBlur"
       />
-      <text class="ac-search-cancel" @click="searchKeyword = ''; searchFocused = false; onSearch()">取消</text>
+      <text v-if="searchFocused || searchKeyword" class="ac-search-cancel" @click="cancelSearch">取消</text>
     </view>
 
     <!-- 搜索历史(聚焦且未输入时展示,上限 10 条可清空) -->
@@ -212,6 +215,10 @@ onLoad(options) {
       clearTimeout(this._searchTimer)
       this._searchTimer = null
     }
+    if (this._blurTimer) {
+      clearTimeout(this._blurTimer)
+      this._blurTimer = null
+    }
   },
   methods: {
     async loadTags() {
@@ -425,10 +432,39 @@ onLoad(options) {
       this.loading = false
       this.loadingMore = false
     },
-    onSearchBlur() {
-      if (!this.searchKeyword) {
-        this.searchFocused = false
+    // 常驻搜索框:聚焦即进入搜索态(展开历史),不重拉列表;
+    // 与右上角「搜索」快捷方式(focusSearch,含 Tab 互斥重置)共用同一输入框
+    onSearchFocus() {
+      if (this._blurTimer) {
+        clearTimeout(this._blurTimer)
+        this._blurTimer = null
+      }
+      this.searchFocused = true
+    },
+    cancelSearch() {
+      // 取消:清掉防抖与失焦定时器,避免取消后仍发出旧词请求或状态反复
+      if (this._searchTimer) {
+        clearTimeout(this._searchTimer)
+        this._searchTimer = null
       }
+      if (this._blurTimer) {
+        clearTimeout(this._blurTimer)
+        this._blurTimer = null
+      }
+      this.searchKeyword = ''
+      this.searchFocused = false
+      this.onSearch()
+    },
+    onSearchBlur() {
+      if (this._blurTimer) clearTimeout(this._blurTimer)
+      var self = this
+      // 延迟收起:点击搜索历史项时 blur 先于 click 触发,
+      // 若立即置 false,历史区 v-if 失效会导致点不中历史项
+      this._blurTimer = setTimeout(function () {
+        self._blurTimer = null
+        // 焦点态与输入框实际焦点保持一致;有关键词时「取消」仍由 searchKeyword 维持显示
+        self.searchFocused = false
+      }, 150)
     },
     onBack() {
       uni.navigateBack()
@@ -494,8 +530,10 @@ onLoad(options) {
   flex-shrink: 0;
 }
 .ac-nav-search-icon {
-  font-size: calc(32rpx * var(--font-scale, 1));
-  color: $facade-text-body;
+  font-size: calc(26rpx * var(--font-scale, 1));
+  font-weight: 500;
+  line-height: 1;
+  color: $brand-primary;
 }
 .ac-nav-add {
   font-size: calc(40rpx * var(--font-scale, 1));
@@ -504,7 +542,7 @@ onLoad(options) {
   line-height: 1;
 }
 
-/* ---- 搜索展开条 ---- */
+/* ---- 搜索栏(常驻可见) ---- */
 .ac-search-bar {
   display: flex;
   align-items: center;

+ 12 - 7
cfc-frontend/pages/shop/address/address.vue

@@ -110,14 +110,19 @@ export default {
       this.loadAddressList()
     },
     onSelect(item) {
-      if (this.selectMode) {
-        var pages = getCurrentPages()
-        var prevPage = pages[pages.length - 2]
-        if (prevPage) {
-          prevPage.selectedAddress = item
-        }
-        uni.navigateBack()
+      if (!this.selectMode) return
+      // uni-app 编译到小程序时 getCurrentPages() 返回的是 Page 实例,Vue 组件实例挂在 $vm 上。
+      // 直接 prevPage.selectedAddress = item 只是给 Page 对象加了个普通属性,不会触发上一页的响应式更新,
+      // 正是「选了地址但结算页始终显示第一个」的根因。H5 端 prevPage 本身即 Vue 实例,故做兼容取值。
+      var pages = getCurrentPages()
+      var prevPage = pages.length > 1 ? pages[pages.length - 2] : null
+      var vm = prevPage ? (prevPage.$vm || prevPage) : null
+      if (vm) {
+        vm.selectedAddress = item
       }
+      // 一次性兜底:结算页 onShow 会重新拉地址列表,按 id 恢复本次选择,避免跨端差异再次吃掉选中值
+      uni.setStorageSync('checkoutSelectedAddressId', item.id)
+      uni.navigateBack()
     },
     onEdit(id) {
       uni.navigateTo({ url: '/pages/shop/address-edit/address-edit?addressId=' + id })

+ 17 - 2
cfc-frontend/pages/shop/checkout/checkout.vue

@@ -924,12 +924,25 @@ export default {
         if (res.code === 200) {
           var list = res.data || []
           this.addressList = list
-          // 如果当前已有选中地址,保持选中状态
+          // 用户在地址选择页本次选中的地址:一次性消费,优先于默认地址与首条
+          var pickedId = uni.getStorageSync('checkoutSelectedAddressId')
+          if (pickedId !== '' && pickedId !== null && pickedId !== undefined) {
+            uni.removeStorageSync('checkoutSelectedAddressId')
+            for (var p = 0; p < list.length; p++) {
+              if (String(list[p].id) === String(pickedId)) {
+                this.selectedAddress = list[p]
+                break
+              }
+            }
+          }
+          // 如果当前已有选中地址,保持选中状态(id 统一按字符串比较,兼容数字/字符串类型差异)
           if (this.selectedAddress) {
             var stillExists = false
             for (var k = 0; k < list.length; k++) {
-              if (list[k].id === this.selectedAddress.id) {
+              if (String(list[k].id) === String(this.selectedAddress.id)) {
                 stillExists = true
+                // 用最新列表数据刷新,保证编辑地址后返回能立刻看到新内容
+                this.selectedAddress = list[k]
                 break
               }
             }
@@ -1000,6 +1013,8 @@ export default {
         uni.showToast({ title: '暂无地址,请先添加', icon: 'none' })
         return
       }
+      // 清掉上次可能残留的一次性选中标记,避免本次打开选择器前误用旧值
+      uni.removeStorageSync('checkoutSelectedAddressId')
       uni.navigateTo({ url: '/pages/shop/address/address?selectMode=1' })
     },
     checkMemberStatus() {

+ 1 - 0
cfc-frontend/pages/shop/order-detail/order-detail.vue

@@ -16,6 +16,7 @@
         <text v-if="order.refundStatus === 1" class="status-hint">退款申请处理中,请耐心等待</text>
         <text v-if="order.refundStatus === 2" class="status-hint">退款已完成,金额将原路返回</text>
       </view>
+      <view class="section">
         <text class="section-title">订单信息</text>
         <view class="info-row">
           <text class="info-label">订单编号</text>

+ 157 - 0
docs/参考资料/知识库/merge_knowledge.py

@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+"""
+Merge knowledge bases into a single unified JSON for backend use.
+
+Sources (all data taken DIRECTLY from native structured JSON fields, no manual transcription):
+  1. 菌群营养知识库.json       — 5报告汇总版(226食物含推荐指数 + 14类菌-疾病关联 + 30营养素 + 108菌种综合信息 + 22核心菌功能描述)
+  2. 知识库.json              — 标准参考版(营养素标准参考:状态分类体系 + 正常参考范围)
+  3. 报告指标知识库.json        — 指标分级标准(24类分级标准 + 菌属功能库95项 + 健康评分标准)
+  4. 菌属-食材映射表.json       — 菌属-食材双向映射(22菌属×推荐/避免食材+文献)
+
+Output: 统一知识库.json
+"""
+import json
+import os
+import re
+
+BASE = os.path.dirname(os.path.abspath(__file__))
+
+def load(name):
+    path = os.path.join(BASE, name)
+    with open(path, 'r', encoding='utf-8') as f:
+        return json.load(f)
+
+def main():
+    kb_nutrition = load('菌群营养知识库.json')   # 5-report aggregate (richest)
+    kb_standard = load('知识库.json')            # standard reference
+    kb_indicators = load('报告指标知识库.json')   # indicator grading (unique)
+    kb_food_map = load('菌属-食材映射表.json')    # bacteria-food mapping (unique)
+
+    merged = {}
+
+    # ──────────────────────────────────────────────
+    # 1. 元数据
+    # ──────────────────────────────────────────────
+    merged['元数据'] = {
+        '名称': '肠道菌群健康知识库(统一版)',
+        '版本': '2.0',
+        '说明': '合并自菌群营养知识库v1.0(5报告汇总)、知识库v2.0(标准参考)、报告指标知识库v3.0(指标分级)、菌属-食材映射表v1.0。全部数据直接取自各源 JSON 原生结构化字段,无手工转写;菌种综合信息已过滤 OCR 噪声键。',
+        '数据来源': [
+            '501999942-某人.json',
+            '530010234-侯.json',
+            '547982403.json',
+            '儿童示例.json',
+            '朱评估报告.json',
+            '北京菌群报告(移植前/后)',
+        ],
+        '用途': '后台数据库通用知识底座:食物营养查询、菌-疾病关联、菌种功能描述、指标分级判定、菌属-食材推荐引擎',
+        '合并日期': '2026-08-05',
+    }
+
+    # ──────────────────────────────────────────────
+    # 2. 食物营养库(取菌群营养知识库 226 种,含推荐指数+来源报告数)
+    # ──────────────────────────────────────────────
+    food_lib = kb_nutrition.get('食物营养库', {})
+    food_items = food_lib.get('食材', {})
+    merged['食物营养库'] = dict(food_lib)
+    merged['食物营养库']['食材数'] = len(food_items)
+    print(f"  食物营养库: {len(food_items)} 种食材 (from 菌群营养知识库)")
+
+    # ──────────────────────────────────────────────
+    # 3. 菌-疾病关联(原生 14 类:6 标准 + 8 北京补充含心理评估)
+    # ──────────────────────────────────────────────
+    disease_bacteria = kb_nutrition.get('菌疾病关联', {})
+    merged['菌-疾病关联'] = disease_bacteria
+    print(f"  菌-疾病关联: {len(disease_bacteria)} 类疾病关联 (native)")
+
+    # ──────────────────────────────────────────────
+    # 4. 营养素参考(原生 30 项状态分布)
+    # ──────────────────────────────────────────────
+    nutrient_ref = kb_nutrition.get('营养素参考', {})
+    merged['营养素参考'] = nutrient_ref
+    print(f"  营养素参考: {len(nutrient_ref)} 种营养素 (native)")
+
+    # ──────────────────────────────────────────────
+    # 5. 营养素标准参考(来自 知识库.json:状态分类体系 + 正常参考范围)
+    # ──────────────────────────────────────────────
+    nutrient_std = kb_standard.get('营养素标准参考', {})
+    merged['营养素标准参考'] = nutrient_std
+    ref_ranges = nutrient_std.get('正常参考范围', {})
+    print(f"  营养素标准参考: 状态分类体系 + {len(ref_ranges)} 类正常参考范围 (from 知识库)")
+
+    # ──────────────────────────────────────────────
+    # 6. 菌种综合信息(原生,过滤无拉丁名的 OCR 噪声键)
+    # ──────────────────────────────────────────────
+    bacteria_all = kb_nutrition.get('菌种综合信息', {})
+    # Real bacteria entries have "中文名 拉丁名" keys; OCR noise keys (ranges,
+    # 共检测到:, 厚壁菌/拟杆菌21.6% etc.) contain no Latin letters.
+    bacteria_filtered = {
+        k: v for k, v in bacteria_all.items() if re.search(r'[A-Za-z]', k)
+    }
+    merged['菌种综合信息'] = {
+        '说明': '菌种功能分类与关联疾病总览(含检出报告数;已过滤 OCR 噪声键)',
+        '菌种': bacteria_filtered,
+    }
+    print(f"  菌种综合信息: {len(bacteria_all)} 原始键 → {len(bacteria_filtered)} 真实菌种 (过滤 {len(bacteria_all)-len(bacteria_filtered)} 个噪声键)")
+
+    # ──────────────────────────────────────────────
+    # 7. 菌种功能详细描述(原生 22 核心菌属,以拉丁名为键)
+    # ──────────────────────────────────────────────
+    func_desc = kb_nutrition.get('菌种功能详细描述(北京菌群报告补充)', {})
+    merged['菌种功能详细描述'] = func_desc
+    genera = func_desc.get('菌种', {})
+    print(f"  菌种功能详细描述: {len(genera)} 个核心菌属 (native)")
+
+    # ──────────────────────────────────────────────
+    # 8. 菌属功能库(来自 报告指标知识库,95 项)
+    # ──────────────────────────────────────────────
+    genera_lib = kb_indicators.get('菌属功能库', {})
+    merged['菌属功能库'] = genera_lib
+    genera_items = genera_lib.get('菌属', [])
+    print(f"  菌属功能库: {len(genera_items)} 个菌属 (from 报告指标知识库)")
+
+    # ──────────────────────────────────────────────
+    # 9. 报告指标分级标准(全部 24 个含指标的分级类别)
+    # ──────────────────────────────────────────────
+    indicator_sections = {
+        k: v for k, v in kb_indicators.items()
+        if isinstance(v, dict) and '指标' in v
+    }
+    total_indicators = sum(
+        len(s.get('指标', [])) for s in indicator_sections.values()
+    )
+    merged['报告指标分级标准'] = {
+        '说明': '各类报告指标的标准化范围、状态分级阈值、说明和调整建议',
+        '版本': kb_indicators.get('元数据', {}).get('版本', '3.0'),
+        '指标总数': total_indicators,
+        '指标类别': indicator_sections,
+    }
+    print(f"  报告指标分级标准: {len(indicator_sections)} 个类别, {total_indicators} 项指标")
+
+    # ──────────────────────────────────────────────
+    # 10. 菌属-食材映射(来自 菌属-食材映射表.json,22 菌属)
+    # ──────────────────────────────────────────────
+    merged['菌属-食材映射'] = kb_food_map.get('菌属映射', [])
+    merged['菌属-食材映射_元数据'] = kb_food_map.get('元数据', {})
+    print(f"  菌属-食材映射: {len(merged['菌属-食材映射'])} 个菌属")
+
+    # ──────────────────────────────────────────────
+    # 11. 健康评分标准(来自 报告指标知识库 健康总分评估)
+    # ──────────────────────────────────────────────
+    merged['健康评分标准'] = kb_indicators.get('健康总分评估', {})
+    print(f"  健康评分标准: 4 个维度")
+
+    # ──────────────────────────────────────────────
+    # Write output
+    # ──────────────────────────────────────────────
+    output_path = os.path.join(BASE, '统一知识库.json')
+    with open(output_path, 'w', encoding='utf-8') as f:
+        json.dump(merged, f, ensure_ascii=False, indent=2)
+
+    print(f"\n合并完成! 输出: {output_path}")
+    print(f"文件大小: {os.path.getsize(output_path) / 1024:.1f} KB")
+    print(f"顶层键: {list(merged.keys())}")
+
+
+if __name__ == '__main__':
+    main()

+ 217 - 0
web/img/ref-screenshots/annotate.py

@@ -0,0 +1,217 @@
+"""
+Utility to annotate academic screenshots with Chinese translations and key content highlights.
+Usage: python annotate.py input.png output.png "annotation text" [--position top|bottom] [--color red]
+"""
+import sys
+import os
+from PIL import Image, ImageDraw, ImageFont, ImageFilter
+
+def find_cjk_font():
+    """Find a CJK-capable font on the system."""
+    candidates = [
+        # Windows
+        "C:/Windows/Fonts/msyh.ttc",      # Microsoft YaHei
+        "C:/Windows/Fonts/simhei.ttf",     # SimHei
+        "C:/Windows/Fonts/simsun.ttc",     # SimSun
+        "C:/Windows/Fonts/msyhbd.ttc",     # YaHei Bold
+        # macOS
+        "/System/Library/Fonts/PingFang.ttc",
+        "/System/Library/Fonts/STHeiti Light.ttc",
+        # Linux
+        "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
+        "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
+    ]
+    for path in candidates:
+        if os.path.exists(path):
+            return path
+    return None
+
+def annotate_image(input_path, output_path, annotations, banner_text=None):
+    """
+    Add Chinese annotations to an image.
+    
+    annotations: list of dicts with keys:
+        - text: Chinese translation/annotation text
+        - position: 'top' or 'bottom' (default: 'top')
+        - bg_color: background color (default: semi-transparent red)
+        - text_color: text color (default: white)
+    
+    banner_text: optional text to add as a banner at the top
+    """
+    img = Image.open(input_path).convert("RGBA")
+    width, height = img.size
+    
+    font_path = find_cjk_font()
+    if not font_path:
+        print("WARNING: No CJK font found, annotations may not render Chinese characters")
+        font_path = None
+    
+    # Create overlay for annotations
+    overlay = Image.new("RGBA", img.size, (0, 0, 0, 0))
+    draw = ImageDraw.Draw(overlay)
+    
+    # Calculate font sizes based on image width
+    if width >= 1200:
+        banner_font_size = 28
+        annotation_font_size = 22
+    elif width >= 800:
+        banner_font_size = 22
+        annotation_font_size = 18
+    else:
+        banner_font_size = 16
+        annotation_font_size = 14
+    
+    try:
+        if font_path:
+            banner_font = ImageFont.truetype(font_path, banner_font_size)
+            annotation_font = ImageFont.truetype(font_path, annotation_font_size)
+        else:
+            banner_font = ImageFont.load_default()
+            annotation_font = ImageFont.load_default()
+    except Exception:
+        banner_font = ImageFont.load_default()
+        annotation_font = ImageFont.load_default()
+    
+    y_offset = 10
+    
+    # Add banner text at top
+    if banner_text:
+        bbox = draw.textbbox((0, 0), banner_text, font=banner_font)
+        text_height = bbox[3] - bbox[1]
+        padding = 12
+        
+        # Semi-transparent dark background
+        draw.rectangle(
+            [0, 0, width, text_height + padding * 2],
+            fill=(0, 0, 0, 180)
+        )
+        # Draw text centered
+        text_width = bbox[2] - bbox[0]
+        x = (width - text_width) // 2
+        draw.text((x, padding), banner_text, fill=(255, 255, 255, 255), font=banner_font)
+        y_offset = text_height + padding * 2 + 10
+    
+    # Add annotation labels
+    for i, ann in enumerate(annotations):
+        text = ann.get("text", "")
+        pos = ann.get("position", "top")
+        bg_color = ann.get("bg_color", (220, 50, 50, 200))
+        text_color = ann.get("text_color", (255, 255, 255, 255))
+        
+        if not text:
+            continue
+        
+        # Word wrap for long text
+        lines = []
+        max_chars_per_line = max(20, width // (annotation_font_size + 2))
+        for line_text in text.split("\n"):
+            while len(line_text) > max_chars_per_line:
+                lines.append(line_text[:max_chars_per_line])
+                line_text = line_text[max_chars_per_line:]
+            lines.append(line_text)
+        
+        # Calculate total height
+        line_height = annotation_font_size + 6
+        total_height = len(lines) * line_height + 16
+        
+        if pos == "bottom":
+            box_y = height - total_height - 10 - i * (total_height + 5)
+        else:
+            box_y = y_offset + i * (total_height + 5)
+        
+        # Draw background
+        draw.rectangle(
+            [10, box_y, width - 10, box_y + total_height],
+            fill=bg_color
+        )
+        
+        # Draw text
+        for j, line in enumerate(lines):
+            draw.text(
+                (20, box_y + 8 + j * line_height),
+                line,
+                fill=text_color,
+                font=annotation_font
+            )
+    
+    # Composite
+    result = Image.alpha_composite(img, overlay)
+    result = result.convert("RGB")
+    result.save(output_path, "PNG", quality=95)
+    print(f"Annotated image saved to: {output_path}")
+
+def add_arrow_annotation(input_path, output_path, arrow_x, arrow_y, label_text, label_pos="right"):
+    """Add an arrow + label annotation to a specific point on the image."""
+    img = Image.open(input_path).convert("RGBA")
+    overlay = Image.new("RGBA", img.size, (0, 0, 0, 0))
+    draw = ImageDraw.Draw(overlay)
+    
+    font_path = find_cjk_font()
+    font_size = 20 if img.size[0] >= 800 else 14
+    try:
+        font = ImageFont.truetype(font_path, font_size) if font_path else ImageFont.load_default()
+    except:
+        font = ImageFont.load_default()
+    
+    # Draw arrow (simple line + triangle)
+    arrow_len = 30
+    if label_pos == "right":
+        end_x = arrow_x + arrow_len
+        end_y = arrow_y
+    elif label_pos == "left":
+        end_x = arrow_x - arrow_len
+        end_y = arrow_y
+    elif label_pos == "down":
+        end_x = arrow_x
+        end_y = arrow_y + arrow_len
+    else:
+        end_x = arrow_x + arrow_len
+        end_y = arrow_y
+    
+    # Draw circle at start point
+    r = 6
+    draw.ellipse([arrow_x - r, arrow_y - r, arrow_x + r, arrow_y + r], fill=(255, 50, 50, 230))
+    
+    # Draw line
+    draw.line([(arrow_x, arrow_y), (end_x, end_y)], fill=(255, 50, 50, 230), width=3)
+    
+    # Draw label
+    bbox = draw.textbbox((0, 0), label_text, font=font)
+    tw = bbox[2] - bbox[0]
+    th = bbox[3] - bbox[1]
+    padding = 6
+    
+    if label_pos == "right":
+        lx = end_x + 8
+        ly = end_y - th // 2
+    elif label_pos == "left":
+        lx = end_x - tw - 8
+        ly = end_y - th // 2
+    else:
+        lx = end_x - tw // 2
+        ly = end_y + 8
+    
+    draw.rectangle([lx - padding, ly - padding, lx + tw + padding, ly + th + padding], fill=(220, 50, 50, 210))
+    draw.text((lx, ly), label_text, fill=(255, 255, 255, 255), font=font)
+    
+    result = Image.alpha_composite(img, overlay)
+    result.convert("RGB").save(output_path, "PNG", quality=95)
+    print(f"Arrow annotation saved to: {output_path}")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) < 4:
+        print("Usage: python annotate.py input.png output.png 'annotation text' [--banner 'banner text']")
+        sys.exit(1)
+    
+    input_path = sys.argv[1]
+    output_path = sys.argv[2]
+    annotation_text = sys.argv[3]
+    
+    banner = None
+    if "--banner" in sys.argv:
+        idx = sys.argv.index("--banner")
+        if idx + 1 < len(sys.argv):
+            banner = sys.argv[idx + 1]
+    
+    annotate_image(input_path, output_path, [{"text": annotation_text}], banner_text=banner)

+ 103 - 0
web/img/ref-screenshots/annotate_cell.py

@@ -0,0 +1,103 @@
+from PIL import Image, ImageDraw, ImageFont
+import os
+
+def annotate_page(page_num, img_path, annotations, output_path):
+    img = Image.open(img_path)
+    draw = ImageDraw.Draw(img)
+    
+    # Try to use a Chinese font
+    font_paths = [
+        'C:/Windows/Fonts/simhei.ttf',
+        'C:/Windows/Fonts/simsun.ttc',
+    ]
+    font_path = None
+    for fp in font_paths:
+        if os.path.exists(fp):
+            font_path = fp
+            break
+    
+    if font_path:
+        try:
+            font = ImageFont.truetype(font_path, 16)
+            font_small = ImageFont.truetype(font_path, 13)
+        except:
+            font = ImageFont.load_default()
+            font_small = ImageFont.load_default()
+    else:
+        font = ImageFont.load_default()
+        font_small = ImageFont.load_default()
+    
+    for rect, text in annotations:
+        # Draw red rectangle
+        draw.rectangle(rect, outline='red', width=4)
+        # Draw annotation text above the rectangle
+        if text:
+            draw.text((rect[0], rect[1] - 22), text, fill='red', font=font_small)
+    
+    img.save(output_path)
+    print(f'Saved: {output_path}')
+
+# Page 2: Summary section
+annotate_page(
+    2,
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p2.png',
+    [
+        # SUMMARY box (around y=350-700)
+        ((60, 340, 1640, 700), 
+         'SUMMARY: 肥胖导致慢性全身性炎症,引起胰岛素抵抗(IR) -> \u03b2细胞功能障碍 -> 2型糖尿病(T2D)'),
+        # Paragraph about macrophages being the ultimate driver
+        ((60, 720, 1640, 940),
+         '促炎巨噬细胞在代谢组织中的募集和活化,是慢性低度炎症的终极驱动力')
+    ],
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p2-annotated.png'
+)
+
+# Page 3: Immunometabolism
+annotate_page(
+    3,
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p3.png',
+    [
+        # Immunometabolism term
+        ((60, 110, 1640, 410),
+         '"免疫代谢"(immunometabolism):炎症与代谢缺陷之间的相互作用'),
+        # Drug limitation
+        ((60, 440, 1640, 700),
+         '所有降糖药中仅3类能预防并发症;除二甲双胍外,其他药物均不能延缓糖尿病进展')
+    ],
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p3-annotated.png'
+)
+
+# Page 4: Inflammatory signaling mechanism
+annotate_page(
+    4,
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p4.png',
+    [
+        # TNF-alpha and IKK/NF-kB pathway
+        ((60, 100, 1640, 420),
+         'TNF-\u03b1 激活 JNK 和 IKK\u03b2 -> NF-\u03baB 核转位 -> 促炎细胞因子增加'),
+        # Inflammasome
+        ((60, 440, 1640, 700),
+         '炎症小体(inflammasome)被葡萄糖和游离脂肪酸激活 -> 产生活性 IL-1\u03b2')
+    ],
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p4-annotated.png'
+)
+
+# Page 5: Macrophage polarization in obese vs lean
+annotate_page(
+    5,
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p5.png',
+    [
+        # M1-like macrophages in obesity
+        ((60, 100, 1640, 380),
+         '肥胖时巨噬细胞向促炎M1样极化 -> 释放促炎细胞因子 -> 降低胰岛素信号'),
+        # Omega-3 inhibits inflammation
+        ((60, 400, 1640, 660),
+         'Omega-3脂肪酸(GPR120受体)抑制M1样极化;饱和脂肪酸(TLR4)促进M1样极化'),
+        # Crown-like structures
+        ((60, 680, 1640, 900),
+         '肥胖巨噬细胞聚集在萎缩脂肪细胞周围形成"冠状结构"')
+    ],
+    'D:/workspace/cfc/web/img/ref-screenshots/cell-inflammation-p5-annotated.png'
+)
+
+print('All annotations done.')