Ver código fonte

docs: 家庭成员页面重新设计 - 设计文档

Xiaogang Liao 1 mês atrás
pai
commit
b95d1fe9da

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-cd191521b43f5eb1cc43afd5d6638ee6e6c6db0e
+4d77b01fc5a652e4c7c7e25b93b1c5c21a2a999d

+ 3 - 3
cfc-web/public/CHANGELOG-v1.0.md

@@ -7,11 +7,11 @@
 ## v1.0.682 (2026-07-31)
 
 ### Bug 修复
-- 后台活动权重维度code与标准五维体系不一致导致新活动小程序端不显示
+- add missing List import in ActivityController and DB migrations for dimension_code/family_scope columns
 
 ### 其他
-- DatabaseInitializer 迁移141: 修正历史 dimension_weights 表 + 主表 JSON 列旧 code
-- ActivityController: 补 java.util.List 导入(遗留编译错误)
+- - DatabaseInitializer: add migrations 138-140 for tasks.dimension_code,
+-   products.family_scope, activities.family_scope (were dead code in migration102)
 - 
 
 

+ 3 - 3
cfc-web/public/CHANGELOG.md

@@ -11,11 +11,11 @@
 ## v1.0.682 (2026-07-31)
 
 ### Bug 修复
-- 后台活动权重维度code与标准五维体系不一致导致新活动小程序端不显示
+- add missing List import in ActivityController and DB migrations for dimension_code/family_scope columns
 
 ### 其他
-- DatabaseInitializer 迁移141: 修正历史 dimension_weights 表 + 主表 JSON 列旧 code
-- ActivityController: 补 java.util.List 导入(遗留编译错误)
+- - DatabaseInitializer: add migrations 138-140 for tasks.dimension_code,
+-   products.family_scope, activities.family_scope (were dead code in migration102)
 - 
 
 

+ 142 - 0
docs/superpowers/specs/2026-07-31-family-members-redesign.md

