Files
jiu/todo/todo.html
T
wangjia 0ec609255c
Deploy / build-linux-web (push) Failing after 10m44s
Deploy / build-windows (push) Failing after 10m45s
Deploy / build-macos (push) Has been skipped
Deploy / build-android (push) Has been skipped
Deploy / build-ios (push) Has been skipped
Deploy / release-deploy (push) Has been skipped
chore: release v1.0.29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 12:12:26 +08:00

991 lines
44 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>酒库管理系统 — 项目 TODO</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0; padding: 0 0 80px;
font-family: "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #1f2933; background: #f7f9fb; line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
header {
background: linear-gradient(135deg, #0A1F3B 0%, #15407D 100%);
color: #fff; padding: 36px 32px 28px;
}
header h1 { margin: 0 0 6px; font-size: 24px; font-weight: 700; }
header .header-meta { color: #ADC9EA; font-size: 13px; margin-top: 4px; }
.stats { display: flex; gap: 16px; margin-top: 14px; flex-wrap: wrap; }
.stat-pill {
background: rgba(255,255,255,0.12); border-radius: 20px;
padding: 4px 14px; font-size: 13px; color: #fff;
}
.stat-pill strong { font-size: 18px; font-weight: 700; margin-right: 2px; }
/* ── 筛选栏 ── */
.filter-bar {
position: sticky; top: 0; z-index: 10;
background: #fff; border-bottom: 1px solid #e4e9ef;
padding: 10px 28px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
.filter-group { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.filter-label { font-size: 12px; color: #7b8794; white-space: nowrap; }
.filter-chip, .filter-btn {
padding: 3px 12px; border-radius: 14px; border: 1.5px solid #d1d9e6;
background: #f7f9fb; color: #52606d; font-size: 12.5px; cursor: pointer;
transition: all .15s; white-space: nowrap;
}
.filter-chip:hover, .filter-btn:hover { border-color: #2563AC; color: #2563AC; }
.filter-chip.active, .filter-btn.active {
background: #2563AC; border-color: #2563AC; color: #fff;
}
.filter-sep { width: 1px; height: 20px; background: #e4e9ef; margin: 0 4px; }
/* ── 内容区 ── */
.wrap { max-width: 980px; margin: 0 auto; padding: 0 24px; }
.section-block { margin-bottom: 8px; }
.section-title {
font-size: 15px; font-weight: 700;
padding: 10px 14px; border-radius: 8px;
display: flex; align-items: center; gap: 8px; cursor: pointer;
margin: 20px 0 8px; user-select: none;
}
.section-title .s-count {
font-size: 12px; font-weight: 500; padding: 1px 8px; border-radius: 10px;
background: rgba(0,0,0,0.08);
}
.section-title .s-arrow { margin-left: auto; font-size: 12px; color: inherit; opacity: .6; }
/* 状态主题色 */
.st-open { background: #eff6ff; color: #1e40af; border-left: 4px solid #3b82f6; }
.st-doing { background: #fff7ed; color: #9a3412; border-left: 4px solid #f97316; }
.st-done { background: #fefce8; color: #854d0e; border-left: 4px solid #eab308; }
.st-accepted{ background: #f0fdf4; color: #166534; border-left: 4px solid #22c55e; }
ul.todo-list { list-style: none; margin: 0; padding: 0; }
.todo-card {
background: #fff; border: 1px solid #e4e9ef; border-radius: 10px;
padding: 14px 18px; margin-bottom: 10px;
border-left-width: 4px;
}
/* 卡片左边框颜色与状态匹配 */
.todo-card.s-open { border-left-color: #3b82f6; }
.todo-card.s-doing { border-left-color: #f97316; }
.todo-card.s-done { border-left-color: #eab308; }
.todo-card.s-accepted { border-left-color: #22c55e; background: #f0fdf4; opacity: .8; }
.card-header { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; }
.item-title { font-weight: 600; font-size: 15px; flex: 1; }
.todo-card.s-accepted .item-title { text-decoration: line-through; color: #52606d; }
.card-badges { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.item-desc { font-size: 13.5px; color: #52606d; margin-top: 6px; }
.item-desc code {
background: #f0f2f5; padding: 1px 5px; border-radius: 3px;
font-family: "JetBrains Mono", monospace; font-size: 12.5px; color: #b91c1c;
}
.card-footer { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.tag-row { display: flex; flex-wrap: wrap; gap: 6px; flex: 1; }
.item-meta { display: flex; flex-wrap: wrap; gap: 8px; font-size: 12px; color: #8aa3c4; }
.meta-date { white-space: nowrap; }
/* ── 标签样式 ── */
.tag { display: inline-block; padding: 2px 9px; border-radius: 10px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.t-block { background: #fde8e8; color: #b91c1c; }
.t-high { background: #fef0d8; color: #9a6700; }
.t-low { background: #e6f0fb; color: #1d4ed8; }
.t-tag { background: #efefef; color: #555; cursor: pointer; transition: .12s; }
.t-tag:hover { background: #d9e8fb; color: #1d4ed8; }
.ver-badge { font-weight: 700; color: #1f7a44; }
/* 状态徽章 */
.status-badge { font-size: 11.5px; }
.s-open { background: #dbeafe; color: #1e40af; }
.s-doing { background: #ffedd5; color: #9a3412; }
.s-done { background: #fef9c3; color: #854d0e; }
.s-accepted { background: #dcfce7; color: #166534; }
/* ── 折叠 ── */
.section-list-wrap.collapsed { display: none; }
.empty-tip { font-size: 14px; color: #8aa3c4; margin: 8px 0 24px; }
.hidden { display: none !important; }
/* ── 拒绝按钮 & 拒绝原因 ── */
.reject-btn {
padding: 3px 10px; border-radius: 6px; border: 1.5px solid #dc2626;
background: #fff; color: #dc2626; font-size: 12px; font-weight: 600;
cursor: pointer; transition: all .15s; white-space: nowrap;
}
.reject-btn:hover { background: #dc2626; color: #fff; }
.reject-note {
margin: 8px 0 4px; padding: 7px 12px; border-radius: 6px;
background: #fef2f2; border: 1px solid #fecaca;
font-size: 13px; color: #b91c1c; line-height: 1.5;
}
.reject-date { color: #ef9999; font-size: 12px; }
/* ── 拒绝 Modal ── */
.modal-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.45);
display: flex; align-items: center; justify-content: center; z-index: 999;
}
.modal-box {
background: #fff; border-radius: 12px; padding: 28px 32px;
width: min(480px, 94vw); box-shadow: 0 20px 60px rgba(0,0,0,.2);
}
.modal-box h3 { margin: 0 0 4px; font-size: 17px; color: #1f2933; }
.modal-subtitle { font-size: 13px; color: #52606d; margin: 0 0 18px; }
.modal-label { font-size: 13px; font-weight: 600; color: #374151; margin-bottom: 6px; display: block; }
.modal-label .req { color: #dc2626; margin-left: 2px; }
.modal-textarea {
width: 100%; border: 1.5px solid #d1d9e6; border-radius: 8px;
padding: 10px 12px; font-size: 14px; font-family: inherit; resize: vertical;
min-height: 88px; outline: none; transition: border-color .15s;
}
.modal-textarea:focus { border-color: #dc2626; }
.modal-textarea.error { border-color: #dc2626; background: #fff8f8; }
.modal-err { font-size: 12px; color: #dc2626; margin-top: 4px; display: none; }
.modal-err.show { display: block; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
.modal-btn { padding: 8px 20px; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; transition: all .15s; }
.modal-btn-cancel { background: #f3f4f6; color: #374151; }
.modal-btn-cancel:hover { background: #e5e7eb; }
.modal-btn-reject { background: #dc2626; color: #fff; }
.modal-btn-reject:hover { background: #b91c1c; }
.modal-cmd-wrap { margin-top: 16px; padding: 12px 14px; background: #1e293b; border-radius: 8px; }
.modal-cmd-label { font-size: 12px; color: #94a3b8; margin-bottom: 8px; }
.modal-cmd-code { font-family: "JetBrains Mono", "Fira Code", monospace; font-size: 13px; color: #86efac; word-break: break-all; display: block; }
.modal-copy-btn { margin-top: 10px; padding: 5px 14px; border-radius: 6px; background: #334155; color: #e2e8f0; border: none; font-size: 12px; cursor: pointer; }
.modal-copy-btn:hover { background: #475569; }
</style>
</head>
<body>
<header>
<div class="wrap">
<h1>酒库管理系统 — 项目 TODO</h1>
<div class="header-meta">生成于 2026-06-09 · 真相源 todo/todo.json</div>
<div class="stats">
<div class="stat-pill"><strong>24</strong>全部</div>
<div class="stat-pill"><strong>6</strong>待开始</div>
<div class="stat-pill"><strong>0</strong>开发中</div>
<div class="stat-pill"><strong>4</strong>待验收</div>
<div class="stat-pill"><strong>14</strong>已验收</div>
</div>
</div>
</header>
<div class="filter-bar" id="filterBar">
<div class="filter-group">
<span class="filter-label">重要度</span>
<button class="filter-btn active" data-filter-level="all">全部</button>
<button class="filter-btn" data-filter-level="high">高优</button>
<button class="filter-btn" data-filter-level="mid">重要</button>
<button class="filter-btn" data-filter-level="low">一般</button>
</div>
<div class="filter-sep"></div>
<div class="filter-group">
<span class="filter-label">状态</span>
<button class="filter-btn active" data-filter-status="all">全部</button>
<button class="filter-btn" data-filter-status="open">待开始</button>
<button class="filter-btn" data-filter-status="doing">开发中</button>
<button class="filter-btn" data-filter-status="done">待验收</button>
<button class="filter-btn" data-filter-status="accepted">已验收</button>
</div>
<div class="filter-sep"></div>
<div class="filter-group">
<span class="filter-label">平台 / 标签</span>
<button class="filter-chip" data-filter-tag="Android">Android</button><button class="filter-chip" data-filter-tag="Web">Web</button><button class="filter-chip" data-filter-tag="Windows">Windows</button><button class="filter-chip" data-filter-tag="iOS">iOS</button><button class="filter-chip" data-filter-tag="mac">mac</button><button class="filter-chip" data-filter-tag="前端">前端</button><button class="filter-chip" data-filter-tag="后端">后端</button><button class="filter-chip" data-filter-tag="安全">安全</button><button class="filter-chip" data-filter-tag="数据库">数据库</button><button class="filter-chip" data-filter-tag="文档">文档</button>
</div>
</div>
<div class="wrap">
<div class="section-block" id="section-open">
<div class="section-title st-open" data-toggle="open">
📋 待开始 <span class="s-count">6</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-open">
<ul class="todo-list" id="list-open">
<li class="todo-card s-open"
data-id="2"
data-level="high"
data-status="open"
data-tags="Web">
<div class="card-header">
<span class="item-title">备案号为占位假值</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-block">高优 · 紧急</span>
</div>
</div>
<div class="item-desc">现为沪ICP备2026000000号(全0),上线前必须替换真实备案号。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Web">Web</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="20"
data-level="mid"
data-status="open"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">商品详情页重设计:展示所有字段,内容为公开页超集</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">当前 product_detail_screen.dart 显示内容过于简单。需先做页面设计(UI 方案),内容要求是公开扫码页的超集:包含商品基本信息、参数(产地/保质期/储存方式)、价格、描述文档、图片、库存批次、入库记录等管理侧专属字段。另:商品公开链接展示为可点击超链接(点击可跳转公开扫码页)。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="21"
data-level="mid"
data-status="open"
data-tags="前端,后端,安全,文档">
<div class="card-header">
<span class="item-title">授权信息功能方案设计</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">完整方案设计,涵盖:界面设计与交互、前端实现方案、后端接口与数据模型、安全策略(防盗用、防滥用、授权校验)等。需产出设计文档后再进入开发阶段。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="安全">安全</span> <span class="tag t-tag" data-tag="文档">文档</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="22"
data-level="low"
data-status="open"
data-tags="前端,Web,文档">
<div class="card-header">
<span class="item-title">关于我们页文档/更新日志链接更新,Web 新增版本历史页</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">1. 客户端「关于我们」页面的「打开文档」「更新日志」链接指向需更新为正确地址。2. Web 营销站新增版本更新页面(/changelog 或 /download/),从 CHANGELOG.md 自动渲染,展示所有历史版本更新记录。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="Web">Web</span> <span class="tag t-tag" data-tag="文档">文档</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="23"
data-level="low"
data-status="open"
data-tags="前端,Web">
<div class="card-header">
<span class="item-title">注册/联系入口「发邮件」升级为「加客服微信」并优化文案</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">当前注册成功页和网站联系支持入口引导用户「发邮件」,体验较弱。升级为扫码加客服微信,同时梳理相关文案措辞,使表达更专业、有亲和力。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="Web">Web</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="24"
data-level="low"
data-status="open"
data-tags="Web,文档">
<div class="card-header">
<span class="item-title">服务条款与隐私政策页面</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">在营销网站(jiu.51yanmei.com)补充服务条款(Terms of Service)和隐私政策(Privacy Policy)页面,注册页底部添加链接。最低优先级,待其他功能稳定后处理。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Web">Web</span> <span class="tag t-tag" data-tag="文档">文档</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="section-block" id="section-doing">
<div class="section-title st-doing" data-toggle="doing">
🔨 开发中 <span class="s-count">0</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-doing">
<ul class="todo-list" id="list-doing">
<p class="empty-tip">暂无条目</p>
</ul>
</div>
</div>
<div class="section-block" id="section-done">
<div class="section-title st-done" data-toggle="done">
🔍 待验收 <span class="s-count">4</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-done">
<ul class="todo-list" id="list-done">
<li class="todo-card s-done"
data-id="19"
data-level="high"
data-status="done"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">入库单新建表单增加产地/保质期/储存方式,选填项默认折叠</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<button class="reject-btn" data-id="19" data-title="入库单新建表单增加产地/保质期/储存方式,选填项默认折叠">拒绝验收</button>
</div>
</div>
<div class="item-desc">新建入库单的商品行增加产地、保质期、储存方式三个字典下拉选项(已有字典数据)。必填项(商品名、数量等)正常显示;选填项默认隐藏,点击展开按钮后在下一行展示。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="27"
data-level="mid"
data-status="done"
data-tags="Windows,mac">
<div class="card-header">
<span class="item-title">桌面客户端单实例限制,禁止同时运行多个进程</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<button class="reject-btn" data-id="27" data-title="桌面客户端单实例限制,禁止同时运行多个进程">拒绝验收</button>
</div>
</div>
<div class="item-desc">Windows 和 macOS 客户端启动时检测是否已有实例运行;若有,则激活已有窗口并退出新进程。Flutter desktop 可通过 window_single_instance 或 dart:io 锁文件实现。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Windows">Windows</span> <span class="tag t-tag" data-tag="mac">mac</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="25"
data-level="low"
data-status="done"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">移动端左侧抽屉菜单支持手势侧滑唤出</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-low">一般 / 优化</span>
<button class="reject-btn" data-id="25" data-title="移动端左侧抽屉菜单支持手势侧滑唤出">拒绝验收</button>
</div>
</div>
<div class="item-desc">窄屏(手机)模式下,导航 Drawer 当前需点击汉堡按钮打开,需支持从屏幕左边缘向右滑动手势唤出菜单,符合移动端操作习惯。涉及 app_shell.dart 的 Drawer 配置。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="26"
data-level="low"
data-status="done"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">入库单详情商品明细改为卡片式竖排布局</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-low">一般 / 优化</span>
<button class="reject-btn" data-id="26" data-title="入库单详情商品明细改为卡片式竖排布局">拒绝验收</button>
</div>
</div>
<div class="item-desc">当前入库单详情的商品明细以横向表格展示,字段多时在窄屏上挤压严重。改为每件商品独占一个卡片区块,字段竖向排列(商品名、规格、数量、单价、产地、保质期等各占一行),信息层次清晰,移动端阅读体验更好。宽屏可保持表格,窄屏自动切换为卡片流(复用 MobileListCard 模式)。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="section-block" id="section-accepted">
<div class="section-title st-accepted" data-toggle="accepted">
✅ 已验收 <span class="s-count">14</span>
<span class="s-arrow">▾ 展开</span>
</div>
<div class="section-list-wrap collapsed" id="list-wrap-accepted">
<ul class="todo-list" id="list-accepted">
<li class="todo-card s-accepted"
data-id="13"
data-level="low"
data-status="accepted"
data-tags="后端,iOS,Android">
<div class="card-header">
<span class="item-title">公开商品 API 补充 batch.quantity 和 product code/barcode</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">Flutter _AuthenticityCard 读取 batch['quantity'] 但后端 public.go batchData 未返回 quantity。另 product code/barcode 未暴露,参数卡无法显示商品编码。handler/public.go GetProduct</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.25</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="6"
data-level="low"
data-status="accepted"
data-tags="前端,Web">
<div class="card-header">
<span class="item-title">注册成功提示路径需核对</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">提示「忘记门店编码可在「系统设置→关于」查看」,需与实际客户端菜单路径一致。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="Web">Web</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.25</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="5"
data-level="low"
data-status="accepted"
data-tags="前端,Web">
<div class="card-header">
<span class="item-title">页脚双份维护</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">njk 站点用 footer.njk 读变量,功能页把页脚硬编码各写一份,改一处不同步。根因同「两套模板」。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="Web">Web</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.25</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="3"
data-level="low"
data-status="accepted"
data-tags="前端,Web">
<div class="card-header">
<span class="item-title">注册接口同源依赖问题</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">用 window.location.origin+'/api/v1/public/register',要求同源部署;分离部署时指错。需确认部署形态。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="Web">Web</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.25</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="18"
data-level="low"
data-status="accepted"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">基础数据管理 UI 增加 4 个字典维护 Tab</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">products_screen.dart 基础数据管理页增加产地/保质期/储存方式/描述文档 4 个维护 Tab,复用现有 option 维护组件(增删改查);描述文档 Tab 含 title+content 多行输入</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.24</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="14"
data-level="low"
data-status="accepted"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">Footer「商品报错」「意见反馈」补充实际功能</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">public_product_screen.dart _Footer 底部两个链接无 url/action,点击静默。需设计方案:跳转反馈表单、弹出联系方式、或发邮件。line:1353-1355</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.24</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="10"
data-level="mid"
data-status="accepted"
data-tags="前端,后端,iOS,Android">
<div class="card-header">
<span class="item-title">公开商品 API 补充 sale_price 并在页面展示价格</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">product model 有 sale_price 字段但 public.go GetProduct 未返回。需:1) 后端 batchData/productData 加 sale_price2) Flutter _TitleBlock 或单独区块展示建议零售价。client: public_product_screen.dart, backend: handler/public.go</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="9"
data-level="mid"
data-status="accepted"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">设计并实现添加门店微信功能</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">PublicProductScreen _ShopCard「添加门店微信」按钮 onPressed: () {} 为空。需设计方案:门店维护微信号字段 or 二维码图片,扫码页展示。涉及后端 shop model 扩展 + 前端展示。client/lib/screens/public/public_product_screen.dart:1255</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="8"
data-level="mid"
data-status="accepted"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">补全「查看本店其他商品」功能</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">PublicProductScreen _ShopCard 底部按钮 onPressed: () {} 为空,需实现跳转至该门店商品列表页。client/lib/screens/public/public_product_screen.dart:1240</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="17"
data-level="mid"
data-status="accepted"
data-tags="前端,iOS,Android">
<div class="card-header">
<span class="item-title">入库表单关联 4 个商品属性字典</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">stock_in_form_screen.dart 生产日期字段附近增加产地/保质期/储存方式/描述文档 4 个下拉选择(数据来自字典 List 接口);FindOrCreate/Update 接受并写入 4 个 id;前端新增对应 API client 方法</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="16"
data-level="mid"
data-status="accepted"
data-tags="后端,前端,iOS,Android">
<div class="card-header">
<span class="item-title">public.go 返回产地/保质期/储存/介绍,公开页去硬编码</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">public.go GetProduct Preload 4 张新表 + 三级回退(描述文档→旧Description→通用中性兜底)+ 默认值常量(保质期/储存方式);Flutter public_product_screen.dart _ParamsCard/_DescriptionSection 改读真实数据,移除茅台 mock</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="iOS">iOS</span> <span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="15"
data-level="mid"
data-status="accepted"
data-tags="后端,数据库">
<div class="card-header">
<span class="item-title">4 张商品属性字典表 + 后端 CRUD</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">新增 product_origin_options/product_shelf_life_options/product_storage_options/product_description_docs 4 张字典表(TenantBase+shop_id 隔离);Product 增加 4 个可空外键;AutoMigrate 注册;handler/product_attr.go CRUD16 个方法);router.go 追加 4 组路由在 /product-options/ 下</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="数据库">数据库</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="4"
data-level="low"
data-status="accepted"
data-tags="前端,Web">
<div class="card-header">
<span class="item-title">scan.html / features 走 passthrough 不享模板</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-low">一般 / 优化</span>
</div>
</div>
<div class="item-desc">features/scan.html 直接复制,不经 njk 模板,页脚/备案/CSS 都得手动同步。建议长期改造为模板化。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="Web">Web</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
<li class="todo-card s-accepted"
data-id="1"
data-level="mid"
data-status="accepted"
data-tags="前端,Web">
<div class="card-header">
<span class="item-title">两套 CSS / 设计令牌并存</span>
<div class="card-badges">
<span class="tag status-badge s-accepted">已验收</span>
<span class="tag t-high">重要</span>
</div>
</div>
<div class="item-desc">主站 njk 用 color.css+style.css,功能页用 colors_and_type.css,导航/页脚各写一份,维护分裂。建议合并到一套,功能页走 base.njk 模板。</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span> <span class="tag t-tag" data-tag="Web">Web</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-08</span>
<span class="meta-date">✅ 验收 2026-06-08 · <span class="ver-badge">v1.0.23</span></span>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<!-- 拒绝验收 Modal -->
<div id="reject-modal" class="modal-overlay" style="display:none">
<div class="modal-box">
<h3>拒绝验收</h3>
<p class="modal-subtitle" id="reject-modal-subtitle"></p>
<label class="modal-label">拒绝原因<span class="req">*</span></label>
<textarea class="modal-textarea" id="reject-reason-input" rows="3" placeholder="请说明具体问题,Claude 将以此为依据修复…"></textarea>
<div class="modal-err" id="reject-reason-err">请填写拒绝原因</div>
<div id="reject-cmd-wrap" class="modal-cmd-wrap" style="display:none">
<div class="modal-cmd-label">在终端运行以下命令:</div>
<code class="modal-cmd-code" id="reject-cmd-text"></code>
<button class="modal-copy-btn" id="reject-copy-btn">复制命令</button>
</div>
<div class="modal-actions">
<button class="modal-btn modal-btn-cancel" id="reject-cancel-btn">取消</button>
<button class="modal-btn modal-btn-reject" id="reject-confirm-btn">确认拒绝</button>
</div>
</div>
</div>
<script>
(function() {
let curLevel = 'all';
let curStatus = 'all';
let curTags = new Set();
function applyFilter() {
document.querySelectorAll('.todo-card').forEach(card => {
const lvl = card.dataset.level;
const status = card.dataset.status;
const tags = card.dataset.tags ? card.dataset.tags.split(',') : [];
let show = true;
if (curLevel !== 'all' && lvl !== curLevel) show = false;
if (curStatus !== 'all' && status !== curStatus) show = false;
if (curTags.size > 0 && ![...curTags].some(t => tags.includes(t))) show = false;
card.classList.toggle('hidden', !show);
});
// 筛选后自动展开包含结果的区域
if (curStatus !== 'all') {
const wrap = document.getElementById('list-wrap-' + curStatus);
if (wrap) {
wrap.classList.remove('collapsed');
const title = document.querySelector('[data-toggle="' + curStatus + '"] .s-arrow');
if (title) title.textContent = '▴ 收起';
}
}
}
// 重要度筛选
document.querySelectorAll('[data-filter-level]').forEach(btn => {
btn.addEventListener('click', () => {
curLevel = btn.dataset.filterLevel;
document.querySelectorAll('[data-filter-level]').forEach(b => b.classList.toggle('active', b === btn));
applyFilter();
});
});
// 状态筛选
document.querySelectorAll('[data-filter-status]').forEach(btn => {
btn.addEventListener('click', () => {
curStatus = btn.dataset.filterStatus;
document.querySelectorAll('[data-filter-status]').forEach(b => b.classList.toggle('active', b === btn));
applyFilter();
});
});
// 标签筛选(多选)
document.querySelectorAll('[data-filter-tag]').forEach(btn => {
btn.addEventListener('click', () => {
const t = btn.dataset.filterTag;
if (curTags.has(t)) { curTags.delete(t); btn.classList.remove('active'); }
else { curTags.add(t); btn.classList.add('active'); }
applyFilter();
});
});
// 卡片标签 → 快速筛选
document.querySelectorAll('.t-tag').forEach(tag => {
tag.addEventListener('click', e => {
e.stopPropagation();
const t = tag.dataset.tag;
const chip = document.querySelector('[data-filter-tag="'+t+'"]');
if (chip) chip.click();
});
});
// 区域折叠切换
document.querySelectorAll('.section-title[data-toggle]').forEach(title => {
title.addEventListener('click', () => {
const st = title.dataset.toggle;
const wrap = document.getElementById('list-wrap-' + st);
const arrow = title.querySelector('.s-arrow');
if (!wrap) return;
const collapsed = wrap.classList.toggle('collapsed');
if (arrow) arrow.textContent = collapsed ? '▾ 展开' : '▴ 收起';
});
});
// ── 拒绝验收 Modal ──
const modal = document.getElementById('reject-modal');
const reasonInput = document.getElementById('reject-reason-input');
const reasonErr = document.getElementById('reject-reason-err');
const cmdWrap = document.getElementById('reject-cmd-wrap');
const cmdText = document.getElementById('reject-cmd-text');
const copyBtn = document.getElementById('reject-copy-btn');
let rejectId = null;
document.querySelectorAll('.reject-btn').forEach(btn => {
btn.addEventListener('click', e => {
e.stopPropagation();
rejectId = btn.dataset.id;
document.getElementById('reject-modal-subtitle').textContent = '#' + rejectId + ' ' + btn.dataset.title;
reasonInput.value = '';
reasonInput.classList.remove('error');
reasonErr.classList.remove('show');
cmdWrap.style.display = 'none';
document.getElementById('reject-confirm-btn').style.display = '';
copyBtn.textContent = '复制命令';
modal.style.display = 'flex';
setTimeout(() => reasonInput.focus(), 80);
});
});
document.getElementById('reject-cancel-btn').addEventListener('click', () => {
modal.style.display = 'none';
});
modal.addEventListener('click', e => {
if (e.target === modal) modal.style.display = 'none';
});
document.getElementById('reject-confirm-btn').addEventListener('click', () => {
const reason = reasonInput.value.trim();
if (!reason) {
reasonInput.classList.add('error');
reasonErr.classList.add('show');
reasonInput.focus();
return;
}
const escaped = reason.replace(/\/g, '\\').replace(/"/g, '\"');
const cmd = 'node todo/todo.mjs reject ' + rejectId + ' --reason "' + escaped + '"';
cmdText.textContent = cmd;
cmdWrap.style.display = 'block';
document.getElementById('reject-confirm-btn').style.display = 'none';
});
reasonInput.addEventListener('input', () => {
if (reasonInput.value.trim()) {
reasonInput.classList.remove('error');
reasonErr.classList.remove('show');
}
});
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(cmdText.textContent).then(() => {
copyBtn.textContent = '已复制 ';
});
});
})();
</script>
</body>
</html>