docs: 体重管理员工端设计总览重构(总览页按目标承诺与签署/体重主页/每日打卡/历史记录/建议与报告/蓝牙设备六大主线重排并改密卡片尺寸与横屏卡片样式,页面清单全面切换到 weight-main-v2/goal-management-v2 等 v2 新版页面,新增 playwright 统一截图脚本 screenshot-weight.mjs 并按其重新生成全部截图,首页站点地图体重管理条目同步更新为 v2 页面列表)

This commit is contained in:
liuyunqin
2026-09-04 16:17:00 +08:00
parent 05e157e667
commit 29a09e9402
91 changed files with 663 additions and 280 deletions
@@ -3,42 +3,53 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>体重管理 - 页面设计总览</title>
<title>体重管理员工端 · 页面设计总览</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1a1a2e; color: #e0e0e0; }
/* 顶部导航 */
.nav { position: sticky; top: 0; z-index: 100; background: #16213e; border-bottom: 1px solid #0f3460; padding: 14px 24px; display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.nav__title { font-size: 16px; font-weight: 600; color: #4fc3f7; margin-right: 20px; white-space: nowrap; }
.nav__title { font-size: 16px; font-weight: 600; color: #4fc3f7; margin-right: 16px; white-space: nowrap; }
.nav__link { font-size: 13px; color: #aaa; text-decoration: none; padding: 5px 12px; border-radius: 20px; transition: all .2s; white-space: nowrap; border: 1px solid transparent; }
.nav__link:hover { background: #0f3460; color: #4fc3f7; border-color: #4fc3f7; }
/* 业务主线区块 */
.section { padding: 24px 24px 8px; }
.section__header { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.section__header { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.section__title { font-size: 15px; font-weight: 600; color: #fff; }
.section__count { font-size: 12px; color: #666; background: #2a2a4a; padding: 2px 8px; border-radius: 10px; }
/* 子分组标题 */
.sub-title { font-size: 12px; color: #6a8caf; margin: 20px 0 10px 0; font-weight: 500; padding-left: 2px; }
/* 横向滚动容器 */
.scroll-row { display: flex; gap: 16px; overflow-x: auto; padding-bottom: 16px; scrollbar-width: thin; scrollbar-color: #333 transparent; }
.scroll-row::-webkit-scrollbar { height: 6px; }
.scroll-row::-webkit-scrollbar-track { background: transparent; }
.scroll-row::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }
.card { flex-shrink: 0; width: 260px; cursor: pointer; transition: transform .2s; }
/* 卡片 */
.card { flex-shrink: 0; width: 240px; cursor: pointer; transition: transform .2s; }
.card:hover { transform: scale(1.02); }
.card__img { width: 260px; height: 564px; border-radius: 12px; overflow: hidden; border: 2px solid #2a2a4a; background: #111; transition: border-color .2s; }
.card__img { width: 240px; height: 520px; border-radius: 12px; overflow: hidden; border: 2px solid #2a2a4a; background: #111; transition: border-color .2s; }
.card:hover .card__img { border-color: #4fc3f7; }
.card__img img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
.card__label { margin-top: 8px; font-size: 12px; color: #999; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card__label { margin-top: 8px; font-size: 12px; color: #ccc; text-align: center; }
/* 横屏页卡片(844×390 比例) */
.card--wide { width: 496px; }
.card--wide .card__img { width: 496px; height: 229px; }
.card--wide .card__img img { object-fit: contain; background: #fff; }
.card__tags { display: flex; justify-content: center; gap: 4px; margin-top: 4px; flex-wrap: wrap; }
.tag { font-size: 10px; padding: 1px 5px; border-radius: 3px; }
.tag--modal { background: #4a2800; color: #ffa940; }
.tag--state { background: #002a4a; color: #4fc3f7; }
.tag--flow { background: #1a3a1a; color: #69db7c; }
.sub-title { font-size: 12px; color: #888; margin: 20px 0 8px 0; font-weight: 500; }
.divider { height: 1px; background: #2a2a4a; margin: 8px 24px; }
/* 灯箱 */
.lightbox { display: none; position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,0.92); align-items: center; justify-content: center; cursor: zoom-out; }
.lightbox.show { display: flex; }
.lightbox img { max-width: 90vw; max-height: 92vh; border-radius: 8px; }
@@ -49,274 +60,290 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-
<body>
<nav class="nav">
<span class="nav__title">体重管理员工端APP · 设计总览</span>
<span class="nav__title">体重管理员工端 · 设计总览</span>
<a class="nav__link" href="#sec-commit">目标承诺</a>
<a class="nav__link" href="#sec-main">体重管理主页</a>
<a class="nav__link" href="#sec-main">体重主页</a>
<a class="nav__link" href="#sec-checkin">每日打卡</a>
<a class="nav__link" href="#sec-history">历史记录</a>
<a class="nav__link" href="#sec-advice">建议与报告</a>
<a class="nav__link" href="#sec-device">蓝牙设备</a>
</nav>
<!-- ========== 一、目标承诺 ========== -->
<section class="section" id="sec-commit">
<div class="section__header">
<h2 class="section__title">一、目标承诺(多步骤流程)</h2>
<span class="section__count">31</span>
<h2 class="section__title">一、目标承诺与签署</h2>
<span class="section__count">22 屏</span>
</div>
<div class="sub-title">1. 目标承诺(goal-commitment.html)— 7 种状态</div>
<div class="sub-title">目标管理(goal-management-v2.html)· 未选/已选个人 + 活动选择/提交确认弹窗</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/goal-commitment.jpg','目标承诺 · 未选目标-有活动')">
<div class="card__img"><img src="screenshots/goal-commitment.jpg" alt=""></div>
<div class="card__label">目标承诺 · 未选目标-有活动</div>
<div class="card__tags"><span class="tag tag--state">默认状态</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-commitment-unset-noact.jpg','目标承诺 · 未选目标-无活动')">
<div class="card__img"><img src="screenshots/goal-commitment-unset-noact.jpg" alt=""></div>
<div class="card__label">目标承诺 · 未选目标-无活动</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-commitment-personal.jpg','目标承诺 · 已选个人目标')">
<div class="card__img"><img src="screenshots/goal-commitment-personal.jpg" alt=""></div>
<div class="card__label">目标承诺 · 已选个人目标</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-commitment-wait.jpg','目标承诺 · 待审核')">
<div class="card__img"><img src="screenshots/goal-commitment-wait.jpg" alt=""></div>
<div class="card__label">目标承诺 · 待审核</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-commitment-pass.jpg','目标承诺 · 审核通过')">
<div class="card__img"><img src="screenshots/goal-commitment-pass.jpg" alt=""></div>
<div class="card__label">目标承诺 · 审核通过</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-commitment-reject.jpg','目标承诺 · 已驳回')">
<div class="card__img"><img src="screenshots/goal-commitment-reject.jpg" alt=""></div>
<div class="card__label">目标承诺 · 已驳回</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-commitment-signed.jpg','目标承诺 · 已签署')">
<div class="card__img"><img src="screenshots/goal-commitment-signed.jpg" alt=""></div>
<div class="card__label">目标承诺 · 已签署</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-commitment-reason.jpg','目标承诺 · 驳回原因弹窗')">
<div class="card__img"><img src="screenshots/goal-commitment-reason.jpg" alt=""></div>
<div class="card__label">目标承诺 · 驳回原因弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
</div>
<div class="sub-title">2. 目标管理(goal-management.html)— 7 种状态</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/goal-management.jpg','目标管理 · 未选目标-有活动')">
<div class="card" onclick="openLightbox('screenshots/goal-management.jpg','目标管理 · 活动/个人Tab')">
<div class="card__img"><img src="screenshots/goal-management.jpg" alt=""></div>
<div class="card__label">目标管理 · 未选目标-有活动</div>
<div class="card__tags"><span class="tag tag--state">默认状态</span></div>
<div class="card__label">目标管理</div>
<div class="card__tags"><span class="tag tag--state">默认·活动</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-unset-noact.jpg','目标管理 · 未选目标-无活动')">
<div class="card__img"><img src="screenshots/goal-management-unset-noact.jpg" alt=""></div>
<div class="card__label">目标管理 · 未选目标-无活动</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-personal.jpg','目标管理 · 已选个人目标')">
<div class="card" onclick="openLightbox('screenshots/goal-management-personal.jpg','目标管理 · 个人目标')">
<div class="card__img"><img src="screenshots/goal-management-personal.jpg" alt=""></div>
<div class="card__label">目标管理 · 已选个人目标</div>
<div class="card__label">目标管理 · 个人目标</div>
<div class="card__tags"><span class="tag tag--state">个人Tab</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-wait.jpg','目标管理 · 待审核')">
<div class="card__img"><img src="screenshots/goal-management-wait.jpg" alt=""></div>
<div class="card__label">目标管理 · 待审核</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-pass.jpg','目标管理 · 审核通过')">
<div class="card__img"><img src="screenshots/goal-management-pass.jpg" alt=""></div>
<div class="card__label">目标管理 · 审核通过</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-reject.jpg','目标管理 · 已驳回')">
<div class="card__img"><img src="screenshots/goal-management-reject.jpg" alt=""></div>
<div class="card__label">目标管理 · 已驳回</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-signed.jpg','目标管理 · 已签署')">
<div class="card__img"><img src="screenshots/goal-management-signed.jpg" alt=""></div>
<div class="card__label">目标管理 · 已签署</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-act-sheet.jpg','目标管理 · 活动选择弹窗')">
<div class="card__img"><img src="screenshots/goal-management-act-sheet.jpg" alt=""></div>
<div class="card__label">目标管理 · 活动选择弹窗</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-activity.jpg','目标管理 · 活动选择弹窗')">
<div class="card__img"><img src="screenshots/goal-management-activity.jpg" alt=""></div>
<div class="card__label">活动选择弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-target-sheet.jpg','目标管理 · 标尺修改弹窗')">
<div class="card__img"><img src="screenshots/goal-management-target-sheet.jpg" alt=""></div>
<div class="card__label">目标管理 · 标尺修改弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-confirm.jpg','目标管理 · 提交确认弹窗')">
<div class="card" onclick="openLightbox('screenshots/goal-management-confirm.jpg','目标管理 · 活动目标提交确认弹窗')">
<div class="card__img"><img src="screenshots/goal-management-confirm.jpg" alt=""></div>
<div class="card__label">目标管理 · 提交确认弹窗</div>
<div class="card__label">活动目标·提交确认</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-reason.jpg','目标管理 · 驳回原因弹窗')">
<div class="card__img"><img src="screenshots/goal-management-reason.jpg" alt=""></div>
<div class="card__label">目标管理 · 驳回原因弹窗</div>
<div class="card" onclick="openLightbox('screenshots/goal-management-confirm-personal.jpg','目标管理 · 个人目标提交确认弹窗')">
<div class="card__img"><img src="screenshots/goal-management-confirm-personal.jpg" alt=""></div>
<div class="card__label">个人目标·提交确认</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
</div>
<div class="sub-title">3~9. 其他页面</div>
<div class="sub-title">目标详情(goal-detail-v2.html)· 活动五态 + 个人 + 编辑弹窗 + 类型切换菜单</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/goal-detail.jpg','目标详情 · 活动审核中')">
<div class="card__img"><img src="screenshots/goal-detail.jpg" alt=""></div>
<div class="card__label">详情 · 审核中</div>
<div class="card__tags"><span class="tag tag--state">review</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-detail-approved.jpg','目标详情 · 审核通过待签署')">
<div class="card__img"><img src="screenshots/goal-detail-approved.jpg" alt=""></div>
<div class="card__label">详情 · 待签署</div>
<div class="card__tags"><span class="tag tag--state">approved</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-detail-rejected.jpg','目标详情 · 已驳回')">
<div class="card__img"><img src="screenshots/goal-detail-rejected.jpg" alt=""></div>
<div class="card__label">详情 · 已驳回</div>
<div class="card__tags"><span class="tag tag--state">rejected</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-detail-signed.jpg','目标详情 · 已签署')">
<div class="card__img"><img src="screenshots/goal-detail-signed.jpg" alt=""></div>
<div class="card__label">详情 · 已签署</div>
<div class="card__tags"><span class="tag tag--state">signed</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-detail-ended.jpg','目标详情 · 已结束')">
<div class="card__img"><img src="screenshots/goal-detail-ended.jpg" alt=""></div>
<div class="card__label">详情 · 已结束</div>
<div class="card__tags"><span class="tag tag--state">ended</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-detail-personal.jpg','目标详情 · 个人目标')">
<div class="card__img"><img src="screenshots/goal-detail-personal.jpg" alt=""></div>
<div class="card__label">详情 · 个人目标</div>
<div class="card__tags"><span class="tag tag--state">personal</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-detail-edit.jpg','目标详情 · 编辑目标弹窗')">
<div class="card__img"><img src="screenshots/goal-detail-edit.jpg" alt=""></div>
<div class="card__label">编辑目标弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-detail-menu.jpg','目标详情 · 目标类型切换菜单')">
<div class="card__img"><img src="screenshots/goal-detail-menu.jpg" alt=""></div>
<div class="card__label">目标类型切换菜单</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
</div>
<div class="sub-title">承诺签署 / 历史 / 活动详情 / 基本信息</div>
<div class="scroll-row">
<div class="card card--wide" onclick="openLightbox('screenshots/promise-signing.jpg','签订承诺 · 横屏签名')">
<div class="card__img"><img src="screenshots/promise-signing.jpg" alt=""></div>
<div class="card__label">签订承诺</div>
<div class="card__tags"><span class="tag tag--flow">横屏签名</span></div>
</div>
<div class="card card--wide" onclick="openLightbox('screenshots/promise-signing-giveup.jpg','签订承诺 · 放弃确认弹窗')">
<div class="card__img"><img src="screenshots/promise-signing-giveup.jpg" alt=""></div>
<div class="card__label">放弃确认弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/my-promise.jpg','我的承诺书')">
<div class="card__img"><img src="screenshots/my-promise.jpg" alt=""></div>
<div class="card__label">我的承诺书</div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-history.jpg','历史目标 · 活动Tab')">
<div class="card__img"><img src="screenshots/goal-history.jpg" alt=""></div>
<div class="card__label">历史目标 · 活动</div>
<div class="card__tags"><span class="tag tag--state">默认Tab</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/goal-history-personal.jpg','历史目标 · 个人Tab')">
<div class="card__img"><img src="screenshots/goal-history-personal.jpg" alt=""></div>
<div class="card__label">历史目标 · 个人</div>
<div class="card__tags"><span class="tag tag--state">个人Tab</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/activity-detail.jpg','活动详情')">
<div class="card__img"><img src="screenshots/activity-detail.jpg" alt=""></div>
<div class="card__label">活动详情</div>
</div>
<div class="card" onclick="openLightbox('screenshots/basic-info.jpg','基本信息')">
<div class="card__img"><img src="screenshots/basic-info.jpg" alt=""></div>
<div class="card__label">基本信息</div>
</div>
<div class="card" onclick="openLightbox('screenshots/basic-info-height.jpg','基本信息 · 身高测量弹窗')">
<div class="card__img"><img src="screenshots/basic-info-height.jpg" alt=""></div>
<div class="card__label">基本信息 · 身高测量弹窗</div>
<div class="card__label">身高测量弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/basic-info-activity.jpg','基本信息 · 活动等级弹窗')">
<div class="card__img"><img src="screenshots/basic-info-activity.jpg" alt=""></div>
<div class="card__label">基本信息 · 活动等级弹窗</div>
<div class="card__label">活动等级弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/indicators-diseases.jpg','指标慢病 · 重点指标')">
<div class="card__img"><img src="screenshots/indicators-diseases.jpg" alt=""></div>
<div class="card__label">指标慢病 · 重点指标</div>
<div class="card__tags"><span class="tag tag--state">默认Tab</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/indicators-diseases-diseases.jpg','指标慢病 · 慢性疾病')">
<div class="card__img"><img src="screenshots/indicators-diseases-diseases.jpg" alt=""></div>
<div class="card__label">指标慢病 · 慢性疾病</div>
</div>
<div class="card" onclick="openLightbox('screenshots/indicators-diseases-measure.jpg','指标慢病 · 测量标尺弹窗')">
<div class="card__img"><img src="screenshots/indicators-diseases-measure.jpg" alt=""></div>
<div class="card__label">指标慢病 · 测量标尺弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/indicators-diseases-disease-edit.jpg','指标慢病 · 慢病修改弹窗')">
<div class="card__img"><img src="screenshots/indicators-diseases-disease-edit.jpg" alt=""></div>
<div class="card__label">指标慢病 · 慢病修改弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/contraindications.jpg','禁忌证')">
<div class="card__img"><img src="screenshots/contraindications.jpg" alt=""></div>
<div class="card__label">禁忌证</div>
</div>
<div class="card" onclick="openLightbox('screenshots/promise-signing.jpg','签订承诺')">
<div class="card__img"><img src="screenshots/promise-signing.jpg" alt=""></div>
<div class="card__label">签订承诺</div>
<div class="card__tags"><span class="tag tag--flow">横屏签名</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/promise-signing-giveup.jpg','签订承诺 · 放弃确认弹窗')">
<div class="card__img"><img src="screenshots/promise-signing-giveup.jpg" alt=""></div>
<div class="card__label">签订承诺 · 放弃确认弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/my-promise.jpg','我的承诺')">
<div class="card__img"><img src="screenshots/my-promise.jpg" alt=""></div>
<div class="card__label">我的承诺</div>
</div>
<div class="card" onclick="openLightbox('screenshots/activity-detail.jpg','活动详情')">
<div class="card__img"><img src="screenshots/activity-detail.jpg" alt=""></div>
<div class="card__label">活动详情</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ========== 二、体重管理主页 ========== -->
<section class="section" id="sec-main">
<div class="section__header">
<h2 class="section__title">二、体重管理主页(核心中枢)</h2>
<span class="section__count">3</span>
<span class="section__count">9 屏</span>
</div>
<div class="sub-title">9. 体重管理主页(weight-main.html— 3 种模式</div>
<div class="sub-title">体重管理主页(weight-main-v2.html· 目标状态 + 记录弹窗 + 切换菜单</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/weight-main.jpg','体重管理主页 · 已参加活动')">
<div class="card" onclick="openLightbox('screenshots/weight-main.jpg','主页 · 活动已签署')">
<div class="card__img"><img src="screenshots/weight-main.jpg" alt=""></div>
<div class="card__label">体重管理主页 · 已参加活动</div>
<div class="card__label">主页 · 已签署</div>
<div class="card__tags"><span class="tag tag--state">默认状态</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-personal.jpg','体重管理主页 · 已设置个人目标')">
<div class="card__img"><img src="screenshots/weight-main-personal.jpg" alt=""></div>
<div class="card__label">体重管理主页 · 已设置个人目标</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-review.jpg','主页 · 活动审核中')">
<div class="card__img"><img src="screenshots/weight-main-review.jpg" alt=""></div>
<div class="card__label">主页 · 审核中</div>
<div class="card__tags"><span class="tag tag--state">review</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-none.jpg','体重管理主页 · 未参加未设置未组织')">
<div class="card" onclick="openLightbox('screenshots/weight-main-approved.jpg','主页 · 待签署')">
<div class="card__img"><img src="screenshots/weight-main-approved.jpg" alt=""></div>
<div class="card__label">主页 · 待签署</div>
<div class="card__tags"><span class="tag tag--state">approved</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-rejected.jpg','主页 · 已驳回')">
<div class="card__img"><img src="screenshots/weight-main-rejected.jpg" alt=""></div>
<div class="card__label">主页 · 已驳回</div>
<div class="card__tags"><span class="tag tag--state">rejected</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-personal.jpg','主页 · 个人目标')">
<div class="card__img"><img src="screenshots/weight-main-personal.jpg" alt=""></div>
<div class="card__label">主页 · 个人目标</div>
<div class="card__tags"><span class="tag tag--state">personal</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-none.jpg','主页 · 未参加未设置')">
<div class="card__img"><img src="screenshots/weight-main-none.jpg" alt=""></div>
<div class="card__label">体重管理主页 · 未参加未设置未组织</div>
<div class="card__label">主页 · 无目标</div>
<div class="card__tags"><span class="tag tag--state">none</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-record.jpg','主页 · 记录体重弹窗')">
<div class="card__img"><img src="screenshots/weight-main-record.jpg" alt=""></div>
<div class="card__label">记录体重弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-record-success.jpg','主页 · 记录成功弹窗')">
<div class="card__img"><img src="screenshots/weight-main-record-success.jpg" alt=""></div>
<div class="card__label">记录成功弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-main-menu.jpg','主页 · 目标类型切换菜单')">
<div class="card__img"><img src="screenshots/weight-main-menu.jpg" alt=""></div>
<div class="card__label">目标类型切换菜单</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ========== 三、每日打卡 ========== -->
<section class="section" id="sec-checkin">
<div class="section__header">
<h2 class="section__title">三、每日打卡</h2>
<span class="section__count">15</span>
<h2 class="section__title">三、每日打卡(吃 / 动 / 拍照识别)</h2>
<span class="section__count">15</span>
</div>
<div class="sub-title">吃打卡(eat-checkin-v2.html)· 餐次切换 + 已选清单/克重/成功弹窗</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/eat-checkin.jpg','吃打卡')">
<div class="card" onclick="openLightbox('screenshots/eat-checkin.jpg','吃打卡 · 早餐')">
<div class="card__img"><img src="screenshots/eat-checkin.jpg" alt=""></div>
<div class="card__label">吃打卡</div>
<div class="card__tags"><span class="tag tag--state">智记/自记</span></div>
<div class="card__label">吃打卡 · 早餐</div>
<div class="card__tags"><span class="tag tag--state">默认</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-portion.jpg','吃打卡 · 份量录入弹窗')">
<div class="card__img"><img src="screenshots/eat-checkin-portion.jpg" alt=""></div>
<div class="card__label">吃打卡 · 份量录入弹窗</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-lunch.jpg','吃打卡 · 午餐')">
<div class="card__img"><img src="screenshots/eat-checkin-lunch.jpg" alt=""></div>
<div class="card__label">吃打卡 · 午餐</div>
<div class="card__tags"><span class="tag tag--state">meal=lunch</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-recorded.jpg','吃打卡 · 已记录分类')">
<div class="card__img"><img src="screenshots/eat-checkin-recorded.jpg" alt=""></div>
<div class="card__label">已记录分类</div>
<div class="card__tags"><span class="tag tag--state">已记录</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-bag.jpg','吃打卡 · 已选清单弹窗')">
<div class="card__img"><img src="screenshots/eat-checkin-bag.jpg" alt=""></div>
<div class="card__label">已选清单弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-summary.jpg','吃打卡 · 已选汇总弹窗')">
<div class="card__img"><img src="screenshots/eat-checkin-summary.jpg" alt=""></div>
<div class="card__label">吃打卡 · 已选汇总弹窗</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-gram.jpg','吃打卡 · 克重录入弹窗')">
<div class="card__img"><img src="screenshots/eat-checkin-gram.jpg" alt=""></div>
<div class="card__label">克重录入弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-confirm.jpg','吃打卡 · 确认提交弹窗')">
<div class="card__img"><img src="screenshots/eat-checkin-confirm.jpg" alt=""></div>
<div class="card__label">吃打卡 · 确认提交弹窗</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-success.jpg','吃打卡 · 记录成功弹窗')">
<div class="card__img"><img src="screenshots/eat-checkin-success.jpg" alt=""></div>
<div class="card__label">记录成功弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-tip.jpg','吃打卡 · 提示弹窗')">
<div class="card__img"><img src="screenshots/eat-checkin-tip.jpg" alt=""></div>
<div class="card__label">吃打卡 · 提示弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
<div class="card" onclick="openLightbox('screenshots/eat-checkin-meals.jpg','吃打卡 · 餐次切换下拉')">
<div class="card__img"><img src="screenshots/eat-checkin-meals.jpg" alt=""></div>
<div class="card__label">餐次切换下拉</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin.jpg','动打卡')">
</div>
<div class="sub-title">动打卡(exercise-checkin-v2.html)· 已选清单/时长/成功弹窗</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/exercise-checkin.jpg','动打卡 · 默认')">
<div class="card__img"><img src="screenshots/exercise-checkin.jpg" alt=""></div>
<div class="card__label">动打卡</div>
<div class="card__tags"><span class="tag tag--state">4种来源</span></div>
<div class="card__label">动打卡 · 默认</div>
<div class="card__tags"><span class="tag tag--state">默认</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin-recorded.jpg','动打卡 · 已记录分类')">
<div class="card__img"><img src="screenshots/exercise-checkin-recorded.jpg" alt=""></div>
<div class="card__label">已记录分类</div>
<div class="card__tags"><span class="tag tag--state">已记录</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin-bag.jpg','动打卡 · 已选清单弹窗')">
<div class="card__img"><img src="screenshots/exercise-checkin-bag.jpg" alt=""></div>
<div class="card__label">已选清单弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin-duration.jpg','动打卡 · 时长录入弹窗')">
<div class="card__img"><img src="screenshots/exercise-checkin-duration.jpg" alt=""></div>
<div class="card__label">动打卡 · 时长录入弹窗</div>
<div class="card__label">时长录入弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin-summary.jpg','动打卡 · 已选汇总弹窗')">
<div class="card__img"><img src="screenshots/exercise-checkin-summary.jpg" alt=""></div>
<div class="card__label">动打卡 · 已选汇总弹窗</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin-success.jpg','动打卡 · 记录成功弹窗')">
<div class="card__img"><img src="screenshots/exercise-checkin-success.jpg" alt=""></div>
<div class="card__label">记录成功弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin-confirm.jpg','动打卡 · 确认提交弹窗')">
<div class="card__img"><img src="screenshots/exercise-checkin-confirm.jpg" alt=""></div>
<div class="card__label">动打卡 · 确认提交弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="sub-title">拍照识别打卡(scan-checkin.html)· 取景 → 识别中 → 结果</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/scan-checkin.jpg','拍照识别 · 取景')">
<div class="card__img"><img src="screenshots/scan-checkin.jpg" alt=""></div>
<div class="card__label">拍照识别 · 取景</div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-checkin-tip.jpg','动打卡 · 提示弹窗')">
<div class="card__img"><img src="screenshots/exercise-checkin-tip.jpg" alt=""></div>
<div class="card__label">动打卡 · 提示弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
<div class="card" onclick="openLightbox('screenshots/scan-checkin-analyzing.jpg','拍照识别 · AI识别中')">
<div class="card__img"><img src="screenshots/scan-checkin-analyzing.jpg" alt=""></div>
<div class="card__label">AI 识别中</div>
<div class="card__tags"><span class="tag tag--state">进度浮层</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-checkin.jpg','体重打卡')">
<div class="card__img"><img src="screenshots/weight-checkin.jpg" alt=""></div>
<div class="card__label">体重打卡</div>
<div class="card__tags"><span class="tag tag--state">4种来源</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-checkin-value.jpg','体重打卡 · 数值录入弹窗')">
<div class="card__img"><img src="screenshots/weight-checkin-value.jpg" alt=""></div>
<div class="card__label">体重打卡 · 数值录入弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-checkin-summary.jpg','体重打卡 · 已选汇总弹窗')">
<div class="card__img"><img src="screenshots/weight-checkin-summary.jpg" alt=""></div>
<div class="card__label">体重打卡 · 已选汇总弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-checkin-confirm.jpg','体重打卡 · 确认提交弹窗')">
<div class="card__img"><img src="screenshots/weight-checkin-confirm.jpg" alt=""></div>
<div class="card__label">体重打卡 · 确认提交弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-checkin-tip.jpg','体重打卡 · 提示弹窗')">
<div class="card__img"><img src="screenshots/weight-checkin-tip.jpg" alt=""></div>
<div class="card__label">体重打卡 · 提示弹窗</div>
<div class="card" onclick="openLightbox('screenshots/scan-checkin-result.jpg','拍照识别 · 结果弹窗')">
<div class="card__img"><img src="screenshots/scan-checkin-result.jpg" alt=""></div>
<div class="card__label">识别结果弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
</div>
@@ -324,104 +351,242 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-
<div class="divider"></div>
<!-- ========== 四、历史记录 ========== -->
<section class="section" id="sec-history">
<div class="section__header">
<h2 class="section__title">四、历史记录</h2>
<span class="section__count">7</span>
<h2 class="section__title">四、历史记录与报告详情</h2>
<span class="section__count">16 屏</span>
</div>
<div class="sub-title">记录明细(周视图 / 月历浮层 / 数据点浮签 / 设备来源)</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/weight-history.jpg','体重记录')">
<div class="card__img"><img src="screenshots/weight-history.jpg" alt=""></div>
<div class="card__label">体重记录</div>
<div class="card__tags"><span class="tag tag--state">日历+列表</span></div>
<div class="card" onclick="openLightbox('screenshots/eat-detail.jpg','摄入记录 · 周')">
<div class="card__img"><img src="screenshots/eat-detail.jpg" alt=""></div>
<div class="card__label">摄入记录 · 周</div>
<div class="card__tags"><span class="tag tag--state">默认</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/report-detail.jpg','详细报告 · 体脂仪')">
<div class="card__img"><img src="screenshots/report-detail.jpg" alt=""></div>
<div class="card__label">详细报告 · 体脂仪</div>
<div class="card__tags"><span class="tag tag--state">默认设备</span></div>
<div class="card" onclick="openLightbox('screenshots/eat-detail-month.jpg','摄入记录 · 月历弹窗')">
<div class="card__img"><img src="screenshots/eat-detail-month.jpg" alt=""></div>
<div class="card__label">摄入 · 月历弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-detail.jpg','消耗记录')">
<div class="card__img"><img src="screenshots/exercise-detail.jpg" alt=""></div>
<div class="card__label">消耗记录</div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-detail-month.jpg','消耗记录 · 月历浮层')">
<div class="card__img"><img src="screenshots/exercise-detail-month.jpg" alt=""></div>
<div class="card__label">消耗 · 月历浮层</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-detail-tip.jpg','消耗记录 · 图表数据点浮签')">
<div class="card__img"><img src="screenshots/exercise-detail-tip.jpg" alt=""></div>
<div class="card__label">消耗 · 数据点浮签</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-exercise-detail.jpg','吃动平衡明细')">
<div class="card__img"><img src="screenshots/eat-exercise-detail.jpg" alt=""></div>
<div class="card__label">吃动平衡明细</div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-exercise-detail-month.jpg','吃动平衡 · 月历浮层')">
<div class="card__img"><img src="screenshots/eat-exercise-detail-month.jpg" alt=""></div>
<div class="card__label">吃动平衡 · 月历浮层</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-detail.jpg','体重明细 · 周')">
<div class="card__img"><img src="screenshots/weight-detail.jpg" alt=""></div>
<div class="card__label">体重明细 · 周</div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-detail-record.jpg','体重明细 · 记录弹窗')">
<div class="card__img"><img src="screenshots/weight-detail-record.jpg" alt=""></div>
<div class="card__label">体重 · 记录弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-detail-month.jpg','体重明细 · 月历浮层')">
<div class="card__img"><img src="screenshots/weight-detail-month.jpg" alt=""></div>
<div class="card__label">体重 · 月历浮层</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-detail-tip.jpg','体重明细 · 图表数据点浮签')">
<div class="card__img"><img src="screenshots/weight-detail-tip.jpg" alt=""></div>
<div class="card__label">体重 · 数据点浮签</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
</div>
<div class="sub-title">详细报告(report-detail.html 三设备 + 体脂 / 身高报告)</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/report-detail-fat.jpg','详细报告 · 体脂仪')">
<div class="card__img"><img src="screenshots/report-detail-fat.jpg" alt=""></div>
<div class="card__label">报告 · 体脂仪</div>
</div>
<div class="card" onclick="openLightbox('screenshots/report-detail-scale.jpg','详细报告 · 体重秤')">
<div class="card__img"><img src="screenshots/report-detail-scale.jpg" alt=""></div>
<div class="card__label">详细报告 · 体重秤</div>
<div class="card__label">报告 · 体重秤</div>
</div>
<div class="card" onclick="openLightbox('screenshots/report-detail-bluetooth.jpg','详细报告 · 蓝牙秤')">
<div class="card__img"><img src="screenshots/report-detail-bluetooth.jpg" alt=""></div>
<div class="card__label">详细报告 · 蓝牙秤</div>
<div class="card__label">报告 · 蓝牙秤</div>
</div>
<div class="card" onclick="openLightbox('screenshots/eat-history.jpg','摄入记录')">
<div class="card__img"><img src="screenshots/eat-history.jpg" alt=""></div>
<div class="card__label">摄入记录</div>
<div class="card" onclick="openLightbox('screenshots/body-fat-report.jpg','体脂报告')">
<div class="card__img"><img src="screenshots/body-fat-report.jpg" alt=""></div>
<div class="card__label">体脂报告</div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-history.jpg','消耗记录')">
<div class="card__img"><img src="screenshots/exercise-history.jpg" alt=""></div>
<div class="card__label">消耗记录</div>
</div>
<div class="card" onclick="openLightbox('screenshots/balance-history.jpg','吃动平衡历史')">
<div class="card__img"><img src="screenshots/balance-history.jpg" alt=""></div>
<div class="card__label">吃动平衡历史</div>
<div class="card__tags"><span class="tag tag--state">月历+柱状图</span></div>
<div class="card" onclick="openLightbox('screenshots/height-scale-report.jpg','身高测量报告')">
<div class="card__img"><img src="screenshots/height-scale-report.jpg" alt=""></div>
<div class="card__label">身高测量报告</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ========== 五、建议与报告 ========== -->
<section class="section" id="sec-advice">
<div class="section__header">
<h2 class="section__title">五、建议与报告</h2>
<span class="section__count">13</span>
<span class="section__count">16 屏</span>
</div>
<div class="sub-title">21. 体重报告(weight-report.html)— 3 种模式</div>
<div class="sub-title">膳食建议(diet-advice-v2.html)· 食谱/营养分析Tab + 详情/不喜欢/月历弹窗</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/weight-report.jpg','体重报告 · 已参加活动')">
<div class="card__img"><img src="screenshots/weight-report.jpg" alt=""></div>
<div class="card__label">体重报告 · 已参加活动</div>
<div class="card__tags"><span class="tag tag--state">默认状态</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-report-personal.jpg','体重报告 · 已设置个人目标')">
<div class="card__img"><img src="screenshots/weight-report-personal.jpg" alt=""></div>
<div class="card__label">体重报告 · 已设置个人目标</div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-report-none.jpg','体重报告 · 未参加未设置')">
<div class="card__img"><img src="screenshots/weight-report-none.jpg" alt=""></div>
<div class="card__label">体重报告 · 未参加未设置</div>
</div>
</div>
<div class="sub-title">22. 排行榜(weight-ranking.html)— 3 种模式</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/weight-ranking.jpg','排行榜 · 已参加活动')">
<div class="card__img"><img src="screenshots/weight-ranking.jpg" alt=""></div>
<div class="card__label">排行榜 · 已参加活动</div>
<div class="card__tags"><span class="tag tag--state">默认状态</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-ranking-personal.jpg','排行榜 · 已设置个人目标')">
<div class="card__img"><img src="screenshots/weight-ranking-personal.jpg" alt=""></div>
<div class="card__label">排行榜 · 已设置个人目标</div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-ranking-none.jpg','排行榜 · 未参加未设置未组织')">
<div class="card__img"><img src="screenshots/weight-ranking-none.jpg" alt=""></div>
<div class="card__label">排行榜 · 未参加未设置未组织</div>
</div>
</div>
<div class="sub-title">18~20. 其他页面</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/diet-advice.jpg','膳食建议')">
<div class="card" onclick="openLightbox('screenshots/diet-advice.jpg','膳食建议 · 食谱')">
<div class="card__img"><img src="screenshots/diet-advice.jpg" alt=""></div>
<div class="card__label">膳食建议</div>
<div class="card__label">膳食建议 · 食谱</div>
</div>
<div class="card" onclick="openLightbox('screenshots/diet-advice-detail.jpg','膳食建议 · 餐品详情弹窗')">
<div class="card__img"><img src="screenshots/diet-advice-detail.jpg" alt=""></div>
<div class="card__label">膳食建议 · 餐品详情弹窗</div>
<div class="card__label">餐品详情弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/diet-advice-dislike.jpg','膳食建议 · 不喜欢确认弹窗')">
<div class="card" onclick="openLightbox('screenshots/diet-advice-dislike.jpg','膳食建议 · 不喜欢弹窗')">
<div class="card__img"><img src="screenshots/diet-advice-dislike.jpg" alt=""></div>
<div class="card__label">膳食建议 · 不喜欢确认弹窗</div>
<div class="card__label">不喜欢弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-advice.jpg','运动建议')">
<div class="card" onclick="openLightbox('screenshots/diet-advice-nutrition.jpg','膳食建议 · 营养分析空态')">
<div class="card__img"><img src="screenshots/diet-advice-nutrition.jpg" alt=""></div>
<div class="card__label">营养分析空态</div>
<div class="card__tags"><span class="tag tag--state">空态</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/diet-advice-nutrition-tab.jpg','膳食建议 · 营养分析Tab')">
<div class="card__img"><img src="screenshots/diet-advice-nutrition-tab.jpg" alt=""></div>
<div class="card__label">营养分析 Tab</div>
<div class="card__tags"><span class="tag tag--state">Tab切换</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/diet-advice-month.jpg','膳食建议 · 月历浮层')">
<div class="card__img"><img src="screenshots/diet-advice-month.jpg" alt=""></div>
<div class="card__label">膳食 · 月历浮层</div>
<div class="card__tags"><span class="tag tag--modal">浮层</span></div>
</div>
</div>
<div class="sub-title">运动建议(exercise-advice-v2.html)· 周 / 月历</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/exercise-advice.jpg','运动建议 · 周')">
<div class="card__img"><img src="screenshots/exercise-advice.jpg" alt=""></div>
<div class="card__label">运动建议</div>
<div class="card__label">运动建议 · 周</div>
</div>
<div class="card" onclick="openLightbox('screenshots/exercise-advice-month.jpg','运动建议 · 月历弹窗')">
<div class="card__img"><img src="screenshots/exercise-advice-month.jpg" alt=""></div>
<div class="card__label">运动 · 月历弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
</div>
<div class="sub-title">体重报告(weight-report.html + weight-record-detail-v2.html)· 三种模式与记录范围切换</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/weight-report.jpg','体重报告 · 已参加活动')">
<div class="card__img"><img src="screenshots/weight-report.jpg" alt=""></div>
<div class="card__label">报告 · 有活动</div>
<div class="card__tags"><span class="tag tag--state">hasact</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-report-personal.jpg','体重报告 · 个人目标')">
<div class="card__img"><img src="screenshots/weight-report-personal.jpg" alt=""></div>
<div class="card__label">报告 · 个人目标</div>
<div class="card__tags"><span class="tag tag--state">personal</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-report-none.jpg','体重报告 · 未参加未设置')">
<div class="card__img"><img src="screenshots/weight-report-none.jpg" alt=""></div>
<div class="card__label">报告 · 无目标</div>
<div class="card__tags"><span class="tag tag--state">none</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-record-detail.jpg','体重记录明细 · 周Tab')">
<div class="card__img"><img src="screenshots/weight-record-detail.jpg" alt=""></div>
<div class="card__label">体重记录明细 · 周</div>
<div class="card__tags"><span class="tag tag--state">默认Tab</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-record-detail-activity.jpg','体重记录明细 · 活动期间Tab')">
<div class="card__img"><img src="screenshots/weight-record-detail-activity.jpg" alt=""></div>
<div class="card__label">体重记录明细 · 活动期间</div>
<div class="card__tags"><span class="tag tag--state">Tab切换</span></div>
</div>
</div>
<div class="sub-title">排行榜(weight-ranking.html)· 三种模式</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/weight-ranking.jpg','排行榜 · 已参加活动')">
<div class="card__img"><img src="screenshots/weight-ranking.jpg" alt=""></div>
<div class="card__label">排行 · 有活动</div>
<div class="card__tags"><span class="tag tag--state">hasact</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-ranking-personal.jpg','排行榜 · 个人目标')">
<div class="card__img"><img src="screenshots/weight-ranking-personal.jpg" alt=""></div>
<div class="card__label">排行 · 个人目标</div>
<div class="card__tags"><span class="tag tag--state">personal</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/weight-ranking-none.jpg','排行榜 · 未参加未设置')">
<div class="card__img"><img src="screenshots/weight-ranking-none.jpg" alt=""></div>
<div class="card__label">排行 · 无目标</div>
<div class="card__tags"><span class="tag tag--state">none</span></div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ========== 六、蓝牙设备 ========== -->
<section class="section" id="sec-device">
<div class="section__header">
<h2 class="section__title">六、蓝牙设备</h2>
<span class="section__count">8 屏</span>
</div>
<div class="sub-title">蓝牙秤管理 / 成员管理 / 配网</div>
<div class="scroll-row">
<div class="card" onclick="openLightbox('screenshots/bluetooth-scale.jpg','蓝牙秤 · 设备列表')">
<div class="card__img"><img src="screenshots/bluetooth-scale.jpg" alt=""></div>
<div class="card__label">蓝牙秤 · 列表</div>
</div>
<div class="card" onclick="openLightbox('screenshots/bluetooth-scale-empty.jpg','蓝牙秤 · 空态')">
<div class="card__img"><img src="screenshots/bluetooth-scale-empty.jpg" alt=""></div>
<div class="card__label">蓝牙秤 · 空态</div>
<div class="card__tags"><span class="tag tag--state">空态</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/bluetooth-device-members.jpg','成员管理 · 已连接')">
<div class="card__img"><img src="screenshots/bluetooth-device-members.jpg" alt=""></div>
<div class="card__label">成员 · 已连接</div>
<div class="card__tags"><span class="tag tag--state">S1</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/bluetooth-device-members-empty.jpg','成员管理 · 未连接空成员')">
<div class="card__img"><img src="screenshots/bluetooth-device-members-empty.jpg" alt=""></div>
<div class="card__label">成员 · 未连接</div>
<div class="card__tags"><span class="tag tag--state">B2</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/bluetooth-device-members-delete.jpg','成员管理 · 删除确认弹窗')">
<div class="card__img"><img src="screenshots/bluetooth-device-members-delete.jpg" alt=""></div>
<div class="card__label">删除确认弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/bluetooth-device-members-sync.jpg','成员管理 · 同步进度弹窗')">
<div class="card__img"><img src="screenshots/bluetooth-device-members-sync.jpg" alt=""></div>
<div class="card__label">同步进度弹窗</div>
<div class="card__tags"><span class="tag tag--modal">弹窗</span></div>
</div>
<div class="card" onclick="openLightbox('screenshots/bluetooth-search.jpg','蓝牙搜索')">
<div class="card__img"><img src="screenshots/bluetooth-search.jpg" alt=""></div>
<div class="card__label">蓝牙搜索</div>
</div>
<div class="card" onclick="openLightbox('screenshots/bluetooth-wifi.jpg','配网')">
<div class="card__img"><img src="screenshots/bluetooth-wifi.jpg" alt=""></div>
<div class="card__label">设备配网</div>
</div>
</div>
</section>
@@ -443,6 +608,7 @@ function openLightbox(src, name) {
function closeLightbox() {
document.getElementById('lightbox').classList.remove('show');
}
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeLightbox(); });
</script>
</body>
</html>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 80 KiB

+25 -18
View File
@@ -608,26 +608,33 @@ const SITE_MAP = [
] },
{ name:'体重管理', dir:'employee-app/weight-management', entry:'weight-main-v2.html', prd:'employee-app/weight-management/doc/PRD.md', done:true, pages:[
{ file:'doc/design-overview.html', label:'页面总览' },
{ file:'weight-main.html', label:'体重管理主页' },
{ file:'weight-ranking.html', label:'排行榜' },
{ file:'eat-checkin.html', label:'吃打卡' },
{ file:'eat-history.html', label:'摄入记录历史' },
{ file:'goal-commitment.html', label:'目标承诺' },
{ file:'basic-info.html', label:'基本信息' },
{ file:'indicators-diseases.html', label:'指标慢病' },
{ file:'goal-management.html', label:'目标管理' },
{ file:'activity-detail.html', label:'活动详情' },
{ file:'contraindications.html', label:'禁忌证' },
{ file:'promise-signing.html', label:'承诺签订' },
{ file:'weight-main-v2.html', label:'体重管理主页' },
{ file:'goal-management-v2.html', label:'目标管理' },
{ file:'goal-detail-v2.html', label:'目标详情' },
{ file:'goal-history-v2.html', label:'历史目标' },
{ file:'promise-signing.html', label:'签订承诺' },
{ file:'my-promise.html', label:'我的承诺书' },
{ file:'balance-history.html', label:'吃动平衡历史' },
{ file:'weight-history.html', label:'体重记录历史' },
{ file:'activity-detail.html', label:'活动详情' },
{ file:'basic-info.html', label:'基本信息' },
{ file:'eat-checkin-v2.html', label:'吃打卡' },
{ file:'exercise-checkin-v2.html', label:'动打卡' },
{ file:'scan-checkin.html', label:'拍照识别打卡' },
{ file:'eat-detail-v2.html', label:'摄入记录' },
{ file:'exercise-detail-v2.html', label:'消耗记录' },
{ file:'eat-exercise-detail-v2.html', label:'吃动平衡明细' },
{ file:'weight-detail-v2.html', label:'体重明细' },
{ file:'report-detail.html', label:'测量报告详情' },
{ file:'body-fat-report.html', label:'体脂报告' },
{ file:'height-scale-report.html', label:'身高测量报告' },
{ file:'diet-advice-v2.html', label:'膳食建议' },
{ file:'exercise-advice-v2.html', label:'运动建议' },
{ file:'weight-record-detail-v2.html', label:'体重记录明细' },
{ file:'weight-report.html', label:'体重报告' },
{ file:'diet-advice.html', label:'膳食建议' },
{ file:'exercise-advice.html', label:'运动建议' },
{ file:'exercise-checkin.html', label:'动打卡' },
{ file:'exercise-history.html', label:'消耗记录' },
{ file:'weight-checkin.html', label:'重打卡' }
{ file:'weight-ranking.html', label:'排行榜' },
{ file:'bluetooth-scale-v2.html', label:'蓝牙秤管理' },
{ file:'bluetooth-device-members-v2.html', label:'蓝牙设备成员管理' },
{ file:'bluetooth-search-v2.html', label:'蓝牙搜索' },
{ file:'bluetooth-wifi-v2.html', label:'设备配网' }
] },
{ name:'营养管理', dir:'employee-app/nutrition', pages:[] },
{ name:'运动管理', dir:'employee-app/exercise', pages:[] },
+210
View File
@@ -0,0 +1,210 @@
/**
* 体重管理模块统一截图脚本
* 规则(参考健康监测 screenshot-final.mjs):
* - phone-shell 尺寸 390 宽
* - 'fixed':弹窗/浮层类截图,严格 390×844,弹窗叠在当前页面之上
* - 'auto' :纯页面截图,短页面最低 844,长页面按真实内容展开
* 用法:node screenshot-weight.mjs [输出名子串过滤...]
*/
import { chromium } from 'playwright';
import { fileURLToPath } from 'url';
import path from 'path';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const docDir = path.join(__dirname, 'employee-app', 'weight-management', 'doc');
const root = path.join(__dirname, 'employee-app', 'weight-management');
const tasks = [
// ========== 目标承诺与签署 ==========
{ html: 'goal-management-v2.html', out: 'goal-management', mode: 'auto' },
{ html: 'goal-management-v2.html', out: 'goal-management-personal', mode: 'auto', eval: "switchType('personal')" },
{ html: 'goal-management-v2.html', out: 'goal-management-activity', mode: 'fixed', eval: 'openActivitySheet()' },
{ html: 'goal-management-v2.html', out: 'goal-management-confirm', mode: 'fixed', eval: "openActivitySheet();var i=document.querySelector('#actSheet .sheet-item');i.click();confirmActivity();closeActivitySheet();submitGoal()" },
{ html: 'goal-management-v2.html', out: 'goal-management-confirm-personal', mode: 'fixed', eval: "switchType('personal');submitGoal()" },
{ html: 'goal-detail-v2.html', out: 'goal-detail', mode: 'auto', query: '?type=activity&state=review' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-approved', mode: 'auto', query: '?type=activity&state=approved' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-rejected', mode: 'auto', query: '?type=activity&state=rejected' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-signed', mode: 'auto', query: '?type=activity&state=signed' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-ended', mode: 'auto', query: '?type=activity&state=ended' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-personal', mode: 'auto', query: '?type=personal' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-edit', mode: 'fixed', query: '?type=personal', eval: 'openEditGoalModal()' },
{ html: 'goal-detail-v2.html', out: 'goal-detail-menu', mode: 'fixed', query: '?type=activity&state=signed', eval: "document.getElementById('navMore').click()" },
{ html: 'promise-signing.html', out: 'promise-signing', mode: 'auto', landscape: true },
{ html: 'promise-signing.html', out: 'promise-signing-giveup', mode: 'fixed', landscape: true, eval: 'openGiveup()' },
{ html: 'my-promise.html', out: 'my-promise', mode: 'auto' },
{ html: 'goal-history-v2.html', out: 'goal-history', mode: 'auto' },
{ html: 'goal-history-v2.html', out: 'goal-history-personal', mode: 'auto', eval: "switchTab('personal')" },
{ html: 'activity-detail.html', out: 'activity-detail', mode: 'auto' },
{ html: 'basic-info.html', out: 'basic-info', mode: 'auto' },
{ html: 'basic-info.html', out: 'basic-info-height', mode: 'fixed', eval: 'openHeightSheet()' },
{ html: 'basic-info.html', out: 'basic-info-activity', mode: 'fixed', eval: 'openSheet()' },
// ========== 体重管理主页 ==========
{ html: 'weight-main-v2.html', out: 'weight-main', mode: 'auto', query: '?type=hasact-signed' },
{ html: 'weight-main-v2.html', out: 'weight-main-review', mode: 'auto', query: '?type=hasact-review' },
{ html: 'weight-main-v2.html', out: 'weight-main-approved', mode: 'auto', query: '?type=hasact-approved' },
{ html: 'weight-main-v2.html', out: 'weight-main-rejected', mode: 'auto', query: '?type=hasact-rejected' },
{ html: 'weight-main-v2.html', out: 'weight-main-personal', mode: 'auto', query: '?type=personal' },
{ html: 'weight-main-v2.html', out: 'weight-main-none', mode: 'auto', query: '?type=none' },
{ html: 'weight-main-v2.html', out: 'weight-main-record', mode: 'fixed', query: '?type=hasact-signed', eval: 'openRecordSheet()' },
{ html: 'weight-main-v2.html', out: 'weight-main-record-success', mode: 'fixed', query: '?type=hasact-signed', eval: "openRecordSheet();submitRecord()" },
{ html: 'weight-main-v2.html', out: 'weight-main-menu', mode: 'fixed', query: '?type=hasact-signed', eval: "document.getElementById('navMore').click()" },
// ========== 每日打卡(吃/动/体重) ==========
{ html: 'eat-checkin-v2.html', out: 'eat-checkin', mode: 'auto' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-lunch', mode: 'auto', query: '?meal=lunch' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-recorded', mode: 'auto', eval: "selectCategory('recorded')" },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-bag', mode: 'fixed', eval: 'openBagPanel()' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-gram', mode: 'fixed', eval: "var d;Object.keys(foodData).some(function(k){return foodData[k].items.some(function(it){if(it.name){d=it;return true}})});openGramPanel(d.id)" },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-success', mode: 'fixed', eval: 'submitCheckin()' },
{ html: 'eat-checkin-v2.html', out: 'eat-checkin-meals', mode: 'fixed', eval: "document.querySelector('.filter-row__meal-btn').click()" },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin', mode: 'auto' },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-recorded', mode: 'auto', eval: "selectCategory('recorded')" },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-bag', mode: 'fixed', eval: 'openBagPanel()' },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-duration', mode: 'fixed', eval: "var d;Object.keys(exerciseData).some(function(k){return exerciseData[k].items.some(function(it){if(it.name){d=it;return true}})});openDurPanel(d.id)" },
{ html: 'exercise-checkin-v2.html', out: 'exercise-checkin-success', mode: 'fixed', eval: 'submitCheckin()' },
{ html: 'scan-checkin.html', out: 'scan-checkin', mode: 'fixed' },
{ html: 'scan-checkin.html', out: 'scan-checkin-analyzing', mode: 'fixed', eval: "takePhoto();clearInterval(analyzeTimer);analyzeTimer=null;setTimeout(function(){document.getElementById('analyzingBar').style.width='45%';document.getElementById('analyzingSub').textContent='正在匹配食物数据库...'},300)", wait: 900 },
{ html: 'scan-checkin.html', out: 'scan-checkin-result', mode: 'fixed', eval: 'showResult()' },
// ========== 历史记录 ==========
{ html: 'weight-record-detail-v2.html', out: 'weight-record-detail', mode: 'auto' },
{ html: 'weight-record-detail-v2.html', out: 'weight-record-detail-activity', mode: 'auto', eval: "switchTab('activity')" },
{ html: 'eat-detail-v2.html', out: 'eat-detail', mode: 'auto' },
{ html: 'eat-detail-v2.html', out: 'eat-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'exercise-detail-v2.html', out: 'exercise-detail', mode: 'auto' },
{ html: 'exercise-detail-v2.html', out: 'exercise-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'exercise-detail-v2.html', out: 'exercise-detail-tip', mode: 'fixed', eval: "document.querySelector('.chart-dot').click()" },
{ html: 'eat-exercise-detail-v2.html', out: 'eat-exercise-detail', mode: 'auto' },
{ html: 'eat-exercise-detail-v2.html', out: 'eat-exercise-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'weight-detail-v2.html', out: 'weight-detail', mode: 'auto' },
{ html: 'weight-detail-v2.html', out: 'weight-detail-record', mode: 'fixed', eval: 'openRecordSheet()' },
{ html: 'weight-detail-v2.html', out: 'weight-detail-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'weight-detail-v2.html', out: 'weight-detail-tip', mode: 'fixed', eval: "document.querySelector('.chart-dot').click()" },
{ html: 'report-detail.html', out: 'report-detail-fat', mode: 'auto', query: '?device=fat' },
{ html: 'report-detail.html', out: 'report-detail-scale', mode: 'auto', query: '?device=scale' },
{ html: 'report-detail.html', out: 'report-detail-bluetooth', mode: 'auto', query: '?device=bluetooth' },
{ html: 'body-fat-report.html', out: 'body-fat-report', mode: 'auto' },
{ html: 'height-scale-report.html', out: 'height-scale-report', mode: 'auto' },
// ========== 建议与报告 ==========
{ html: 'diet-advice-v2.html', out: 'diet-advice', mode: 'auto' },
{ html: 'diet-advice-v2.html', out: 'diet-advice-detail', mode: 'fixed', eval: "openFoodDetail('breakfast',0)" },
{ html: 'diet-advice-v2.html', out: 'diet-advice-dislike', mode: 'fixed', eval: 'showDislikeSheet()' },
{ html: 'diet-advice-v2.html', out: 'diet-advice-nutrition', mode: 'fixed', eval: 'showNutritionEmpty(true)' },
{ html: 'diet-advice-v2.html', out: 'diet-advice-nutrition-tab', mode: 'auto', eval: "switchTab('nutrition')" },
{ html: 'diet-advice-v2.html', out: 'diet-advice-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'exercise-advice-v2.html', out: 'exercise-advice', mode: 'auto' },
{ html: 'exercise-advice-v2.html', out: 'exercise-advice-month', mode: 'fixed', eval: 'toggleMonthPicker()' },
{ html: 'weight-report.html', out: 'weight-report', mode: 'auto', query: '?type=hasact' },
{ html: 'weight-report.html', out: 'weight-report-personal', mode: 'auto', query: '?type=personal' },
{ html: 'weight-report.html', out: 'weight-report-none', mode: 'auto', query: '?type=none' },
{ html: 'weight-ranking.html', out: 'weight-ranking', mode: 'auto', query: '?type=hasact' },
{ html: 'weight-ranking.html', out: 'weight-ranking-personal', mode: 'auto', query: '?type=personal' },
{ html: 'weight-ranking.html', out: 'weight-ranking-none', mode: 'auto', query: '?type=none' },
// ========== 蓝牙设备 ==========
{ html: 'bluetooth-scale-v2.html', out: 'bluetooth-scale', mode: 'auto' },
{ html: 'bluetooth-scale-v2.html', out: 'bluetooth-scale-empty', mode: 'auto', eval: "document.getElementById('deviceListArea').classList.remove('show');document.getElementById('emptyState').style.display=''" },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members', mode: 'auto', query: '?device=s1' },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members-empty', mode: 'auto', query: '?device=b2' },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members-delete', mode: 'fixed', query: '?device=s1', eval: "document.querySelector('.action-btn--delete').click()" },
{ html: 'bluetooth-device-members-v2.html', out: 'bluetooth-device-members-sync', mode: 'fixed', query: '?device=s1', eval: 'syncMembers()', wait: 1600 },
{ html: 'bluetooth-search-v2.html', out: 'bluetooth-search', mode: 'auto' },
{ html: 'bluetooth-wifi-v2.html', out: 'bluetooth-wifi', mode: 'auto' },
];
(async () => {
const filters = process.argv.slice(2);
const runTasks = filters.length ? tasks.filter(t => filters.some(f => t.out.includes(f))) : tasks;
const browser = await chromium.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const context = await browser.newContext({ viewport: { width: 390, height: 4000 } });
for (const t of runTasks) {
const page = await context.newPage();
const filePath = path.join(root, t.html);
const url = `file:///${filePath.replace(/\\/g, '/')}${t.query || ''}`;
await page.goto(url, { waitUntil: 'networkidle' });
if (t.eval) {
await page.evaluate(t.eval).catch(e => console.log(`! eval failed [${t.out}]: ${e.message}`));
await page.waitForTimeout(t.wait || 300);
}
if (t.landscape) {
// 横屏页:phone-shell 固定 844×390,不展开、按元素原尺寸截取
await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (shell) {
shell.style.borderRadius = '0';
shell.style.boxShadow = 'none';
}
document.body.style.padding = '0';
document.body.style.margin = '0';
document.body.style.minHeight = 'auto';
document.body.style.background = '#fff';
document.documentElement.style.display = 'block';
});
await page.setViewportSize({ width: 844, height: 390 });
await page.waitForTimeout(200);
} else if (t.mode === 'fixed') {
await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (shell) {
shell.style.borderRadius = '0';
shell.style.boxShadow = 'none';
}
document.body.style.padding = '0';
document.body.style.minHeight = 'auto';
document.body.style.background = '#fff';
});
await page.setViewportSize({ width: 390, height: 844 });
await page.waitForTimeout(200);
} else {
await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (shell) {
shell.style.height = 'auto';
shell.style.minHeight = '844px';
shell.style.maxHeight = 'none';
shell.style.borderRadius = '0';
shell.style.boxShadow = 'none';
shell.style.overflow = 'visible';
shell.style.display = 'block';
}
document.querySelectorAll('.scroll-content, .page-content, .main-content, .list-wrap, .content').forEach(c => {
c.style.overflow = 'visible';
c.style.maxHeight = 'none';
c.style.height = 'auto';
c.style.flex = 'none';
});
document.body.style.padding = '0';
document.body.style.minHeight = 'auto';
document.body.style.background = '#fff';
});
await page.waitForTimeout(300);
const measured = await page.evaluate(() => {
const shell = document.querySelector('.phone-shell');
if (!shell) return document.body.scrollHeight;
return Math.ceil(shell.getBoundingClientRect().height);
});
const h = Math.max(measured, 844);
await page.setViewportSize({ width: 390, height: h });
await page.waitForTimeout(150);
}
const shell = await page.$('.phone-shell');
const target = shell || page;
await target.screenshot({
path: path.join(docDir, 'screenshots', `${t.out}.jpg`),
type: 'jpeg',
quality: 85,
});
console.log(`${t.out}.jpg [${t.mode}]`);
await page.close();
}
await browser.close();
console.log(`\n完成!共截取 ${runTasks.length}`);
})();