@@ -0,0 +1,142 @@
+# 家庭成员页面重新设计
+
+## 概述
+
+重新设计小程序家庭成员管理页面,从丑旧的行式列表改为温暖家庭风的卡片式布局,并将"添加成员"和"邀请成员"提升为底部固定按钮,邀请直接弹出海报。
+
+## 当前问题
+
+- 页面视觉老旧(紫色渐变头部 + 简单行式列表)
+- 添加/邀请功能藏在齿轮设置弹窗中,需要额外点击
+- 邀请流程需经过中间邀请码卡片,步骤多
+- 成员行显示三维评分条,信息过重
+
+## 设计方案
+
+### 视觉风格:温暖家庭风
+
+- 主色:暖橙 `#F97316` / 米白 `#FFFCF5` / 暖灰 `#F5F0EB`
+- 卡片圆角 + 柔和阴影,家庭相册感
+- 去掉紫色渐变,使用暖橙渐变头部
+
+### 页面结构
+
+```
+┌─────────────────────────────┐
+│  🏠 幸福之家 · 5位成员        │  ← 暖橙渐变头部
+├─────────────────────────────┤
+│                             │
+│  ┌─────┐                    │
+│  │ 🧑  │ 爸爸 · 家长        │  ← 卡片式成员条目
+│  └─────┘                    │
+│  ┌─────┐                    │
+│  │ 👩  │ 妈妈 · 家长        │
+│  └─────┘                    │
+│  ┌─────┐                    │
+│  │ 👦  │ 小明 · 孩子        │
+│  └─────┘                    │
+│                             │
+│  [空状态: 邀请家人加入引导]     │
+│                             │
+├─────────────────────────────┤
+│  [+ 添加成员] [📨 邀请成员]  │  ← 底部固定栏
+└─────────────────────────────┘
+```
+
+### 组件详细设计
+
+#### 1. 页面头部
+
+- 暖橙渐变背景 `linear-gradient(135deg, #F97316, #EA580C)`
+- 居中布局:🏠 家庭图标 + 家庭名称 + 成员数
+- 白色文字
+- 圆角底部
+
+#### 2. 成员列表
+
+- 白底圆角卡片,柔和阴影
+- 每行包含:
+  - 左侧:头像 80rpx 圆形
+  - 中间:昵称(粗体 30rpx)+ 关系标签(彩色小标签)
+  - 右侧:箭头 `›`
+- 无三维评分条
+- 无筛选标签
+- 点击 → 成员详情弹窗
+- 卡片间间距 16rpx
+
+#### 3. 空状态
+
+- 当成员数为 0 时显示
+- 大图标 👨‍👩‍👧‍👦
+- 文案:"还没有家庭成员"
+- 副文案:"邀请家人加入,一起记录成长"
+- 按钮:「邀请家人」(暖橙填充)
+
+#### 4. 底部固定栏
+
+- 固定在页面底部,安全区适配
+- 两个按钮并排,间距 20rpx
+- 「+ 添加成员」:白色背景 + 暖橙边框/文字,flex:1
+- 「📨 邀请成员」:暖橙渐变填充,白色文字,flex:1
+- 添加 → 跳转 `/pages/family/add-member`
+- 邀请 → 加载 QR 码 → 直接弹出 SharePoster
+
+#### 5. 成员详情弹窗
+
+- 保留现有功能,视觉改造为暖色风格
+- 顶部:头像 + 昵称 + 关系标签
+- 信息区:性别 · 年龄 · 手机号
+- 操作按钮 2 列网格:切换身份 / 编辑资料 / 关系问卷 / 记录互动 / 移出家庭
+
+#### 6. 邀请流程
+
+简化前:
+```
+点击齿轮 → 邀请成员 → 显示邀请码卡片 → 点击分享邀请卡 → 弹出海报
+```
+
+简化后:
+```
+点击「邀请成员」→ 加载 QR 码 → 直接弹出海报
+```
+
+### 移除内容
+
+| 移除项 | 原因 |
+|--------|------|
+| 紫色渐变头部 | 改为暖橙家庭风 |
+| 齿轮设置弹窗 | 功能合并到底部栏 |
+| 筛选标签(全部/家长/孩子) | 精简需求 |
+| 行内三维评分条 | 精简需求 |
+| 中间邀请码卡片 | 直接弹出海报 |
+
+### 保留内容
+
+| 保留项 | 说明 |
+|--------|------|
+| 成员详情弹窗 | 操作入口,保留全部功能 |
+| SharePoster 组件 | 邀请海报,直接触发 |
+| 添加成员页面 | `/pages/family/add-member`,保持不变 |
+| 切换身份/编辑资料/移出家庭等操作 | 在详情弹窗中保留 |
+
+### 数据流
+
+```
+页面加载 → listFamilyMembers() → 渲染成员列表
+点击成员 → showMemberDetail(m) → 详情弹窗
+点击添加 → uni.navigateTo(/pages/family/add-member)
+点击邀请 → generateFamilyInviteQrCode() → 获取 qrCodeBase64 → showPoster = true
+```
+
+### 文件变更清单
+
+| 文件 | 变更类型 | 说明 |
+|------|----------|------|
+| `pages/profile-extra/family-members.vue` | 重写 | 模板/样式/脚本全部重新设计 |
+| `components/SharePoster.vue` | 无变更 | 保持现有,直接调用 |
+
+### 非功能性要求
+
+- 遵循小程序限制:禁止可选链 `?.`、禁止 CSS Grid、禁止 `:key` 表达式
+- 使用 Vue 2 Options API
+- 匹配现有品牌色系

+ 353 - 1
web/articles/plastic-health.html

@@ -531,6 +531,127 @@
     .compare-table tr:last-child td { border-bottom: none; }
     .compare-table .highlight-cell { background: var(--green-light); font-weight: 600; }
 
