docs(V0.2): 界面设计同步 2026-10-23 调整 (含日历+打卡合并草案) + 设计文档 §11
Docker Build and Push / build-image (push) Successful in 4m14s

界面设计.html
- 新增 2.12「V0.2.1 界面调整」章节: 头部统一 / 日历·打卡合并 / 模板分类 / 宏观 tab 溢出
- 已实现部分 (①③④) 与待确认草案 (②) 分别标注
- 2.9 宏观截图同步新 tab 顺序与右侧透支提示; 2.3 / 2.7 清单补「模板」分段
- 全部原型头部改用 23px 紧凑头部, 「我」页去掉头部条

设计文档.md
- 新增 §11 界面调整: 变更清单、头部统一对照表、模板接口与排序口径、宏观 tab 顺序与溢出实现、
  待确认的日历+打卡合并方案 (含确认后需改动的文件与样式段)
This commit is contained in:
cheney
2026-10-23 16:41:04 +08:00
parent c765755906
commit 072f304cc2
2 changed files with 717 additions and 23 deletions
+645 -23
View File
@@ -187,8 +187,94 @@
.empty .ico{width:64px;height:64px;margin:0 auto 12px;border-radius:50%;background:#f2f3f5;display:flex;align-items:center;justify-content:center;color:#c4c8ce;font-size:30px}
.empty p{font-size:13px;margin-bottom:12px}
/* ---------- 表格 ---------- */
table{width:100%;border-collapse:collapse;background:#fff;border:1px solid #e6e8eb;border-radius:10px;overflow:hidden;font-size:13px}
/* ---------- 统一主 Tab 头部(V0.2.1 调整:高度减半,标题主色小字) ---------- */
.navbar-sm{height:23px;margin-top:28px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
font-size:12px;font-weight:600;letter-spacing:1px;color:var(--primary);background:var(--bg)}
/* 「我」页:不设头部,头像区直接顶到状态栏下方 */
.me-head.flush{padding-top:24px}
/* ---------- 打卡 · 日历与打卡合并视图(V0.2.1 草案) ---------- */
.mv-cal{background:#fff;border-radius:10px;margin:8px 12px 0;padding:6px 6px 8px;box-shadow:0 1px 2px rgba(0,0,0,.04)}
.mv-month{display:flex;align-items:center;gap:8px;padding:2px 4px 6px}
.mv-month b{font-size:14px}
.mv-month .arrow{font-size:17px;color:var(--muted);line-height:1;padding:0 3px}
.mv-month .today{margin-left:auto;font-size:11px;color:var(--primary)}
.mv-grid{display:grid;grid-template-columns:repeat(7,1fr)}
.mv-wd span{text-align:center;font-size:10px;color:var(--muted);padding:1px 0 3px;display:block}
.mv-day{height:31px;margin:1px 0;display:flex;flex-direction:column;align-items:center;justify-content:center;
border-radius:6px;font-size:12px;line-height:1.15;color:var(--text)}
.mv-day.out{color:#c8c8c8}
.mv-day.future{color:#b0b0b0}
.mv-day.today{color:var(--primary);font-weight:700}
.mv-day.sel{background:var(--primary);color:#fff;font-weight:600}
.mv-day i{width:4px;height:4px;border-radius:50%;margin-top:2px}
.mv-day i.partial{background:var(--warn)} .mv-day i.full{background:var(--success)} .mv-day i.over{background:var(--over)}
.mv-day.sel i{background:#fff}
.mv-rate{display:flex;justify-content:space-between;align-items:center;font-size:11px;color:var(--muted);padding:7px 6px 0}
.mv-rate b{color:var(--primary)}
.mv-daybar{display:flex;align-items:center;gap:8px;padding:10px 14px 2px;font-size:13px;font-weight:600}
.mv-daybar .badge{font-weight:400}
.mv-daybar .sum{margin-left:auto;font-size:11px;color:var(--muted);font-weight:400}
.mv-groups{padding-bottom:12px}
.mv-ghead{display:flex;align-items:center;gap:6px;padding:9px 14px 5px;font-size:11px;font-weight:600;color:var(--muted)}
.mv-ghead::before{content:"";width:3px;height:11px;border-radius:2px;background:var(--primary)}
.mv-ghead .cnt{margin-left:auto;font-weight:400;color:var(--weak)}
.mv-card{position:relative;display:flex;align-items:center;gap:9px;background:#fff;border-radius:9px;
padding:8px 10px;margin:0 12px 7px;box-shadow:0 1px 2px rgba(0,0,0,.04)}
.mv-card.done{background:#f7fbf8}
.mv-ring{width:42px;height:42px;position:relative;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.mv-ring svg{display:block}
.mv-ring .c{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700}
.mv-ring .c em{font-style:normal;font-size:9px;color:var(--muted);font-weight:400}
.mv-ring .ov{position:absolute;top:-3px;right:-5px;font-size:9px;color:#fff;background:var(--orange);border-radius:999px;padding:0 4px}
.mv-main{flex:1;min-width:0}
.mv-name{font-size:14px;font-weight:600;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mv-meta{font-size:10px;color:var(--muted);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mv-meta .st{color:var(--orange);background:rgba(255,143,31,.12);border-radius:3px;padding:0 4px;margin-left:5px}
.mv-btn{font-size:11px;border-radius:999px;padding:4px 12px;background:var(--primary);color:#fff;flex-shrink:0;white-space:nowrap}
.mv-btn.grey{background:#f2f3f5;color:var(--muted)}
.mv-more{color:#c0c4cc;font-size:15px;line-height:1;padding:4px 2px 4px 6px;flex-shrink:0}
/* 操作面板(⋯ 触发) */
.mv-sheet .sheet-title{font-size:15px;margin-bottom:8px}
.mv-sheet-row{display:flex;align-items:center;justify-content:space-between;padding:12px 4px;border-top:1px solid var(--line);font-size:14px}
.mv-sheet-row.danger{color:var(--danger)}
.mv-sheet-row .k{font-size:11px;color:var(--weak)}
/* ---------- 清单 · 模板分类(V0.2.1 草案) ---------- */
.tpl-card{background:#fff;border-radius:10px;padding:11px 12px;margin:0 12px 10px;box-shadow:0 1px 2px rgba(0,0,0,.04)}
.tpl-row{display:flex;align-items:center;gap:7px}
.tpl-name{font-size:14px;font-weight:600}
.tpl-badge{font-size:10px;color:var(--muted);background:#f2f3f5;border-radius:4px;padding:1px 5px}
.tpl-num{margin-left:auto;font-size:11px;color:var(--muted)}
.tpl-chips{display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}
.tpl-chip{font-size:10px;color:var(--muted);background:#f7f8fa;border-radius:4px;padding:2px 6px}
.tpl-chip.more{color:var(--primary);background:#f0f5ff}
.tpl-acts{display:flex;gap:14px;margin-top:9px;padding-top:7px;border-top:1px dashed var(--line)}
/* 新建清单(无场景,模板从右侧滑出选择) */
.pick-row{display:flex;align-items:center;gap:8px;border:1px solid var(--line);border-radius:8px;padding:8px 10px;font-size:14px;color:var(--weak)}
.pick-row .meta{margin-left:auto;display:flex;align-items:center;gap:4px;font-size:11px;color:var(--muted)}
.side-panel{position:absolute;top:28px;right:0;bottom:0;width:78%;background:#fff;z-index:11;
box-shadow:-6px 0 18px rgba(15,23,42,.12);display:flex;flex-direction:column;padding:14px 0 0}
.side-head{display:flex;align-items:center;padding:0 16px 4px}
.side-head b{font-size:16px}
.side-head .x{margin-left:auto;font-size:20px;color:#c8c8c8;line-height:1}
.side-tip{font-size:11px;color:var(--muted);padding:0 16px 8px}
.side-item{display:flex;align-items:baseline;gap:8px;padding:11px 16px}
.side-item.on{background:#f0f5ff}
.side-item .nm{flex:1;font-size:14px;font-weight:600;display:flex;align-items:center;gap:6px}
.side-item.on .nm{color:var(--primary)}
.side-item .sub{font-size:11px;color:var(--muted)}
/* ---------- 宏观 · tab 溢出提示(V0.2.1 草案) ---------- */
.mtabs-wrap{position:relative;background:#fff;border-bottom:1px solid var(--line)}
.mtabs-wrap .mtabs{border-bottom:none}
.mtabs-fade{position:absolute;top:0;right:0;bottom:0;width:52px;display:flex;align-items:center;justify-content:flex-end;
padding-right:8px;background:linear-gradient(to right,rgba(255,255,255,0),#fff 55%,#fff)}
.mtabs-fade .hint{width:19px;height:19px;border-radius:50%;background:#fff;border:1px solid #e3e6ea;
display:flex;align-items:center;justify-content:center;color:var(--primary);font-size:13px;font-weight:700;
box-shadow:0 1px 4px rgba(0,0,0,.14)}
/* ---------- 表格 ---------- */ table{width:100%;border-collapse:collapse;background:#fff;border:1px solid #e6e8eb;border-radius:10px;overflow:hidden;font-size:13px}
th,td{border-bottom:1px solid var(--line);padding:9px 12px;text-align:left;vertical-align:middle}
th{background:#fafbfc;font-weight:600;color:#444;white-space:nowrap}
tr:last-child td{border-bottom:none}
@@ -244,7 +330,7 @@
<p>打卡 / 日历 / 清单 —— 移动端静态高保真原型(设计基线 420 × 820,适配 375~430px)</p>
<div class="meta">
<span>版本 V0.2(0.2.0)</span><span>分支 dev-0.2</span><span>antd-mobile 风格</span>
<span>底部 4 Tab(打卡默认)</span><span>时区 Asia/Shanghai</span><span>文档日期 2026-10-22</span>
<span>底部 4 Tab(打卡默认)</span><span>时区 Asia/Shanghai</span><span>文档日期 2026-10-22 · 修订 2026-10-23</span>
</div>
</div>
@@ -297,14 +383,14 @@
</div>
<!-- ============ 2 主界面 ============ -->
<h2>2 主界面<span class="sub">三个子视图(SwipeTabs 左右滑动切换)</span></h2>
<h2>2 主界面<span class="sub">三个子视图(V0.2 现状;合并方案见 2.12 ②)</span></h2>
<div class="stage">
<!-- ---------- 2.1 打卡视图 ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="habit-head">
<div>
@@ -423,7 +509,7 @@
<!-- ---------- 2.2 日历视图 ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px">
<div>打卡</div><div class="on">日历</div><div>清单</div>
@@ -525,13 +611,13 @@
<!-- ---------- 2.3 清单视图 ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px">
<div>打卡</div><div>日历</div><div class="on">清单</div>
</div>
<div class="list-head">
<div class="seg"><div class="on">进行中</div><div>已归档</div></div>
<div class="seg"><div class="on">进行中</div><div>已归档</div><div>模板</div></div>
<div class="mini-btn solid">+ 新建</div>
</div>
@@ -589,7 +675,7 @@
<!-- ---------- 2.4 新建打卡项弹窗 ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="habit-head">
<div>
@@ -654,7 +740,7 @@
<!-- ---------- 2.5 完成特效 ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="habit-head">
<div><div class="habit-date">10月22日 周四</div><div class="habit-summary">已完成 <strong>3</strong> / 4</div></div>
@@ -726,7 +812,7 @@
<!-- ---------- 2.6 日历·明天(计划) ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px"><div>打卡</div><div class="on">日历</div><div>清单</div></div>
<div class="cal-month-bar"><span class="cal-arrow">‹</span><span class="cal-month">2026年10月</span><span class="cal-arrow">›</span><span class="cal-today">回到今天</span></div>
@@ -792,11 +878,11 @@
<!-- ---------- 2.7 清单完成 + 空态 ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px"><div>打卡</div><div>日历</div><div class="on">清单</div></div>
<div class="list-head">
<div class="seg"><div class="on">进行中</div><div>已归档</div></div>
<div class="seg"><div class="on">进行中</div><div>已归档</div><div>模板</div></div>
<div class="mini-btn solid">+ 新建</div>
</div>
<div class="checklist-card done" style="position:relative;overflow:visible">
@@ -839,7 +925,7 @@
<!-- ---------- 2.8 空态与归档 ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">打卡</div>
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="habit-head">
<div><div class="habit-date">10月22日 周四</div><div class="habit-summary">已完成 <strong>0</strong> / 0</div></div>
@@ -871,11 +957,14 @@
<!-- ---------- 2.9 宏观(原首页报警/新闻并入) ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">宏观</div>
<div class="navbar-sm">宏观</div>
<div class="body">
<div class="mtabs">
<div>油价</div><div>经济数据</div><div>策略研究</div><div>经济文章</div>
<div>报警<span class="badge-dot">3</span></div><div class="on">新闻</div>
<div class="mtabs-wrap">
<div class="mtabs">
<div>报警<span class="badge-dot">3</span></div><div class="on">新闻</div>
<div>油价</div><div>经济数据</div><div>策略研究</div><div>经济文章</div>
</div>
<div class="mtabs-fade"><span class="hint">›</span></div>
</div>
<div class="feed">
<div class="feed-card">
@@ -913,16 +1002,15 @@
</div>
<div class="caption">
<b>2.9 宏观 · 原「首页」报警/新闻并入<span class="tag">设计变更</span></b>
<p>「首页」Tab 取消,报警与新闻成为宏观的第 5、6 个子 Tab(<code>/macro/alerts</code>、<code>/macro/news</code>),共 6 个子 Tab 可横向滚动,当前项底部有 3px 主色指示条;报警数量以红色角标挂在子 Tab 上。新闻详情改为 <code>/macro/news/:id</code>,底部「宏观」保持高亮。</p>
<p>「首页」Tab 取消,报警与新闻并入宏观。<b>2026-10-23 调整</b>:两者移到<b>最左</b>(报警 → 新闻 → 油价 → 经济数据 → 策略研究 → 经济文章),<b>默认选中「新闻」</b>;共 6 个子 Tab 可横向滚动,当前项底部有 3px 主色指示条;报警数量以红色角标挂在子 Tab 上。tab 条右侧增加<b>渐变遮罩 + 箭头</b>,提示还有未展示的 tab(详见 2.12 ④)。新闻详情为 <code>/macro/news/:id</code>,底部「宏观」保持高亮。</p>
</div>
</div>
<!-- ---------- 2.10 我的 · OTA(开发版本 关 = 默认) ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">我</div>
<div class="body">
<div class="me-head">
<div class="me-head flush">
<div class="me-avatar">C</div>
<div class="me-name">Cheney</div>
<div class="me-mail">qian.huang@sunyard.com</div>
@@ -964,9 +1052,8 @@
<!-- ---------- 2.11 我的 · OTA(开发版本 开) ---------- -->
<div class="shot">
<div class="phone">
<div class="navbar">我</div>
<div class="body">
<div class="me-head">
<div class="me-head flush">
<div class="me-avatar">C</div>
<div class="me-name">Cheney</div>
<div class="me-mail">qian.huang@sunyard.com</div>
@@ -1008,9 +1095,544 @@
</div>
<!-- ============ 2.12 V0.2.1 界面调整草案 ============ -->
<h2>2.12 V0.2.1 界面调整<span class="sub">头部统一 / 日历·打卡合并 / 模板分类 / 宏观 tab 溢出</span></h2>
<p class="desc">本节为 2026-10-23 提出的调整方案。其中 <b>①头部统一 / ③模板分类 / ④宏观 tab 溢出</b> 已按本节实现;
<b>②日历与打卡合并</b> 为待确认草案,<b>确认后再改代码</b>。</p>
<h3>① 四个主 Tab 头部统一</h3>
<table>
<tr><th>页</th><th>调整前</th><th>调整后</th></tr>
<tr><td>打卡</td><td>NavBar 45px,标题「打卡」17px 深色</td><td><b>23px</b>,标题「打卡」12px 主色加粗,字距 1px</td></tr>
<tr><td>宏观</td><td>NavBar 45px,<b>无标题</b>(空白)</td><td>23px + 标题「宏观」(同打卡规格)</td></tr>
<tr><td>监控</td><td>NavBar 45px,<b>无标题</b>(空白)</td><td>23px + 标题「监控」(同打卡规格)</td></tr>
<tr><td>我</td><td>NavBar 45px 空白条</td><td><b>整条去掉</b>,蓝色头像区直接顶到状态栏下方</td></tr>
</table>
<p class="note">实现为独立组件 <code>PageHeader</code>,只被打卡 / 宏观 / 监控 引用;「我」页不引用,因此三者互不影响。
头部背景取页面底色 <code>#f5f5f5</code>(不再是卡片白),视觉上更轻。</p>
<div class="stage">
<div class="shot">
<div class="phone">
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="habit-head">
<div><div class="habit-date">10月23日 周五</div><div class="habit-summary">已完成 <strong>2</strong> / 10</div></div>
<div class="head-right"><div class="mini-btn solid">+ 新建</div></div>
</div>
<div class="habit-list">
<div class="habit-card">
<div class="ring">
<svg width="58" height="58" viewBox="0 0 58 58">
<circle cx="29" cy="29" r="26" fill="none" stroke="#ececf0" stroke-width="6"/>
<circle cx="29" cy="29" r="26" fill="none" stroke="#1677ff" stroke-width="6" stroke-linecap="round" stroke-dasharray="163.4" stroke-dashoffset="81.7" transform="rotate(-90 29 29)"/>
</svg><div class="ring-center">1<em>/2</em></div>
</div>
<div class="habit-main"><div class="habit-name">喝水</div><div class="habit-meta">每天 2 次<span class="streak">连续 12</span></div><div class="habit-range">今日</div></div>
<div class="habit-actions"><div class="mini-btn solid">打卡</div></div>
<div class="habit-foot"><span class="link">撤销</span><span class="link">编辑</span><span class="link danger">归档</span></div>
</div>
<div class="habit-card">
<div class="ring">
<svg width="58" height="58" viewBox="0 0 58 58">
<circle cx="29" cy="29" r="26" fill="none" stroke="#ececf0" stroke-width="6"/>
<circle cx="29" cy="29" r="26" fill="none" stroke="#ff8f1f" stroke-width="6" stroke-linecap="round" stroke-dasharray="163.4" stroke-dashoffset="163.4" transform="rotate(-90 29 29)"/>
</svg><div class="ring-center">0<em>/1</em></div>
</div>
<div class="habit-main"><div class="habit-name">俯卧撑</div><div class="habit-meta">每周 1 次</div><div class="habit-range">10-19 ~ 10-25</div></div>
<div class="habit-actions"><div class="mini-btn solid">打卡</div></div>
<div class="habit-foot"><span class="link">编辑</span><span class="link danger">归档</span></div>
</div>
</div>
</div>
<div class="tabbar">
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>①-a 打卡(头部已缩至 23px)<span class="tag">已实现</span></b>
<p>原 45px 白色 NavBar 换成 23px 底色条 + 主色标题,页面上移,首屏多出一张卡片的高度。</p>
</div>
</div>
<div class="shot">
<div class="phone">
<div class="navbar-sm">宏观</div>
<div class="body">
<div class="mtabs-wrap">
<div class="mtabs">
<div>报警<span class="badge-dot">1</span></div><div class="on">新闻</div><div>油价</div>
<div>经济数据</div><div>策略研究</div><div>经济文章</div>
</div>
<div class="mtabs-fade"><span class="hint">›</span></div>
</div>
<div class="feed">
<div class="feed-card">
<div class="feed-title">央行开展 5000 亿元 MLF 操作,中标利率持平</div>
<div class="feed-sub">本月 MLF 净投放 2000 亿元,市场流动性保持合理充裕…</div>
<div class="feed-meta"><span>妙想财经</span><span>2026-10-22</span><span class="rk">#1</span></div>
</div>
<div class="feed-card">
<div class="feed-title">9 月社零同比增长 3.2%,服务消费延续回暖</div>
<div class="feed-sub">餐饮收入增速回升至 5.1%,可选消费分化明显…</div>
<div class="feed-meta"><span>妙想财经</span><span>2026-10-21</span><span class="rk">#2</span></div>
</div>
</div>
</div>
<div class="tabbar">
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>①-b + ④ 宏观:头部标题化 · 报警/新闻置最左 · 右侧溢出提示<span class="tag">已实现</span></b>
<p><b>顺序</b>:报警 → 新闻 → 油价 → 经济数据 → 策略研究 → 经济文章,<b>默认选中「新闻」</b>(<code>/macro</code> 自动跳 <code>/macro/news</code>)。<br>
<b>溢出提示</b>:tab 条仍可向右滚动时,右边缘出现白色渐变遮罩 + 主色圆底箭头「›」(1.5s 轻微左右摆动,可点击向右翻页),滚到末尾自动消失;切换 tab 时自动把激活项滚到可视区中间。<br>
antd-mobile Tabs 自带的右侧渐变遮罩只有 30px 且无箭头,用户难以察觉,故叠加此提示层。</p>
</div>
</div>
</div>
<h3>② 打卡:日历 + 打卡合并(<span style="color:var(--danger)">待确认草案</span>)</h3>
<p class="desc">把原来的「打卡」「日历」两个 tab 合成一个:<b>上半部日历,下半部打卡卡片</b>。取舍按下述规则:</p>
<table>
<tr><th>项</th><th>规则</th></tr>
<tr><td>页面结构</td><td>SwipeTabs 从 3 个减为 <b>2 个</b>:<b>日历(含打卡)</b> / <b>清单</b></td></tr>
<tr><td>上方日历</td><td>保留月历网格 + 月份左右切换 + 「回到今天」;<b>去掉</b>「昨天 / 今天 / 明天」分段控件(点日期即可,节省 38px 高度)</td></tr>
<tr><td>日历高度</td><td>日期格子 38px → <b>31px</b>,去掉 6 行补位中的空行;整块约 240px</td></tr>
<tr><td>下方打卡</td><td>沿用现有打卡卡片样式,但<b>紧凑化</b>:内边距 12px → 8px,环形 58px → <b>42px</b>(环宽 6 → 5),行高压缩,卡片间距 10 → 7px</td></tr>
<tr><td>卡片内容</td><td>保留环形进度(含达标对勾 / 超额 +n)、名称、周期与目标、连续天数、打卡按钮;<b>撤销 / 编辑 / 归档收进「⋯」</b>,减少一行高度</td></tr>
<tr><td>分组</td><td>按周期类型分组:<b>每天 → 每周 → 每月</b>,每组一个小标题(左侧主色竖条 + 组名 + 条数);<b>去掉「每年」分组</b>(已有年目标项并入「每月」区末尾的「其他周期」小标题,或建议直接隐藏年视图)</td></tr>
<tr><td>联动</td><td>点日历某日 → 下方卡片即该日数据(进度仍按该日所属周期算);未来日期只读,按钮变灰显示「计划」</td></tr>
<tr><td>空态</td><td>日历下方直接显示「该日没有打卡项」+「+ 新建」引导</td></tr>
</table>
<div class="stage">
<div class="shot">
<div class="phone">
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px">
<div class="on">日历</div><div>清单</div>
</div>
<div class="mv-cal">
<div class="mv-month">
<span class="arrow">‹</span><b>2026年10月</b><span class="arrow">›</span>
<span class="today">回到今天</span>
</div>
<div class="mv-grid mv-wd"><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span><span>日</span></div>
<div class="mv-grid">
<div class="mv-day out">29</div><div class="mv-day out">30</div>
<div class="mv-day">1<i class="full"></i></div><div class="mv-day">2<i class="full"></i></div>
<div class="mv-day">3<i class="partial"></i></div><div class="mv-day">4<i class="full"></i></div><div class="mv-day">5</div>
<div class="mv-day">6<i class="full"></i></div><div class="mv-day">7<i class="over"></i></div>
<div class="mv-day">8<i class="full"></i></div><div class="mv-day">9<i class="partial"></i></div>
<div class="mv-day">10<i class="full"></i></div><div class="mv-day">11<i class="full"></i></div><div class="mv-day">12</div>
<div class="mv-day">13</div><div class="mv-day">14<i class="full"></i></div><div class="mv-day">15<i class="full"></i></div>
<div class="mv-day">16<i class="partial"></i></div><div class="mv-day">17<i class="full"></i></div><div class="mv-day">18<i class="full"></i></div><div class="mv-day">19<i class="full"></i></div>
<div class="mv-day">20<i class="full"></i></div><div class="mv-day">21<i class="full"></i></div>
<div class="mv-day today sel">22<i class="partial"></i></div><div class="mv-day future">23</div><div class="mv-day future">24</div><div class="mv-day future">25</div>
<div class="mv-day future">26</div><div class="mv-day future">27</div><div class="mv-day future">28</div>
<div class="mv-day future">29</div><div class="mv-day future">30</div><div class="mv-day future">31</div><div class="mv-day out">1</div>
</div>
<div class="mv-rate"><span>本月完成率 <b>82%</b></span><span>今天 · 2026-10-22</span></div>
</div>
<div class="mv-daybar">今天<span class="badge">已完成 2 / 4</span><span class="sum">点环即打卡</span></div>
<div class="mv-groups">
<div class="mv-ghead">每天<span class="cnt">2 项</span></div>
<div class="mv-card">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#1677ff" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="58.1" transform="rotate(-90 21 21)"/>
</svg>
<div class="c">1<em>/2</em></div>
</div>
<div class="mv-main">
<div class="mv-name">喝水</div>
<div class="mv-meta">每天 2 次 · 今日 1/2<span class="st">连续 12</span></div>
</div>
<div class="mv-btn">打卡</div>
<div class="mv-more">⋯</div>
</div>
<div class="mv-card done">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#a855f7" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="0" transform="rotate(-90 21 21)"/>
</svg>
<div class="c"><svg width="15" height="15" viewBox="0 0 24 24" fill="none"><path d="M4 12.5l5 5L20 6.5" stroke="#a855f7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<span class="ov">+1</span>
</div>
<div class="mv-main">
<div class="mv-name">吃药</div>
<div class="mv-meta">每天 1 次 · 今日 2/1<span class="st">连续 30</span></div>
</div>
<div class="mv-btn grey">再打一次</div>
<div class="mv-more">⋯</div>
</div>
<div class="mv-ghead">每周<span class="cnt">1 项</span></div>
<div class="mv-card">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ff8f1f" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="77.5" transform="rotate(-90 21 21)"/>
</svg>
<div class="c">1<em>/3</em></div>
</div>
<div class="mv-main">
<div class="mv-name">健身</div>
<div class="mv-meta">每周 3 次 · 本周 1/3<span class="st">连续 5</span></div>
</div>
<div class="mv-btn">打卡</div>
<div class="mv-more">⋯</div>
</div>
<div class="mv-ghead">每月<span class="cnt">1 项</span></div>
<div class="mv-card">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#00b578" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="116.2" transform="rotate(-90 21 21)"/>
</svg>
<div class="c">0<em>/12</em></div>
</div>
<div class="mv-main">
<div class="mv-name">读书</div>
<div class="mv-meta">每月 12 次 · 本月 0/12</div>
</div>
<div class="mv-btn">打卡</div>
<div class="mv-more">⋯</div>
</div>
</div>
</div>
<div class="tabbar">
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>②-a 合并后的主视图(日历在上 · 打卡在下,按天 / 周 / 月分组)<span class="tag" style="background:#fff1f0;color:#ff3141">待确认</span></b>
<p>卡片整体压缩:环形 42px、内边距 8px、行距 7px,单卡高度约 58px(原 92px),一屏可看 4~5 项。分组顺序固定为 <b>每天 → 每周 → 每月</b>,
组标题左侧 3px 主色竖条;<b>无「每年」分组</b>。日历顶部的「昨天 / 今天 / 明天」分段已去掉,直接点日期切换,选中日靠「今天」主色数字与蓝色实心块识别。</p>
</div>
</div>
<div class="shot">
<div class="phone">
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px"><div class="on">日历</div><div>清单</div></div>
<div class="mv-cal">
<div class="mv-month"><span class="arrow">‹</span><b>2026年10月</b><span class="arrow">›</span><span class="today">回到今天</span></div>
<div class="mv-grid mv-wd"><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span><span>日</span></div>
<div class="mv-grid">
<div class="mv-day out">29</div><div class="mv-day out">30</div>
<div class="mv-day">1<i class="full"></i></div><div class="mv-day">2<i class="full"></i></div>
<div class="mv-day">3<i class="partial"></i></div><div class="mv-day">4<i class="full"></i></div><div class="mv-day">5</div>
<div class="mv-day">6<i class="full"></i></div><div class="mv-day">7<i class="over"></i></div>
<div class="mv-day">8<i class="full"></i></div><div class="mv-day">9<i class="partial"></i></div>
<div class="mv-day">10<i class="full"></i></div><div class="mv-day">11<i class="full"></i></div><div class="mv-day">12</div>
<div class="mv-day">13</div><div class="mv-day">14<i class="full"></i></div><div class="mv-day">15<i class="full"></i></div>
<div class="mv-day">16<i class="partial"></i></div><div class="mv-day">17<i class="full"></i></div><div class="mv-day">18<i class="full"></i></div><div class="mv-day">19<i class="full"></i></div>
<div class="mv-day">20<i class="full"></i></div><div class="mv-day">21<i class="full"></i></div>
<div class="mv-day today">22<i class="partial"></i></div><div class="mv-day future sel">23</div><div class="mv-day future">24</div><div class="mv-day future">25</div>
<div class="mv-day future">26</div><div class="mv-day future">27</div><div class="mv-day future">28</div>
<div class="mv-day future">29</div><div class="mv-day future">30</div><div class="mv-day future">31</div><div class="mv-day out">1</div>
</div>
<div class="mv-rate"><span>本月完成率 <b>82%</b></span><span>明天 · 2026-10-23</span></div>
</div>
<div class="mv-daybar">明天<span class="badge">计划(不可打卡)</span></div>
<div class="mv-groups">
<div class="mv-ghead">每天<span class="cnt">2 项</span></div>
<div class="mv-card">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#1677ff" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="116.2" transform="rotate(-90 21 21)"/>
</svg><div class="c">0<em>/2</em></div>
</div>
<div class="mv-main"><div class="mv-name">喝水</div><div class="mv-meta">每天 2 次 · 当日 0/2</div></div>
<div class="mv-btn grey">计划</div><div class="mv-more">⋯</div>
</div>
<div class="mv-card">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#a855f7" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="116.2" transform="rotate(-90 21 21)"/>
</svg><div class="c">0<em>/1</em></div>
</div>
<div class="mv-main"><div class="mv-name">吃药</div><div class="mv-meta">每天 1 次 · 当日 0/1</div></div>
<div class="mv-btn grey">计划</div><div class="mv-more">⋯</div>
</div>
<div class="mv-ghead">每周<span class="cnt">1 项</span></div>
<div class="mv-card">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ff8f1f" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="77.5" transform="rotate(-90 21 21)"/>
</svg><div class="c">1<em>/3</em></div>
</div>
<div class="mv-main"><div class="mv-name">健身</div><div class="mv-meta">每周 3 次 · 本周 1/3</div></div>
<div class="mv-btn grey">计划</div><div class="mv-more">⋯</div>
</div>
</div>
</div>
<div class="tabbar">
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>②-b 未来日期(计划态)</b>
<p>选中未来日期时,打卡按钮统一变灰显示「计划」,点击不生效;环形仍显示该日所属周期的既有进度(此处举例为周期初)。
补打窗口仍是最近 7 天,超出时同样只读。</p>
</div>
</div>
<div class="shot">
<div class="phone">
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px"><div class="on">日历</div><div>清单</div></div>
<div class="mv-cal">
<div class="mv-month"><span class="arrow">‹</span><b>2026年10月</b><span class="arrow">›</span><span class="today">回到今天</span></div>
<div class="mv-grid mv-wd"><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span><span>日</span></div>
<div class="mv-grid">
<div class="mv-day out">29</div><div class="mv-day out">30</div>
<div class="mv-day">1<i class="full"></i></div><div class="mv-day">2<i class="full"></i></div>
<div class="mv-day">3<i class="partial"></i></div><div class="mv-day">4<i class="full"></i></div><div class="mv-day">5</div>
<div class="mv-day">6<i class="full"></i></div><div class="mv-day">7<i class="over"></i></div>
<div class="mv-day">8<i class="full"></i></div><div class="mv-day">9<i class="partial"></i></div>
<div class="mv-day">10<i class="full"></i></div><div class="mv-day">11<i class="full"></i></div><div class="mv-day">12</div>
<div class="mv-day">13</div><div class="mv-day">14<i class="full"></i></div><div class="mv-day">15<i class="full"></i></div>
<div class="mv-day">16<i class="partial"></i></div><div class="mv-day">17<i class="full"></i></div><div class="mv-day">18<i class="full"></i></div><div class="mv-day">19<i class="full"></i></div>
<div class="mv-day">20<i class="full"></i></div><div class="mv-day">21<i class="full"></i></div>
<div class="mv-day today">22<i class="partial"></i></div><div class="mv-day future">23</div><div class="mv-day future">24</div><div class="mv-day future">25</div>
<div class="mv-day future">26</div><div class="mv-day future">27</div><div class="mv-day future">28</div>
<div class="mv-day future">29</div><div class="mv-day future">30</div><div class="mv-day future">31</div><div class="mv-day out">1</div>
</div>
<div class="mv-rate"><span>本月完成率 <b>82%</b></span><span>今天 · 2026-10-22</span></div>
</div>
<div class="mv-daybar">今天<span class="badge">已完成 2 / 4</span><span class="sum">点环即打卡</span></div>
<div class="mv-groups">
<div class="mv-ghead">每天<span class="cnt">2 项</span></div>
<div class="mv-card">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#1677ff" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="58.1" transform="rotate(-90 21 21)"/>
</svg><div class="c">1<em>/2</em></div>
</div>
<div class="mv-main"><div class="mv-name">喝水</div><div class="mv-meta">每天 2 次 · 今日 1/2</div></div>
<div class="mv-btn">打卡</div><div class="mv-more">⋯</div>
</div>
<div class="mv-card done">
<div class="mv-ring">
<svg width="42" height="42" viewBox="0 0 42 42">
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#ececf0" stroke-width="5"/>
<circle cx="21" cy="21" r="18.5" fill="none" stroke="#a855f7" stroke-width="5" stroke-linecap="round" stroke-dasharray="116.2" stroke-dashoffset="0" transform="rotate(-90 21 21)"/>
</svg>
<div class="c"><svg width="15" height="15" viewBox="0 0 24 24" fill="none"><path d="M4 12.5l5 5L20 6.5" stroke="#a855f7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<span class="ov">+1</span>
</div>
<div class="mv-main"><div class="mv-name">吃药</div><div class="mv-meta">每天 1 次 · 今日 2/1</div></div>
<div class="mv-btn grey">再打一次</div><div class="mv-more">⋯</div>
</div>
</div>
</div>
<div class="mask" style="inset:28px 0 0 0"></div>
<div class="sheet mv-sheet">
<div class="sheet-title">吃药 · 每天 1 次</div>
<div class="mv-sheet-row"><span>撤销上次打卡</span><span class="k">今日 2 次</span></div>
<div class="mv-sheet-row"><span>编辑打卡项</span><span class="k">名称 / 周期 / 目标</span></div>
<div class="mv-sheet-row danger"><span>归档</span><span class="k">历史记录保留</span></div>
<div class="btn-ghost" style="margin-top:10px">取消</div>
</div>
<div class="tabbar">
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>②-c 卡片「⋯」操作面板</b>
<p>紧凑化后卡片不再常显三个文字链接,改由右上角「⋯」唤出底部面板:撤销 / 编辑 / 归档。面板项 44px 高,
「归档」用危险色;撤销仅在当日有记录时可用,否则置灰。</p>
</div>
</div>
</div>
<h3>③ 清单:新增「模板」分类(与进行中 / 已归档并列)<span class="tag">已实现</span></h3>
<p class="desc">清单页分段从 2 个变为 <b>3 个</b>:<b>进行中 / 已归档 / 模板</b>。模板拥有独立的增删改查,<b>按名称字母顺序</b>(中文按拼音、数字按数值)排序;
新建清单时<b>只保留名称</b>,<b>去掉场景</b>,模板改为点击后在<b>右侧滑出</b>的面板里选择。</p>
<div class="stage">
<div class="shot">
<div class="phone">
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px"><div>日历</div><div class="on">清单</div></div>
<div class="list-head">
<div class="seg"><div>进行中</div><div>已归档</div><div class="on">模板</div></div>
<div class="mini-btn solid">+ 新建模板</div>
</div>
<div class="tpl-card">
<div class="tpl-row"><span class="tpl-name">出差</span><span class="tpl-badge">预置</span><span class="tpl-num">11 项</span></div>
<div class="tpl-chips">
<span class="tpl-chip">身份证 / 证件</span><span class="tpl-chip">手机充电器 + 充电宝</span>
<span class="tpl-chip">笔记本电脑 + 电源</span><span class="tpl-chip">耳机</span><span class="tpl-chip more">+7</span>
</div>
<div class="tpl-acts"><span class="link">由此新建</span></div>
</div>
<div class="tpl-card">
<div class="tpl-row"><span class="tpl-name">回老家</span><span class="tpl-badge">预置</span><span class="tpl-num">12 项</span></div>
<div class="tpl-chips">
<span class="tpl-chip">身份证 / 证件</span><span class="tpl-chip">钥匙</span>
<span class="tpl-chip">手机充电器 + 充电宝</span><span class="tpl-chip">换洗衣物</span><span class="tpl-chip more">+8</span>
</div>
<div class="tpl-acts"><span class="link">由此新建</span></div>
</div>
<div class="tpl-card">
<div class="tpl-row"><span class="tpl-name">露营装备</span><span class="tpl-num">2 项</span></div>
<div class="tpl-chips"><span class="tpl-chip">帐篷</span><span class="tpl-chip">睡袋</span></div>
<div class="tpl-acts"><span class="link">由此新建</span><span class="link">编辑</span><span class="link danger">删除</span></div>
</div>
</div>
<div class="tabbar">
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>③-a 模板分类<span class="tag">已实现</span></b>
<p>模板卡片:名称 + 「预置」标记 + 项数;下方最多展示 4 个检查项摘要(超出折叠为 <code>+n</code>);底部操作「由此新建 / 编辑 / 删除」。
<b>系统预置模板不可编辑、不可删除</b>(只保留「由此新建」)。右上角按钮随分段切换:模板页为「+ 新建模板」,其余为「+ 新建」。</p>
</div>
</div>
<div class="shot">
<div class="phone">
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px"><div>日历</div><div class="on">清单</div></div>
<div class="mask" style="inset:28px 0 0 0"></div>
<div class="sheet">
<div class="sheet-title">新建检查清单</div>
<div class="field"><label>名称</label><div class="inp">周末露营</div></div>
<div class="field">
<label>模板</label>
<div class="pick-row">露营装备<span class="meta">2 项 <span style="color:#c8c8c8;font-size:15px">›</span></span></div>
</div>
<div class="btn-primary">创建</div>
<div class="btn-ghost">取消</div>
</div>
<div class="side-panel">
<div class="side-head"><b>选择模板</b><span class="x">×</span></div>
<div class="side-tip">按名称字母顺序排列</div>
<div class="side-item"><span class="nm">不使用模板</span><span class="sub">空白清单</span></div>
<div class="side-item"><span class="nm">出差<span class="tpl-badge">预置</span></span><span class="sub">11 项</span></div>
<div class="side-item"><span class="nm">回老家<span class="tpl-badge">预置</span></span><span class="sub">12 项</span></div>
<div class="side-item on"><span class="nm">露营装备</span><span class="sub">2 项</span></div>
</div>
</div>
<div class="tabbar">
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>③-b 新建清单 + 模板右侧滑出面板<span class="tag">已实现</span></b>
<p>表单只剩两项:<b>名称</b>(≤20 字,必填)与 <b>模板</b>(可空)。<b>场景选择器已移除</b>——场景不再由用户填写,新建时沿用所选模板自带的场景(无模板则为空),历史数据仍正常展示场景标签。<br>
点模板行从<b>右侧滑出 78% 宽面板</b>:首项固定为「不使用模板」,其后按字母顺序列出全部模板(预置带标记、右侧显示项数),选中项主色高亮,点遮罩或 × 关闭。</p>
</div>
</div>
<div class="shot">
<div class="phone">
<div class="navbar-sm">打卡</div>
<div class="body">
<div class="seg" style="width:calc(100% - 28px);margin-left:14px"><div>日历</div><div class="on">清单</div></div>
<div class="mask" style="inset:28px 0 0 0"></div>
<div class="sheet">
<div class="sheet-title">编辑模板</div>
<div class="field"><label>模板名称</label><div class="inp">露营装备</div></div>
<div class="field">
<label>检查项(3)</label>
<div style="display:flex;flex-direction:column;gap:6px">
<div style="display:flex;align-items:center;gap:8px"><span style="width:16px;text-align:right;font-size:11px;color:#999">1</span><div class="inp" style="flex:1">帐篷</div><span style="color:#ccc;font-size:17px">×</span></div>
<div style="display:flex;align-items:center;gap:8px"><span style="width:16px;text-align:right;font-size:11px;color:#999">2</span><div class="inp" style="flex:1">睡袋</div><span style="color:#ccc;font-size:17px">×</span></div>
<div style="display:flex;align-items:center;gap:8px"><span style="width:16px;text-align:right;font-size:11px;color:#999">3</span><div class="inp ph" style="flex:1">检查项内容</div><span style="color:#ccc;font-size:17px">×</span></div>
</div>
<div style="margin-top:8px;border:1px solid var(--line);border-radius:7px;text-align:center;padding:6px 0;font-size:12px;color:var(--muted)">+ 添加检查项</div>
</div>
<div class="btn-primary">保存</div>
<div class="btn-ghost">取消</div>
</div>
</div>
<div class="tabbar">
<div class="tab on"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8.5"/><path d="M8 12.4l2.6 2.6L16.5 9.5" stroke-linecap="round" stroke-linejoin="round"/></svg>打卡</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19V9m5 10V5m5 14v-7m5 7V8"/></svg>宏观</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="7" rx="2"/><rect x="3" y="13" width="18" height="7" rx="2"/><circle cx="7" cy="7.5" r="1"/><circle cx="7" cy="16.5" r="1"/></svg>监控</div>
<div class="tab"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="8" r="3.6"/><path d="M4.5 20c1.4-3.6 4.1-5.4 7.5-5.4S18.1 16.4 19.5 20"/></svg>我</div>
</div>
</div>
<div class="caption">
<b>③-c 模板编辑(增删改)<span class="tag">已实现</span></b>
<p>新建 / 编辑共用同一弹层:模板名称(≤20 字)+ 检查项列表(逐条 ≤40 字,可删除、可追加)。保存时至少需 1 条有效检查项,名称为空或全空则拦截并 Toast 提示。
删除模板需二次确认,且不影响已用该模板创建过的清单。</p>
</div>
</div>
</div>
<h3>④ 补充:模板接口</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td>GET</td><td><code>/api/checklists/templates</code></td><td>模板列表(含预置),<b>服务端按名称字母顺序排序</b></td></tr>
<tr><td>GET</td><td><code>/api/checklists/templates/:id</code></td><td>模板详情</td></tr>
<tr><td>POST</td><td><code>/api/checklists/templates</code></td><td>新建模板 <code>{ name, items[] }</code></td></tr>
<tr><td>PUT</td><td><code>/api/checklists/templates/:id</code></td><td>改名称 / 检查项(预置模板拒绝)</td></tr>
<tr><td>DELETE</td><td><code>/api/checklists/templates/:id</code></td><td>删自建模板(预置模板拒绝)</td></tr>
</table>
<p class="note">排序口径:<code>Intl.Collator('zh-Hans-CN', { numeric: true, sensitivity: 'base' })</code>,中文按拼音、数字按数值比较,前后端一致。</p>
<!-- ============ 3 组件状态 ============ -->
<h2>3 组件状态定义<span class="sub">Component States</span></h2>
<h3>3.0 页面头部(PageHeader)</h3>
<table>
<tr><th>项</th><th>取值</th></tr>
<tr><td>高度</td><td>23px(原 NavBar 45px 的一半),<code>flex: 0 0 23px</code> 不参与压缩</td></tr>
<tr><td>标题字号 / 字重</td><td>12px / 600</td></tr>
<tr><td>标题颜色</td><td>主色 <code>#1677ff</code></td></tr>
<tr><td>背景</td><td>页面底色 <code>#f5f5f5</code></td></tr>
<tr><td>适用范围</td><td>打卡 / 宏观 / 监控;<b>「我」页不使用</b></td></tr>
</table>
<p class="note">实现为独立组件 <code>src/components/PageHeader.jsx</code>,三页各自引用、互不影响;「我」页移除 NavBar 后头像区直接贴顶。</p>
<h3>3.1 环形进度(尺寸 58,环宽 6,r=26,周长 163.4)</h3>
<table>
<tr><th>状态</th><th>条件</th><th>环形</th><th>中心</th><th>卡片底</th><th>按钮</th></tr>
+72
View File
@@ -442,3 +442,75 @@ compareVersion('0.2','0.2.0') // → 0 (缺位补 0)
### 10.5 界面设计同步
`界面设计.html` 已同步:底部 Tab 改为 4 项且「打卡」高亮在首位、打卡页去掉特效开关按钮、新增「我的 · OTA 开发版本开关」屏、宏观新增「报警 / 新闻」子 Tab 与合并后的 6 Tab 导航。
---
## 11. 界面调整(2026-10-23)
### 11.1 变更清单
| # | 范围 | 变更 | 状态 |
| --- | --- | --- | --- |
| 1 | 页面头部 | 打卡 NavBar 高度减半(45 → 23px)、标题改主色 12px;宏观 / 监控补标题;「我」页整条去掉 | **已实现** |
| 2 | 打卡页 | 日历与打卡合并为一个视图(上日历、下打卡,按天/周/月分组、去掉年分组、卡片紧凑化) | **待确认**(设计稿已出,见《界面设计.html》2.12 ②节) |
| 3 | 清单页 | 新增「模板」分类(与进行中 / 已归档并列),独立 CRUD + 字母序;新建清单去掉场景、模板改右侧滑出选择 | **已实现** |
| 4 | 宏观页 | 报警 / 新闻移到最左并默认选中新闻;tab 条增加右侧溢出提示 | **已实现** |
### 11.2 页面头部统一
新增组件 `src/components/PageHeader.jsx`(高度 23px、标题 12px/600 主色、字距 1px、背景取页面底色 `#f5f5f5`),只被 **打卡 / 宏观 / 监控** 三页引用:
| 页 | 变更前 | 变更后 |
| --- | --- | --- |
| 打卡 | `NavBar` 45px,标题 17px 深色 | `PageHeader` 23px,标题主色 |
| 宏观 | `NavBar` 45px,空白 | `PageHeader` + 标题「宏观」 |
| 监控 | `NavBar` 45px,空白 | `PageHeader` + 标题「监控」 |
| 我 | `NavBar` 45px 空白条 | **移除**,`me-profile` 直接贴顶 |
> 「我」页不引用该组件,因此调整打卡/宏观/监控的头部不会波及「我」;反之亦然。三个页面之间也各自持有独立实例,互不影响。
### 11.3 清单模板分类
**接口**(`server/routes/checklists.js`):
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/api/checklists/templates` | 列表,**服务端按名称字母顺序排序**(原为 `builtin desc, id asc`) |
| GET | `/api/checklists/templates/:id` | 详情(新增) |
| POST | `/api/checklists/templates` | 新建(名称 ≤20 字,≥1 条检查项) |
| PUT | `/api/checklists/templates/:id` | 改名称 / 检查项(新增;`builtin=true` 拒绝,404) |
| DELETE | `/api/checklists/templates/:id` | 删自建模板(`builtin=true` 拒绝) |
排序口径:`Intl.Collator('zh-Hans-CN', { numeric: true, sensitivity: 'base' })` —— 中文按拼音、数字按数值。前端 `ListView` 拿到列表后再排一次,保证与后端一致(网络返回顺序不可控时也不乱)。
**前端**(`src/pages/checkin/ListView.jsx`):
- 分段由 2 项扩为 3 项:`active`(进行中)/ `archived`(已归档)/ `template`(模板);右上角按钮随分段切换为「+ 新建」/「+ 新建模板」。
- 模板卡片:名称 + 「预置」标记 + 项数;最多展示 4 条检查项摘要,超出折叠为 `+n`;操作「由此新建 / 编辑 / 删除」,预置模板只保留「由此新建」。
- 新建 / 编辑模板共用底部弹层:名称 + 检查项逐条编辑(≤40 字,可删可加)。
- **新建清单表单**:仅「名称」+「模板」两项 —— **场景选择器已移除**(`scene` 改为沿用所选模板自带的场景,无模板则为空;数据库字段保留,历史数据照常展示)。
- **模板选择**:点击模板行从**右侧滑出** `78vw` 面板(antd-mobile `Popup position="right"`),首项固定「不使用模板」,其后为字母序模板列表(预置带标记 + 项数),选中项主色高亮。
### 11.4 宏观 tab 顺序与溢出提示
- Tab 顺序改为 **报警 → 新闻 → 油价 → 经济数据 → 策略研究 → 经济文章**,默认选中 **新闻**;`/macro` 由路由直接 `Navigate` 到 `/macro/news`(`App.jsx`),`activeFromPath` 兜底同样返回 `news`。
- `SwipeTabs` 增加溢出提示层:测量 `.adm-tabs-tab-list` 的 `scrollLeft + clientWidth < scrollWidth`,为真时在顶部右侧渲染 `.tab-more-hint`(白色渐变 + 主色圆底箭头「›」,1.5s 轻微摆动动画,点击向右翻页 60% 视宽),滚到末尾自动消失。
- 切换 tab 时把激活项平滑滚到可视区中央,避免选中项藏在屏幕外。
- 为什么不用框架自带的:antd-mobile Tabs 自带 `.adm-tabs-header-mask-right` 只有 30px 纯渐变、无箭头、无交互,用户难以判断"右侧还有内容",故叠加此提示层。
### 11.5 待确认项:日历与打卡合并
方案已写入《界面设计.html》2.12 ②节(含主视图、未来日期计划态、卡片「⋯」操作面板三屏)。要点:
| 项 | 方案 |
| --- | --- |
| 结构 | SwipeTabs 由 3 个减为 2 个:`日历(含打卡)` / `清单` |
| 日历 | 月历 + 月份切换 + 回到今天;**去掉「昨天/今天/明天」分段**;日期格 38 → 31px |
| 打卡 | 沿用现有卡片样式,紧凑化:内边距 12→8px、环形 58→42px(环宽 6→5)、间距 10→7px |
| 操作 | 撤销 / 编辑 / 归档收进「⋯」底部面板(原为卡片底部三个文字链接) |
| 分组 | **每天 → 每周 → 每月**,组标题左侧主色竖条;**去掉「每年」分组** |
| 联动 | 点日期即切换当日数据;未来日期只读,按钮灰显「计划」 |
确认后再落地:`CheckinPage` 收敛为 2 个 tab、`CalendarView` 与 `HabitView` 合成新视图组件、`HabitView` 的循环/编辑逻辑抽出复用、样式新增 `.mv-*` 段。