+    /* ===== Comparison Carousel (Mobile) ===== */
+    .compare-carousel { display: none; }
+    .carousel-track {
+      display: flex;
+      gap: 1rem;
+      overflow-x: auto;
+      scroll-snap-type: x mandatory;
+      -webkit-overflow-scrolling: touch;
+      scrollbar-width: none;
+      padding: 0.5rem 0 1rem;
+    }
+    .carousel-track::-webkit-scrollbar { display: none; }
+    .carousel-card {
+      flex: 0 0 calc(100% - 2rem);
+      scroll-snap-align: start;
+      background: white;
+      border-radius: var(--radius);
+      box-shadow: var(--shadow-card);
+      padding: 1.5rem;
+      border-top: 4px solid #CBD5E0;
+      position: relative;
+    }
+    .carousel-card .card-badge {
+      display: inline-block;
+      font-size: 0.7rem;
+      font-weight: 700;
+      padding: 0.25rem 0.75rem;
+      border-radius: 999px;
+      margin-bottom: 0.75rem;
+      letter-spacing: 0.02em;
+    }
+    .carousel-card .card-badge.danger { background: #FFF5F5; color: #C53030; }
+    .carousel-card .card-badge.success { background: #F0FFF4; color: #276749; }
+    .carousel-card .card-badge.warning { background: #FFFFF0; color: #975A16; }
+    .carousel-card .card-badge.primary { background: #EBF8FF; color: #2B6CB0; }
+    .carousel-card.border-danger { border-top-color: #E53E3E; }
+    .carousel-card.border-success { border-top-color: #38A169; }
+    .carousel-card.border-warning { border-top-color: #DD6B20; }
+    .carousel-card.border-primary { border-top-color: #3182CE; }
+    .carousel-card .card-title {
+      font-size: 1.1rem;
+      font-weight: 700;
+      margin-bottom: 1rem;
+      padding-bottom: 0.75rem;
+      border-bottom: 1px solid #E2E8F0;
+    }
+    .carousel-card .criteria-list { list-style: none; }
+    .carousel-card .criteria-list li {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      padding: 0.6rem 0;
+      border-bottom: 1px dashed #EDF2F7;
+      gap: 0.5rem;
+    }
+    .carousel-card .criteria-list li:last-child { border-bottom: none; }
+    .carousel-card .criterion-label {
+      flex-shrink: 0;
+      font-size: 0.8rem;
+      font-weight: 600;
+      color: var(--text-primary);
+      width: 5.5rem;
+    }
+    .carousel-card .criterion-value {
+      font-size: 0.8rem;
+      color: var(--text-secondary);
+      text-align: right;
+      line-height: 1.5;
+    }
+    .carousel-card .criterion-value .highlight-cell { background: transparent; font-weight: 600; }
+    .carousel-card .card-footer {
+      margin-top: 0.75rem;
+      padding-top: 0.75rem;
+      border-top: 1px solid #E2E8F0;
+      font-size: 0.85rem;
+      font-weight: 700;
+      text-align: center;
+    }
+    .carousel-card .card-footer.danger { color: #C53030; }
+    .carousel-card .card-footer.success { color: #276749; }
+    .carousel-card .card-footer.warning { color: #975A16; }
+    .carousel-card .card-footer.primary { color: #2B6CB0; }
+    .carousel-nav {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: 1rem;
+      margin-top: 0.75rem;
+    }
+    .carousel-arrow {
+      width: 36px; height: 36px;
+      border-radius: 50%;
+      border: 1px solid #E2E8F0;
+      background: white;
+      color: var(--text-secondary);
+      font-size: 1.3rem;
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      transition: all 0.2s;
+      box-shadow: 0 1px 4px rgba(0,0,0,0.06);
+    }
+    .carousel-arrow:hover { background: var(--bg-alt); color: var(--text-primary); }
+    .carousel-dots {
+      display: flex;
+      gap: 0.5rem;
+    }
+    .carousel-dot {
+      width: 8px; height: 8px;
+      border-radius: 50%;
+      background: #CBD5E0;
+      cursor: pointer;
+      transition: all 0.2s;
+    }
+    .carousel-dot.active {
+      background: var(--blue);
+      width: 24px;
+      border-radius: 4px;
+    }
+
     /* ===== Responsive ===== */
     @media (max-width: 640px) {
       .hero { padding: 4rem 1rem 3rem; min-height: auto; }
@@ -545,6 +666,8 @@
       section { padding: 2.5rem 0; }
       h2 { font-size: 1.4rem; }
       .highlight-box { padding: 1.25rem; }
+      .compare-table-wrapper { display: none; }
+      .compare-carousel { display: block; }
       .compare-table { font-size: 0.8rem; }
       .compare-table th, .compare-table td { padding: 0.5rem 0.75rem; }
     }
@@ -958,7 +1081,7 @@
     <h2>📊 四大饮水方案全面对比</h2>
     <p>基于以上风险分析,我们将办公室常见的四种饮水方案放在同一张表中清晰对比:</p>
 
-    <div style="overflow-x:auto;">
+    <div class="compare-table-wrapper" style="overflow-x:auto;">
     <table class="compare-table">
       <thead>
         <tr>
@@ -1037,6 +1160,186 @@
     </table>
     </div>
 
+    <!-- ===== Mobile Carousel ===== -->
+    <div class="compare-carousel" id="compareCarousel">
+      <div class="carousel-track">
+        <!-- Card 1: 桶装水/瓶装水 -->
+        <div class="carousel-card border-danger" data-index="0">
+          <span class="card-badge danger">❌ 风险最高</span>
+          <div class="card-title">📦 桶装水/瓶装水</div>
+          <ul class="criteria-list">
+            <li>
+              <span class="criterion-label">微塑料</span>
+              <span class="criterion-value">620万颗粒/升,随时间+温度递增<sup><a href="#ref-3">[3]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">抗生素/污染物</span>
+              <span class="criterion-value">出厂检测有限,难以保证去除</span>
+            </li>
+            <li>
+              <span class="criterion-label">矿物质</span>
+              <span class="criterion-value">含天然矿物质(但伴塑料污染)</span>
+            </li>
+            <li>
+              <span class="criterion-label">BPA/塑化剂</span>
+              <span class="criterion-value">高温暴晒释放BPA、锑、邻苯二甲酸酯</span>
+            </li>
+            <li>
+              <span class="criterion-label">二次污染</span>
+              <span class="criterion-value">饮水机+桶身细菌滋生,密封垫磨损</span>
+            </li>
+            <li>
+              <span class="criterion-label">健康增益</span>
+              <span class="criterion-value">无</span>
+            </li>
+            <li>
+              <span class="criterion-label">致肥/焦虑风险</span>
+              <span class="criterion-value">⚠️ BPA/塑化剂关联致肥+焦虑<sup><a href="#ref-14">[14]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">综合成本</span>
+              <span class="criterion-value">高:每桶12~22元+饮水机折旧</span>
+            </li>
+          </ul>
+          <div class="card-footer danger">❌ 风险最高</div>
+        </div>
+
+        <!-- Card 2: 自来水(烧开后) -->
+        <div class="carousel-card border-success" data-index="1">
+          <span class="card-badge success">✅ 安全经济之选</span>
+          <div class="card-title">🚰 自来水(烧开后)</div>
+          <ul class="criteria-list">
+            <li>
+              <span class="criterion-label">微塑料</span>
+              <span class="criterion-value">~200万/升(原水),烧开去除84%+<sup><a href="#ref-4">[4]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">抗生素/污染物</span>
+              <span class="criterion-value" style="color:#C53030;">❌ 烧开无法去除抗生素、农药、避孕药等</span>
+            </li>
+            <li>
+              <span class="criterion-label">矿物质</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">✅ 完整保留钙镁等</span>
+            </li>
+            <li>
+              <span class="criterion-label">BPA/塑化剂</span>
+              <span class="criterion-value">无额外污染</span>
+            </li>
+            <li>
+              <span class="criterion-label">二次污染</span>
+              <span class="criterion-value">低(即烧即饮)</span>
+            </li>
+            <li>
+              <span class="criterion-label">健康增益</span>
+              <span class="criterion-value">无(仅为安全饮水)</span>
+            </li>
+            <li>
+              <span class="criterion-label">致肥/焦虑风险</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">❌ 无</span>
+            </li>
+            <li>
+              <span class="criterion-label">综合成本</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">✅ 极低</span>
+            </li>
+          </ul>
+          <div class="card-footer success">✅ 安全经济之选</div>
+        </div>
+
+        <!-- Card 3: RO反渗透纯水机 -->
+        <div class="carousel-card border-warning" data-index="2">
+          <span class="card-badge warning">⚠️ 缺矿物质</span>
+          <div class="card-title">💧 RO反渗透纯水机</div>
+          <ul class="criteria-list">
+            <li>
+              <span class="criterion-label">微塑料</span>
+              <span class="criterion-value">RO膜孔径0.0001μm,可截留<sup><a href="#ref-16">[16]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">抗生素/污染物</span>
+              <span class="criterion-value">RO膜可截留大部分大分子有机物</span>
+            </li>
+            <li>
+              <span class="criterion-label">矿物质</span>
+              <span class="criterion-value" style="color:#C53030;">❌ 完全去除所有矿物<sup><a href="#ref-16">[16]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">BPA/塑化剂</span>
+              <span class="criterion-value">小分子有机物部分可通过RO膜</span>
+            </li>
+            <li>
+              <span class="criterion-label">二次污染</span>
+              <span class="criterion-value">中高:滤芯久不换滋生细菌<sup><a href="#ref-18">[18]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">健康增益</span>
+              <span class="criterion-value">无(纯水呈弱酸性)</span>
+            </li>
+            <li>
+              <span class="criterion-label">致肥/焦虑风险</span>
+              <span class="criterion-value">不确定(无矿物水可能代谢负担)<sup><a href="#ref-17">[17]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">综合成本</span>
+              <span class="criterion-value">中高:设备+换芯+废水(1:3~7)</span>
+            </li>
+          </ul>
+          <div class="card-footer warning">⚠️ 缺矿物质</div>
+        </div>
+
+        <!-- Card 4: 电解还原水机 -->
+        <div class="carousel-card border-primary" data-index="3">
+          <span class="card-badge primary">🥇 综合最优解</span>
+          <div class="card-title">⚡ 电解还原水机</div>
+          <ul class="criteria-list">
+            <li>
+              <span class="criterion-label">微塑料</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">多层过滤可有效截留</span>
+            </li>
+            <li>
+              <span class="criterion-label">抗生素/污染物</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">活性炭+UF过滤有效去除余氯、农残、激素</span>
+            </li>
+            <li>
+              <span class="criterion-label">矿物质</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">✅ 完整保留,生物利用度更优</span>
+            </li>
+            <li>
+              <span class="criterion-label">BPA/塑化剂</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">有效过滤(活性炭+UF膜吸附)</span>
+            </li>
+            <li>
+              <span class="criterion-label">二次污染</span>
+              <span class="criterion-value">低(定期换芯即可)</span>
+            </li>
+            <li>
+              <span class="criterion-label">健康增益</span>
+              <span class="criterion-value" style="color:#2B6CB0;font-weight:600;">🏆 溶解氢抗氧化(ORP -250~-500mV)<sup><a href="#ref-19">[19]</a></sup></span>
+            </li>
+            <li>
+              <span class="criterion-label">致肥/焦虑风险</span>
+              <span class="criterion-value" style="color:#276749;font-weight:600;">❌ 无</span>
+            </li>
+            <li>
+              <span class="criterion-label">综合成本</span>
+              <span class="criterion-value">中高:设备+换芯</span>
+            </li>
+          </ul>
+          <div class="card-footer primary">🥇 综合最优解</div>
+        </div>
+      </div>
+
+      <div class="carousel-nav">
+        <button class="carousel-arrow carousel-prev" aria-label="上一项">‹</button>
+        <div class="carousel-dots">
+          <span class="carousel-dot active" data-index="0"></span>
+          <span class="carousel-dot" data-index="1"></span>
+          <span class="carousel-dot" data-index="2"></span>
+          <span class="carousel-dot" data-index="3"></span>
+        </div>
+        <button class="carousel-arrow carousel-next" aria-label="下一项">›</button>
+      </div>
+    </div>
+
     <div class="two-col-grid" style="margin-top:1.5rem;">
       <div class="highlight-box green" style="margin:0;">
         <p><strong>自来水烧开</strong>——在微塑料层面是最安全经济的方案,但需注意:<strong>烧开无法去除抗生素、农药残留、避孕药等化学污染物</strong>。若水源存在此类风险,建议搭配活性炭滤水壶或选择过滤净水方案。结论:<strong>预算有限时的最优入门方案,但非完美方案</strong>。</p>
@@ -1297,5 +1600,54 @@
 <!-- Footer (shared) -->
 <div id="footer-placeholder"></div>
 <script src="js/footer.js"></script>
+<script>
+(function(){
+  var track = document.getElementById('compareCarousel');
+  if (!track) return;
+  track = track.querySelector('.carousel-track');
+  var dots = document.querySelectorAll('.carousel-dot');
+  var prev = document.querySelector('.carousel-prev');
+  var next = document.querySelector('.carousel-next');
+  var cards = track.querySelectorAll('.carousel-card');
+  if (!cards.length) return;
+
+  var gap = 16; // 1rem in px
+  var updateDots = function(){
+    var scrollLeft = track.scrollLeft;
+    var cardWidth = cards[0].offsetWidth + gap;
+    var activeIndex = Math.round(scrollLeft / cardWidth);
+    if (activeIndex < 0) activeIndex = 0;
+    if (activeIndex >= dots.length) activeIndex = dots.length - 1;
+    dots.forEach(function(d, i){
+      d.classList.toggle('active', i === activeIndex);
+    });
+  };
+
+  track.addEventListener('scroll', updateDots);
+
+  dots.forEach(function(dot){
+    dot.addEventListener('click', function(){
+      var index = parseInt(this.getAttribute('data-index'));
+      if (cards[index]) {
+        cards[index].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
+      }
+    });
+  });
+
+  if (prev) {
+    prev.addEventListener('click', function(){
+      var cardWidth = cards[0].offsetWidth + gap;
+      track.scrollBy({ left: -cardWidth, behavior: 'smooth' });
+    });
+  }
+
+  if (next) {
+    next.addEventListener('click', function(){
+      var cardWidth = cards[0].offsetWidth + gap;
+      track.scrollBy({ left: cardWidth, behavior: 'smooth' });
+    });
+  }
+})();
+</script>
 </body>
 </html>