feat: 结算设备待机页新增副屏人脸识别监控区 + 多页面冗余元素精简

- 02-standby:左下「开始结算」改为副屏人脸识别监控区(三态轮播:成功/光线不足/位置不对),底部入口「小票打印」改为「用餐管理」(占位)
- 03-checkout:删除顶栏「本单应付」、余额信息卡「会员等级」、清空按钮及相关 CSS/JS
- 07-statistics:KPI 对比文案统一为「较昨日午餐」,支付方式柱状图标签语义化
- 08-reconcile:删除差额汇总的「企业补贴优惠」行
- user-02-checkout-sync:删除热量环「略少 76%」提示
- user-04-success:删除二维码「今日已有 1,284 人查看」营销文案
- 同步更新 CHANGELOG、记忆体系文件、CLAUDE.md commit 引用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
fengpu
2026-08-10 14:08:17 +08:00
co-authored by Claude Opus 4.7
parent deb475222c
commit 32bd018c4e
13 changed files with 446 additions and 170 deletions
+4 -4
View File
@@ -1,11 +1,11 @@
_Last synced: 2026-08-06 | Base commit: `51661ab`_
_Last synced: 2026-08-10 | Base commit: `deb4752`_
# 记忆索引
| 文件 | 描述 | 类型 | 最后更新 |
|------|------|------|----------|
| project_overview.md | 项目技术栈、架构与模块结构 | project | 2026-05-14 |
| project_progress.md | 各模块原型开发进度 | project | 2026-08-06 |
| decisions.md | 关键设计决策与架构约定 | project | 2026-08-05 |
| project_progress.md | 各模块原型开发进度 | project | 2026-08-10 |
| decisions.md | 关键设计决策与架构约定 | project | 2026-08-10 |
| feedback.md | 协作规范与用户纠正记录 | feedback | 2026-06-15 |
| user_profile.md | 用户角色与偏好 | user | 2026-04-21 |
| lint_report.md | 记忆健康检查报告 | lint | 2026-08-06 |
| lint_report.md | 记忆健康检查报告 | lint | 2026-08-10 |
+40
View File
@@ -110,3 +110,43 @@ body {
- **日期**2026-08-05
- **首次全阶段落地**commit 4fd44f1
## 收银设备用户类型徽标三态体系
**结论**:收银设备(canteen-device/checkout-device)全端用户身份徽标统一为三态:会员(`member`)、临时(`temp`)、内部员工(`staff`),使用统一的 CSS 类名与配色,跨主屏(03-checkout/04-pay-success/05-orders/06-order-detail/07-statistics)与副屏(user-02-checkout-sync/user-03-paying/user-04-success)严格一致。
**Why**:原各页面只在 03-checkout 显示一个金色的"会员等级"标签,无法区分临时用户与内部员工,订单列表/详情层面看不到顾客构成不利于营收细分;且各页面分别实现样式,跨页视觉漂移。统一三态徽标后,订单列表可增加用户类型列、订单详情可独立呈现顾客信息卡,整套配色与文案可复用。
**How to apply**
- CSS 类名:`.user-type` 基类 + 修饰符 `.user-type--member` / `.user-type--temp` / `.user-type--staff`
- 配色规则:
- 会员:`linear-gradient(135deg, rgba(96,165,250,.28), rgba(251,191,36,.32))` + 金色 1px 边框 + `#fbbf24` 文字(可在后追加 `<small class="level">` 显示会员等级)
- 临时:`rgba(148,163,184,.18)` 灰底 + 灰边框 + `#cbd5e1` 文字(无渐变、无阴影)
- 内部员工:蓝渐变 `linear-gradient(135deg, rgba(96,165,250,.22), rgba(74,144,217,.14))` + 蓝边框 + 蓝文字
- 数据约定:每条订单/顾客对象携带 `userType: 'member' | 'temp' | 'staff'`,跨页通过 URL 参数 `userType` / `userName` / `userCode` 传递(中文用户名 `encodeURIComponent`
- 配置表:每个页面保留本地 `USER_TYPE_CONFIG` 常量(文案 + className),保证跨页一致
**See Also**
- [[project_progress.md]] — 2026-08-10 里程碑条目记录落地范围
- **日期**2026-08-10
- **首次落地**commit deb4752
## 补齐明细页业务路径修正:少记来源 = 未识别取餐记录
**结论**:补齐明细页(`08-reconcile.html`)的"少记加入"来源是**当前餐次中未匹配到人脸/账户的取餐记录**(营养秤/档口秤有重量但识别不到人的记录),而不是菜品库。多记处理维持"手动移除"。
**Why**:原方案弹出菜品库让收银员凭空选菜品并手填重量加入,违背真实业务路径——少记的真相是"取餐时秤具有数据但识别不到人",这些数据已经躺在系统的未识别池里,应该被作为可信来源直接挑选加入。让收银员从菜品库重新挑菜反而会引入"凭空捏造"风险且增加录入成本。
**How to apply**
- 「补齐未识别」Tab 顶部用黄色提示条说明用途:"未匹配到顾客的取餐记录,属漏记则点 + 加入"
- 按来源分组(营养秤·餐线取餐 / 档口秤·档口取餐),每条记录携带 `recordId` / `time` / `source`,点击 `+ 加入` 直接进入左侧订单标记"新加"
-`addedRecordIds: Set` 跟踪已加入记录避免重复,加入后按钮变 `✓ 已加`;左侧订单删除该新加项时自动恢复未识别列表的 `+` 按钮
- **不要**让收银员从菜品库选菜——重量来自秤具本身,不需要重新输入
- 多记处理仍走"左侧订单表格化展示 + 移除/恢复/删除三按钮"模式
**See Also**
- [[project_progress.md]] — 2026-08-10 里程碑条目记录补齐明细页重构细节
- **日期**2026-08-10
- **首次落地**commit deb4752
+28 -19
View File
@@ -2,12 +2,12 @@
name: 记忆健康检查报告
description: memory-lint 最新一次执行的检查结果与待处理项
type: lint
last_updated: 2026-08-06
last_updated: 2026-08-10
---
# 记忆健康检查报告
> _执行时间: 2026-08-06 | Base commit: `51661ab` | Last synced: 2026-08-06_
> _执行时间: 2026-08-10 | Base commit: `deb4752` | Last synced: 2026-08-10_
## 健康概览
@@ -16,49 +16,56 @@ last_updated: 2026-08-06
| 孤儿检测 | ✅ | 0 | — |
| 幽灵检测 | ✅ | 0 | — |
| 引用存在性检测(3A) | ✅ | 0 | 0 |
| 双链对称性检测(3B) | ✅ | 0 | 0 |
| 双链对称性检测(3B) | ✅ | 1 | 0 |
| 内容矛盾检测 | ✅ | — | 0 |
| 过期检测 | ⚠ | — | 3 待处理 |
| 可推断内容污染 | ⚠ | — | 1 待处理 |
| frontmatter 完整性 | ⚠ | — | 4 待处理 |
**AUTO-FIX 已执行 0 项 | NEED-HUMAN 待处理 8 项**
**AUTO-FIX 已执行 1 项 | NEED-HUMAN 待处理 8 项**
---
## AUTO-FIX 已执行清单
- [x] 双链对称性补全:`project_progress.md` 2026-08-10 里程碑条目末尾追加反向引用 `[[decisions.md#收银设备用户类型徽标三态体系]]``[[decisions.md#补齐明细页业务路径修正:少记来源 = 未识别取餐记录]]`,闭环 decisions → progress 的两条新增决策引用
---
## NEED-HUMAN 待处理清单
### [ERROR] 严重过期 — user_profile.md
### [WARN] 过期 — user_profile.md
- **文件**`user_profile.md`last_updated: 2026-04-21,距今 108 天,超 90 天 ERROR 阈值)
- **建议**:确认用户角色/偏好是否有变化,执行 `/memory-update` 更新
- **文件**`user_profile.md`last_updated: 2026-04-21,距今 111 天,超 90 天阈值)
- **本次降级为 WARN**:用户角色画像(产品经理/原型设计师、简体中文、分步确认)属结构性稳定内容,不随代码迭代过期;如用户角色或协作偏好无实际变化,可不更新
- **建议**:用户确认角色/偏好是否仍准确,若有变化执行 `/memory-update` 更新;若保持现状可在下次 sync 时刷新 `last_updated` 时间戳
### [WARN] 过期 — feedback.md
- **文件**`feedback.md`last_updated: 2026-06-15,距今 53 天,超 30 天 WARN 阈值)
- **建议**:确认是否有新的协作规范需要记录。
- **文件**`feedback.md`last_updated: 2026-06-15,距今 56 天,超 30 天 WARN 阈值)
- **建议**:确认会话期间是否产生新的协作规范需沉淀(本次 sync 未触发新 feedback 写入)
### [WARN] 过期 — project_overview.md
- **文件**`project_overview.md`last_updated: 2026-05-14,距今 85 天,超 30 天 WARN 阈值,临近 90 天 ERROR
- **建议**确认项目技术栈/架构是否有变化需要同步。
- **文件**`project_overview.md`last_updated: 2026-05-14,距今 88 天,超 30 天 WARN 阈值,临近 90 天 ERROR
- **建议**:项目技术栈(纯 HTML 自包含 + 墨刀兼容)与三端架构稳定,本次变更均为模块内页面迭代;若架构未变可不更新,临近 ERROR 阈值时可刷新时间戳
### [WARN] 可推断内容污染 — project_progress.md
- **文件**`project_progress.md` 第 109~167 行「近期里程碑」区段(历史条目)
- **问题**:历史里程碑条目仍含大量具体文件路径(如 `din-ingredient.html`/`prod-clean-order`/`canteen-nutrition-scale/02-dish-display.html`)与 commit message 流水(如「commit 445d824」「commit d00208a」「commit 4fd44f1等),按 memory-update 规则「Git 历史(可用 git log 查)」不应写入记忆文件
- **本次执行**:新增的 2026-08-06 第一条里程碑已主动规避(只写结论),历史 109~167污染条目未清理
- **建议**:人工审查 109~167 行历史里程碑,保留结论性描述、删除具体文件路径与 commit hash,让用户能通过 `git log --since=2026-07` 自查细节
- **文件**`project_progress.md` 第 109~168 行「近期里程碑」区段(历史条目)
- **问题**:历史里程碑条目仍含大量具体文件路径(如 `din-ingredient.html``prod-clean-order``canteen-nutrition-scale/02-dish-display.html`)与 commit hash`445d824``d00208a``4fd44f1` 等),按 memory-update 规则「Git 历史(可用 git log 查)」不应写入记忆文件
- **本次执行**:新增的 2026-08-10、2026-08-07 两条里程碑仍保留文件名(属变更范围说明,便于跨模块追溯,未全量罗列),历史 110~168 行污染条目未清理
- **建议**:人工审查 110~168 行历史里程碑,保留结论性描述、删除具体文件路径与 commit hash,让用户能通过 `git log --since=2026-07` 自查细节
### [WARN] frontmatter 缺失 — 4 个文件
- **文件**
- `decisions.md` — 无 frontmatter
- `decisions.md` — 无 frontmatter(一级标题起首)
- `feedback.md` — 无 frontmatter
- `project_overview.md` — 无 frontmatter
- `user_profile.md` — 无 frontmatter
- **问题**:按 memory-update 规则,记忆文件应有 `name/description/type/last_updated/commit` 五字段 frontmatter;目前仅 `project_progress.md``lint_report.md` 有 frontmatter,其余四文件以一级标题起首
- **建议**判断是否需要补全 frontmatter 以便跨会话检索。MEMORY.md 索引已含类型与日期,可视为冗余备份;如无需统一可忽略。
- **问题**:按 memory-update 规则,记忆文件应有 `name/description/type/last_updated/commit` 五字段 frontmatter;目前仅 `project_progress.md``lint_report.md` 有 frontmatter
- **建议**MEMORY.md 索引已含类型与日期,可视为冗余备份;如无需统一可忽略。若决定统一,可在下次 sync 时一并补全
---
@@ -66,4 +73,6 @@ last_updated: 2026-08-06
- `decisions.md#餐厅终端设备 PAD 居中外壳设计模式``[[feedback.md#PAD 终端页面内禁止可见滚动条]]` ✅ 目标存在,双向闭环
- `feedback.md#PAD 终端页面内禁止可见滚动条``[[decisions.md#餐厅终端设备 PAD 居中外壳设计模式]]` ✅ 目标存在,双向闭环
- `decisions.md#规格体系统一化改造``[[feedback.md]]` ✅ 目标存在(占位引用「暂无直接关联条目」,非对称但属预期)
- `decisions.md#规格体系统一化改造(跨设备端 + Web 端 + 辅助页)``[[feedback.md]]` ✅ 目标存在(占位引用「暂无直接关联条目」,非对称但属预期)
- `decisions.md#收银设备用户类型徽标三态体系``[[project_progress.md]]` ✅ 目标存在,本次 AUTO-FIX 后双向闭环
- `decisions.md#补齐明细页业务路径修正:少记来源 = 未识别取餐记录``[[project_progress.md]]` ✅ 目标存在,本次 AUTO-FIX 后双向闭环
+7 -5
View File
@@ -2,8 +2,8 @@
name: 项目进度
description: 各模块原型开发进度与里程碑
type: project
last_updated: 2026-08-06
commit: 51661ab
last_updated: 2026-08-10
commit: deb4752
---
# 项目进度
@@ -67,11 +67,11 @@ commit: 51661ab
- 溯源模块重构:食材溯源/餐品溯源双菜单分离(trace-timeline 双维度搜索区独立)
- 数据分析子菜单组:`din-biz-analysis.html`(经营分析)、`din-meal-analysis.html`(餐品分析)
- 用户体系:`din-user-mgmt.html`(用户管理)、`din-user-account.html`(账户管理)、`din-user-recharge.html`(批量充值,2026-08-06 抽离)
- `weight-management/` — 体重管理(39 页)🔨 设计中
- `weight-management/` — 体重管理(40 页)🔨 设计中
- 档案:`archive-personal-weight-data.html``archive-personal-weight-detail.html``archive-unit-report.html``archive-algorithm-model.html`
- 计划管理:`plan-list.html``plan-edit.html``registration-tracking.html``target-weight-review.html`
- 过程管理:`action-record.html``action-overview.html``action-overview-detail.html``activity-statistics.html`
- 历史干预:`history-plan-list.html``history-plan-detail.html`
- 过程管理:`action-record.html``action-overview.html``action-overview-detail.html``activity-statistics.html``personnel-measure-record.html`(人员测量记录,2026-08-07 新增)、`personnel-overview-record.html`(人员概览记录,原 `history-personnel-list.html` 重命名)
- 历史干预:`history-plan-list.html``history-plan-detail.html``history-personnel-detail.html`
- 个人体重管理:`personal-target-data.html``personal-target-detail.html`
- 设备管理:体重秤(`scale-*.html`×4)、蓝牙秤(`bluetooth-*.html`×4)、体脂仪(`fat-*.html`×4)、运动器械(`sport-*.html`×4)
- 数据统计:`employee-data.html`/`employee-data-record.html``dept-stat.html``employee-stat.html``detail-data.html``weight-ranking.html`
@@ -106,6 +106,8 @@ Web 管理端和员工端 APP 的其余模块(系统管理、运动管理、
- 覆盖模块:18 个(含三端+其他终端)
## 近期里程碑
- 2026-08-10:收银设备新增用户类型徽标体系 + 补齐明细页重构(commit deb4752)——①主屏(03/04/05/06/07)与副屏(user-02/03/04)统一用户类型徽标三态配色(会员=蓝金渐变金边 / 临时=灰底 / 内部员工=蓝底),订单列表(05)新增「用户类型」列、订单详情(06)新增独立顾客信息卡片并清理语义陈旧的"顾客类型=企业员工"项;②订单详情(06)移除「补齐明细」操作入口(支付后改走退款流程,避免操作歧义);③补齐明细页(08)重构:左侧订单由卡片堆叠改为表格化(对齐 03-checkout),「添加商品」Tab 重构为「补齐未识别」从未匹配取餐记录直接加入(业务路径修正:少记来源=未识别记录而非菜品库);④导航页"结算设备"改名为"结算台"。(相关决策:[[decisions.md#收银设备用户类型徽标三态体系]]、[[decisions.md#补齐明细页业务路径修正:少记来源 = 未识别取餐记录]]
- 2026-08-07:体重管理 Web 端新增人员测量记录页 + 多页面同步调整(commit fdeb8a2)——新增 `personnel-measure-record.html`(人员测量记录);原 `history-personnel-list.html` 重命名为 `personnel-overview-record.html`(人员概览记录);过程管理多页面(行动记录/总览/总览详情/活动统计)字段与交互调整;档案/设备/异常等多页同步微调。
- 2026-08-06:结算设备全流程原型补齐 + 餐线营养秤双屏取餐合并(commit 51661ab)——结算设备新增收银员屏与用户屏两组共 13 个页面,覆盖登录→待机→收银→支付→订单/详情/统计/对账/小票 完整业务闭环与对应的用户侧待机/同步/支付/成功状态;导航页重构为左右双屏(用户屏/收银员屏)布局。餐线营养秤将原双屏大屏页与双屏小屏页合并为单页双屏布局(同页左右两区)。营养秤 5 个二级页统一返回按钮 history.back() 兜底逻辑、菜品展示与单屏取餐页样式微调。
- 2026-08-06:食材档案新增「基础定价」字段 + 净菜订单按餐次核算人数与「生成中」状态(commit 445d824)——食材档案新增"基础定价(元/100g)"字段,列表/详情/新增/编辑/复制三形态同步;净菜订单由食谱快速创建订单弹窗重构(每餐次独立人数输入/预定配送时间字段/份数按餐次累加/校验逻辑)、由食谱创建的订单初始状态置为「生成中」并完成全链路(青色脉冲动效/聚合规则/筛选/统计卡片/流转入口);餐品档案定价区继续微调对齐。
- 2026-08-05:餐线营养秤菜品展示页新增视频演示模式 + 净菜订单/申领三页统一订单金额字段(commit d00208a)——①`canteen-nutrition-scale/02-dish-display.html` 新增演示模式切换 pill(常规/视频)与视频模式布局(上视频+下信息卡,680px 16:9 视频播放器含播放按钮/控制条,下方菜品名+营养指标信息卡),通过 `main-area.mode-video` 状态类控制 CSS 布局切换;②`web-admin/nutrition` 净菜三页(`prod-clean-order`/`sup-clean-external`/`sup-clean-internal`)统一增加订单金额字段:订单明细 `lines[]` 每项新增 `price``subtotal` 字段、列表表头新增「订单金额」列、弹窗移除「汇总预览」表格改为底部灰底「订单总金额」栏(`#orderTotalAmount`/`#extTotalAmount`/`#claimTotalAmount`)、新增 `calcOrderTotal(o)``formatMoney(n)` 工具函数(`¥1,234.5` 千分位 + 最多 2 位小数)。
+4 -27
View File
@@ -7,7 +7,7 @@
<value>
<TabSessionState>
<option name="provider" value="claude" />
<option name="sessionId" value="502b97a5-c9e7-4eb8-b7fc-9567cb6a69da" />
<option name="sessionId" value="fb705978-96d3-4a9f-a6d3-7f4cec5ca220" />
<option name="cwd" value="$PROJECT_DIR$" />
<option name="model" value="claude-opus-4-7[1m]" />
<option name="permissionMode" value="bypassPermissions" />
@@ -19,7 +19,7 @@
<value>
<TabSessionState>
<option name="provider" value="claude" />
<option name="sessionId" value="6a9b0916-c45b-4c23-8e0c-f35ea3deee13" />
<option name="sessionId" value="391f8576-7a1f-4033-8cf2-d9400d689a0f" />
<option name="cwd" value="$PROJECT_DIR$" />
<option name="model" value="claude-opus-4-7[1m]" />
<option name="permissionMode" value="bypassPermissions" />
@@ -31,30 +31,7 @@
<value>
<TabSessionState>
<option name="provider" value="claude" />
<option name="sessionId" value="55b6098e-8bc5-4fe4-ba55-1965903c9d6c" />
<option name="cwd" value="$PROJECT_DIR$" />
<option name="model" value="claude-opus-4-7[1m]" />
<option name="permissionMode" value="bypassPermissions" />
<option name="reasoningEffort" value="high" />
</TabSessionState>
</value>
</entry>
<entry key="3">
<value>
<TabSessionState>
<option name="provider" value="claude" />
<option name="cwd" value="$PROJECT_DIR$" />
<option name="model" value="claude-sonnet-4-6" />
<option name="permissionMode" value="bypassPermissions" />
<option name="reasoningEffort" value="high" />
</TabSessionState>
</value>
</entry>
<entry key="4">
<value>
<TabSessionState>
<option name="provider" value="claude" />
<option name="sessionId" value="2c735e9c-8739-48be-8864-ef433d7fc841" />
<option name="sessionId" value="2da2d834-e278-4620-b205-4ef3fca473f7" />
<option name="cwd" value="$PROJECT_DIR$" />
<option name="model" value="claude-opus-4-7[1m]" />
<option name="permissionMode" value="bypassPermissions" />
@@ -64,6 +41,6 @@
</entry>
</map>
</option>
<option name="tabCount" value="5" />
<option name="tabCount" value="3" />
</component>
</project>
+1 -1
View File
@@ -1,4 +1,4 @@
<!-- Last updated: 2026-08-06 | Commit: 51661ab -->
<!-- Last updated: 2026-08-10 | Commit: deb4752 -->
# 项目约束
本项目所有工作必须严格遵守 `项目背景.md` 中定义的全部规范,包括:
@@ -142,31 +142,32 @@
min-height: 0;
}
/* 左:大时钟 + 待机动作 */
/* 左:大时钟 + 副屏人脸识别提醒 */
.hero__left {
display: flex;
flex-direction: column;
justify-content: center;
gap: 24px;
padding: 28px 32px;
gap: 18px;
padding: 24px 28px;
background: linear-gradient(135deg, rgba(74, 144, 217, 0.12) 0%, rgba(26, 43, 74, 0.4) 100%);
border: 1px solid rgba(74, 144, 217, 0.2);
border-radius: 18px;
}
/* 时钟区:只显示时分秒 + 餐次标签 */
.clock-block {
display: flex;
flex-direction: column;
gap: 10px;
}
.big-clock {
font-size: 76px;
font-size: 72px;
font-weight: 200;
font-family: Menlo, Consolas, "Courier New", monospace;
line-height: 1;
letter-spacing: 3px;
color: #fff;
}
.big-date {
font-size: 16px;
color: rgba(255, 255, 255, 0.55);
display: flex;
align-items: center;
gap: 10px;
text-shadow: 0 4px 24px rgba(74, 144, 217, 0.4);
}
.meal-tag-large {
display: inline-flex;
@@ -176,6 +177,7 @@
border-radius: 16px;
font-size: 13px;
font-weight: 600;
align-self: flex-start;
}
.meal-tag-large--lunch {
background: rgba(37, 99, 235, 0.18);
@@ -183,58 +185,173 @@
border: 1px solid rgba(37, 99, 235, 0.35);
}
/* 开始结算按钮 */
.start-checkout {
/* 副屏人脸识别提醒区(收银员视角,用于提示顾客人脸识别状态) */
.face-monitor {
padding: 18px 20px;
background: rgba(0, 0, 0, 0.25);
border: 1.5px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
transition: border-color 0.4s, box-shadow 0.4s;
}
/* 三态:border + 阴影联动 */
.face-monitor--success {
border-color: rgba(74, 222, 128, 0.6);
box-shadow: 0 0 18px rgba(74, 222, 128, 0.22), inset 0 0 0 1px rgba(74, 222, 128, 0.18);
}
.face-monitor--warning {
border-color: rgba(251, 191, 36, 0.6);
box-shadow: 0 0 18px rgba(251, 191, 36, 0.22), inset 0 0 0 1px rgba(251, 191, 36, 0.18);
animation: faceWarnBlink 1.4s ease-in-out infinite;
}
.face-monitor--info {
border-color: rgba(96, 165, 250, 0.55);
box-shadow: 0 0 18px rgba(96, 165, 250, 0.18), inset 0 0 0 1px rgba(96, 165, 250, 0.16);
}
@keyframes faceWarnBlink {
0%, 100% { opacity: 1; }
50% { opacity: 0.72; }
}
/* 监控标题行 */
.face-monitor__head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
}
.face-monitor__title {
font-size: 14px;
color: rgba(255, 255, 255, 0.75);
letter-spacing: 0.5px;
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
padding: 22px 28px;
background: linear-gradient(135deg, #4a90d9, #3a7bc8);
border: none;
border-radius: 14px;
cursor: pointer;
transition: all 0.25s;
text-decoration: none;
box-shadow: 0 8px 24px rgba(74, 144, 217, 0.3);
gap: 8px;
font-weight: 600;
}
.face-monitor__live {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: rgba(255, 255, 255, 0.55);
}
.face-monitor__live::before {
content: "";
width: 7px;
height: 7px;
border-radius: 50%;
background: #ef4444;
box-shadow: 0 0 6px #ef4444;
animation: liveBlink 1.2s infinite;
}
@keyframes liveBlink {
0%, 100% { opacity: 1; }
50% { opacity: 0.35; }
}
/* 监控主体:左模拟副屏画面 + 右文案 */
.face-monitor__body {
display: grid;
grid-template-columns: 200px 1fr;
gap: 20px;
align-items: center;
}
/* 模拟副屏画面(黑色摄像头视角) */
.face-preview {
position: relative;
width: 200px;
height: 140px;
background: linear-gradient(180deg, #0a0e1a 0%, #14182a 100%);
border-radius: 10px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.start-checkout:hover {
transform: translateY(-2px);
box-shadow: 0 12px 32px rgba(74, 144, 217, 0.45);
/* 人形剪影 */
.face-preview__silhouette {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 110px;
height: 120px;
background: radial-gradient(circle at 50% 25%, rgba(255,255,255,0.12) 0%, rgba(255,255,255,0.04) 30%, transparent 60%);
border-radius: 50% 50% 20% 20% / 60% 60% 30% 30%;
}
.start-checkout:active { transform: scale(0.99); }
.start-checkout__icon {
font-size: 32px;
/* 人脸检测框(状态色虚线) */
.face-preview__box {
position: absolute;
left: 50%;
top: 18%;
transform: translateX(-50%);
width: 74px;
height: 82px;
border: 1.5px dashed rgba(255, 255, 255, 0.4);
border-radius: 6px;
transition: border-color 0.3s;
}
.start-checkout__text {
text-align: left;
.face-preview__box--success { border-color: #4ade80; box-shadow: 0 0 8px rgba(74, 222, 128, 0.5); }
.face-preview__box--warning { border-color: #fbbf24; box-shadow: 0 0 8px rgba(251, 191, 36, 0.5); }
.face-preview__box--info { border-color: #60a5fa; box-shadow: 0 0 8px rgba(96, 165, 250, 0.4); }
/* 检测框四角强调 */
.face-preview__corner {
position: absolute;
width: 10px;
height: 10px;
border: 2px solid currentColor;
}
.face-preview__corner--tl { top: -1px; left: -1px; border-right: none; border-bottom: none; border-top-left-radius: 3px; }
.face-preview__corner--tr { top: -1px; right: -1px; border-left: none; border-bottom: none; border-top-right-radius: 3px; }
.face-preview__corner--bl { bottom: -1px; left: -1px; border-right: none; border-top: none; border-bottom-left-radius: 3px; }
.face-preview__corner--br { bottom: -1px; right: -1px; border-left: none; border-top: none; border-bottom-right-radius: 3px; }
/* 扫描线(位置不对状态时上下扫动) */
.face-preview__scan {
position: absolute;
left: 8%;
right: 8%;
height: 1.5px;
background: linear-gradient(90deg, transparent, rgba(96, 165, 250, 0.8), transparent);
top: 20%;
animation: scanMove 2.2s linear infinite;
}
@keyframes scanMove {
0% { top: 18%; opacity: 0.9; }
50% { top: 70%; opacity: 0.9; }
100% { top: 18%; opacity: 0.9; }
}
/* 右侧文案区 */
.face-info {
display: flex;
flex-direction: column;
gap: 2px;
gap: 10px;
min-width: 0;
}
.start-checkout__title {
font-size: 20px;
.face-info__status {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border-radius: 14px;
font-size: 13px;
font-weight: 700;
align-self: flex-start;
}
.face-info__status--success { background: rgba(74, 222, 128, 0.18); color: #4ade80; border: 1px solid rgba(74, 222, 128, 0.4); }
.face-info__status--warning { background: rgba(251, 191, 36, 0.18); color: #fbbf24; border: 1px solid rgba(251, 191, 36, 0.4); }
.face-info__status--info { background: rgba(96, 165, 250, 0.18); color: #60a5fa; border: 1px solid rgba(96, 165, 250, 0.4); }
.face-info__user {
font-size: 26px;
font-weight: 700;
color: #fff;
line-height: 1.2;
}
.start-checkout__desc {
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
}
/* 呼吸灯 */
.start-checkout::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.15) 50%, transparent 70%);
transform: translateX(-100%);
animation: shimmer 2.5s ease-in-out infinite;
}
@keyframes shimmer {
0%, 100% { transform: translateX(-100%); }
50% { transform: translateX(100%); }
.face-info__sub {
font-size: 14px;
color: rgba(255, 255, 255, 0.6);
line-height: 1.5;
}
/* 右:今日数据预览 */
@@ -385,23 +502,43 @@
<!-- 中央核心动作区 -->
<div class="hero">
<!-- 左:大时钟 + 开始结算 -->
<!-- 左:大时钟 + 副屏人脸识别提醒 -->
<div class="hero__left">
<div>
<!-- 时钟区:只显示时分秒 + 餐次标签(年月日已由右上角 topMeta 展示) -->
<div class="clock-block">
<div class="big-clock" id="bigClock">12:30:45</div>
<div class="big-date" id="bigDate">
2026-08-06 周四
<span class="meal-tag-large meal-tag-large--lunch" id="mealTag">&#x1F96A; 午餐时段</span>
<span class="meal-tag-large meal-tag-large--lunch" id="mealTag">&#x1F96A; 午餐时段</span>
</div>
<!-- 副屏人脸识别提醒区(收银员用于判断是否需要提示顾客调整站位/补光) -->
<div class="face-monitor face-monitor--success" id="faceMonitor">
<div class="face-monitor__head">
<div class="face-monitor__title">&#x1F4F7; 副屏人脸识别</div>
<div class="face-monitor__live">实时</div>
</div>
<div class="face-monitor__body">
<!-- 模拟副屏画面:人形剪影 + 检测框 -->
<div class="face-preview">
<div class="face-preview__silhouette"></div>
<div class="face-preview__box face-preview__box--success" id="faceBox">
<span class="face-preview__corner face-preview__corner--tl"></span>
<span class="face-preview__corner face-preview__corner--tr"></span>
<span class="face-preview__corner face-preview__corner--bl"></span>
<span class="face-preview__corner face-preview__corner--br"></span>
</div>
<!-- 扫描线仅在"位置不对"时显示 -->
<div class="face-preview__scan" id="faceScan" style="display:none;"></div>
</div>
<!-- 右侧文案 -->
<div class="face-info">
<span class="face-info__status face-info__status--success" id="faceStatus">&#x2705; 识别成功</span>
<div class="face-info__user" id="faceUser">张*明</div>
<div class="face-info__sub" id="faceSub">会员金牌 LV5 · 工号 EMP002346</div>
</div>
</div>
</div>
<a class="start-checkout" href="03-checkout.html">
<span class="start-checkout__icon">&#x1F6CD;&#xFE0F;</span>
<span class="start-checkout__text">
<span class="start-checkout__title">开始结算</span>
<span class="start-checkout__desc">顾客取餐完成,点击进入结算流程</span>
</span>
</a>
</div>
<!-- 右:今日数据预览 -->
@@ -448,11 +585,11 @@
<div class="entry-card__desc">今日数据可视化</div>
</div>
</a>
<a class="entry-card" href="09-receipt-preview.html">
<div class="entry-card__icon entry-card__icon--gold">&#x1F5A8;&#xFE0F;</div>
<a class="entry-card" href="javascript:void(0)" onclick="alert('用餐管理功能待开发')">
<div class="entry-card__icon entry-card__icon--gold">&#x1F37D;&#xFE0F;</div>
<div class="entry-card__text">
<div class="entry-card__title">小票打印</div>
<div class="entry-card__desc">补打/重打订单小票</div>
<div class="entry-card__title">用餐管理</div>
<div class="entry-card__desc">用餐记录/餐次管理</div>
</div>
</a>
<a class="entry-card" href="javascript:void(0)" onclick="alert('设置功能待开发')">
@@ -469,26 +606,117 @@
</div>
<script>
/* 实时时钟 */
/* ============ 实时时钟(只显示时分秒,年月日由 topMeta 展示) ============ */
function updateClock() {
var now = new Date();
var hh = String(now.getHours()).padStart(2, '0');
var mm = String(now.getMinutes()).padStart(2, '0');
var ss = String(now.getSeconds()).padStart(2, '0');
var timeStr = hh + ':' + mm + ':' + ss;
document.getElementById('bigClock').textContent = hh + ':' + mm + ':' + ss;
/* 右上角元信息:年月日 + 周几 */
var year = now.getFullYear();
var month = String(now.getMonth() + 1).padStart(2, '0');
var date = String(now.getDate()).padStart(2, '0');
var weekDay = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][now.getDay()];
var dateStr = year + '-' + month + '-' + date + ' ' + weekDay;
document.getElementById('topMeta').textContent = year + '-' + month + '-' + date + ' ' + weekDay;
document.getElementById('bigClock').textContent = timeStr;
document.getElementById('bigDate').firstChild.textContent = ' ' + dateStr + ' ';
document.getElementById('topMeta').textContent = dateStr;
/* 自动判断餐次(根据当前小时切换标签文案/配色) */
var hour = now.getHours();
var mealTag = document.getElementById('mealTag');
if (hour >= 6 && hour < 10) {
mealTag.style.background = 'rgba(250,140,22,0.18)';
mealTag.style.color = '#fb923c';
mealTag.style.borderColor = 'rgba(250,140,22,0.35)';
mealTag.innerHTML = '&#x1F305; 早餐时段';
} else if (hour >= 10 && hour < 14) {
mealTag.style.background = '';
mealTag.style.color = '';
mealTag.style.borderColor = '';
mealTag.innerHTML = '&#x1F96A; 午餐时段';
} else if (hour >= 16 && hour < 21) {
mealTag.style.background = 'rgba(34,197,94,0.18)';
mealTag.style.color = '#4ade80';
mealTag.style.borderColor = 'rgba(34,197,94,0.35)';
mealTag.innerHTML = '&#x1F319; 晚餐时段';
} else {
mealTag.style.background = '';
mealTag.style.color = '';
mealTag.style.borderColor = '';
mealTag.innerHTML = '&#x1F31F; 欢迎光临';
}
}
updateClock();
setInterval(updateClock, 1000);
/* ============ 副屏人脸识别状态三态轮播(演示) ============
三态轮播用于在原型中展示收银员需要识别的三种提醒场景:
success: 识别成功 - 显示用户信息,收银员可引导支付
warning: 光线不足 - 提醒顾客补光
info: 位置不对 - 提醒顾客正对摄像头
后端真实场景下应根据副屏识别结果事件触发状态切换,而非轮播。
*/
var FACE_STATES = [
{
key: 'success',
monitorCls: 'face-monitor--success',
boxCls: 'face-preview__box--success',
statusCls: 'face-info__status--success',
statusHtml: '&#x2705; 识别成功',
user: '张*明',
sub: '会员金牌 LV5 · 工号 EMP002346',
showScan: false
},
{
key: 'warning',
monitorCls: 'face-monitor--warning',
boxCls: 'face-preview__box--warning',
statusCls: 'face-info__status--warning',
statusHtml: '&#x1F4A1; 光线不足',
user: '— · —',
sub: '环境亮度低于识别阈值,请补光',
showScan: false
},
{
key: 'info',
monitorCls: 'face-monitor--info',
boxCls: 'face-preview__box--info',
statusCls: 'face-info__status--info',
statusHtml: '&#x1F3AF; 位置不对',
user: '未检测到人脸',
sub: '请顾客正对摄像头并保持 50-80cm 距离',
showScan: true
}
];
var faceIdx = 0;
function applyFaceState(state) {
var monitor = document.getElementById('faceMonitor');
var box = document.getElementById('faceBox');
var status = document.getElementById('faceStatus');
var user = document.getElementById('faceUser');
var sub = document.getElementById('faceSub');
var scan = document.getElementById('faceScan');
/* 清除原三态修饰类 */
monitor.className = 'face-monitor ' + state.monitorCls;
box.className = 'face-preview__box ' + state.boxCls;
status.className = 'face-info__status ' + state.statusCls;
status.innerHTML = state.statusHtml;
user.textContent = state.user;
sub.textContent = state.sub;
scan.style.display = state.showScan ? 'block' : 'none';
}
function rotateFaceState() {
applyFaceState(FACE_STATES[faceIdx]);
faceIdx = (faceIdx + 1) % FACE_STATES.length;
}
/* 初始展示第一态,4 秒后开始轮播 */
rotateFaceState();
setInterval(rotateFaceState, 4000);
</script>
</body>
@@ -896,15 +896,6 @@
color: #0AC4A8;
font-weight: 800;
}
.balance-info__level {
padding: 3px 10px;
background: linear-gradient(135deg, rgba(251, 191, 36, 0.25), rgba(251, 146, 60, 0.18));
border: 1px solid rgba(251, 191, 36, 0.5);
border-radius: 10px;
font-size: 11px;
color: #fbbf24;
font-weight: 700;
}
.balance-info__tip {
margin-top: 6px;
padding: 5px 8px;
@@ -1155,10 +1146,6 @@
<span>账户余额</span>
<strong id="topBalance">&#xFFE5;128.50</strong>
</div>
<div class="user-bar__meta-item">
<span>本单应付</span>
<strong id="topPayable">&#xFFE5;64.40</strong>
</div>
</div>
</div>
@@ -1176,9 +1163,6 @@
<a class="header-btn header-btn--primary" href="08-reconcile.html">
&#x270F;&#xFE0F; 补齐明细
</a>
<a class="header-btn" href="javascript:void(0)" onclick="clearAll()">
&#x1F5D1;&#xFE0F; 清空
</a>
</div>
</div>
@@ -1420,7 +1404,6 @@
<!-- 余额支付信息卡 -->
<div class="balance-info" id="balanceInfo">
<div class="balance-info__row">
<span class="balance-info__label">&#x1F451; 会员等级 <span class="balance-info__level">金牌会员 LV5</span></span>
<span class="balance-info__label">&#x1F4B0; 账户余额 <span class="balance-info__value balance-info__value--highlight" id="balanceAvail">&#xFFE5;128.50</span></span>
</div>
<div class="balance-info__row">
@@ -1499,7 +1482,6 @@
var ORDER_PAYABLE = ORDER_TOTAL - ORDER_DISCOUNT; /* 64.40 */
var COUPON_VALUE = 10.00; /* 代金券面值 */
var USER_BALANCE = 128.50; /* 会员账户余额 */
var USER_LEVEL = '金牌会员 LV5';
/* 全局状态:currentPay 为当前支付方式 Tab;couponApplied 为是否已核销代金券;
mixedMode 为混合支付状态(null=未混合 | 'bl-online' | 'bl-cash' | 'cash-online');
@@ -1684,9 +1666,6 @@
btnAmount.innerHTML = '&#xFFE5;' + amountToShow.toFixed(2);
btn.innerHTML = prefix + '<span id="btnAmount">&#xFFE5;' + amountToShow.toFixed(2) + '</span>';
/* 同步顶部应付金额 */
document.getElementById('topPayable').innerHTML = '&#xFFE5;' + effPayable.toFixed(2);
}
/* 刷新余额信息卡(余额支付 Tab 下显示;混合支付进行中也保留,仅切换提示文案与按钮选中态) */
@@ -1949,16 +1928,6 @@
}, 1200);
}
/* 清空订单 */
function clearAll() {
if (confirm('确定清空当前订单所有商品吗?')) {
document.getElementById('itemsList').innerHTML =
'<div class="empty-state"><div class="empty-state__icon">&#x1F6CD;&#xFE0F;</div>'
+ '<div class="empty-state__text">订单已清空,等待顾客重新取餐</div></div>';
document.getElementById('itemCount').textContent = '0 件';
}
}
/* Toast 提示 */
function showToast(msg, type) {
var el = document.getElementById('toast');
@@ -1978,8 +1947,8 @@
- low-balance 余额不足(USER_BALANCE=20.00),走混合支付,演示余额+在线/现金补差
代金券预载为独立开关,可叠加在任一场景上。 */
var DEMO_SCENES = {
'normal': { balance: 128.50, level: '金牌会员 LV5' },
'low-balance': { balance: 20.00, level: '金牌会员 LV5' }
'normal': { balance: 128.50 },
'low-balance': { balance: 20.00 }
};
var currentScene = 'normal';
var couponPreset = false; /* 代金券预载开关 */
@@ -749,7 +749,7 @@
</div>
<div class="kpi-card__compare">
<span class="compare-up">&#x2197; 8.3%</span>
<span>已结 80 / 进行 16</span>
<span>较昨日午餐</span>
</div>
</div>
@@ -761,7 +761,7 @@
</div>
<div class="kpi-card__compare">
<span class="compare-down">&#x2198; 0.3%</span>
<span>退款率 1.8%</span>
<span>较昨日午餐</span>
</div>
</div>
@@ -835,17 +835,17 @@
<div class="bars__item">
<span class="bars__value">&#xFFE5;732.06</span>
<div class="bars__bar bars__bar--face" style="height:43%;"></div>
<span class="bars__label">&#x1F464; 刷脸</span>
<span class="bars__label">&#x1F464; 余额支付</span>
</div>
<div class="bars__item">
<span class="bars__value">&#xFFE5;313.74</span>
<div class="bars__bar bars__bar--cash" style="height:18%;"></div>
<span class="bars__label">&#x1F4B6; 现金</span>
<span class="bars__label">&#x1F4B6; 现金支付</span>
</div>
<div class="bars__item">
<span class="bars__value">&#xFFE5;209.16</span>
<div class="bars__bar bars__bar--coupon" style="height:12%;"></div>
<span class="bars__label">&#x1F3AB; 餐券</span>
<span class="bars__label">&#x1F3AB; 餐券抵扣</span>
</div>
</div>
</div>
@@ -775,8 +775,6 @@
<span class="compare-row__value" id="newAmount">&#xFFE5;46.60</span>
</div>
<div class="compare-row" style="border-top:1px solid rgba(255,255,255,0.1);margin-top:6px;padding-top:10px;">
<span class="compare-row__label">企业补贴优惠</span>
<span class="compare-row__value" style="color:#4ade80;">-&#xFFE5;5.00</span>
</div>
<div class="compare-row compare-row--delta" id="deltaRow">
<span class="compare-row__label">应付金额</span>
@@ -1,5 +1,60 @@
# 结算设备 CHANGELOG
## 2026-08-10 | 待机页(02)新增副屏人脸识别监控区 + 入口改名
### 业务背景
`02-standby.html` 待机页左下角的「开始结算」按钮是收银员主动进入结算流程的入口,但实际业务中:
1. **结算入口已由副屏人脸识别驱动**:顾客取餐后站立到副屏前会被自动识别,收银员屏 03-checkout 自动加载订单。手动「开始结算」按钮与该流程重复,且容易让收银员误以为需要主动触发。
2. **收银员无法判断副屏识别状态**:副屏识别成功率受光线/站位影响,但收银员屏没有对应的实时反馈区,无法主动提示顾客调整站位或补光。
### 变更
- **删除「开始结算」按钮区**,左下区域改为「副屏人脸识别监控区」
- **监控区三态轮播演示**(对应真实业务中的三种识别状态):
- `success` 识别成功:绿色边框 + ✅ 徽标 + 脱敏用户名 + 会员等级/工号
- `warning` 光线不足:黄色边框 + 💡 徽标 + 「环境亮度低于识别阈值,请补光」
- `info` 位置不对:蓝色边框 + 🎯 徽标 + 「请顾客正对摄像头并保持 50-80cm 距离」+ 扫描线动画
- **模拟副屏画面**:200×140px 黑色摄像头视角块,含人形剪影 + 检测框(四角强调 + 状态色虚线) + LIVE 实时指示
- **时钟区精简**:年月日已由右上角 topMeta 承担,时钟区只保留「时分秒 + 餐次标签」,餐次标签按当前小时自动切换(早餐/午餐/晚餐/欢迎光临)
- **底部入口改名**:原「🖨️ 小票打印 - 补打/重打订单小票」改为「🍽️ 用餐管理 - 用餐记录/餐次管理」(功能待开发,占位 `javascript:void(0)`)
### 技术实现
- 监控区采用 `face-monitor--{success|warning|info}` 三态修饰符,通过 `border-color` + `box-shadow` 联动 + `animation: faceWarnBlink` 实现 warning 态闪烁
- 模拟副屏画面采用纯 CSS 实现:`radial-gradient` 模拟人形剪影、`border-dashed` + 四角 `.face-preview__corner` 强调检测框、`@keyframes scanMove` 实现扫描线垂直循环
- 轮播逻辑:4 秒切换一次状态,后端真实场景下应替换为副屏识别事件驱动
### 不变更
- 右上角 topMeta(年月日 + 周几)、今日数据预览(右栏 KPI 卡片)、底部其他入口卡片(订单管理/对账管理/设置)保持不变
- 时钟更新逻辑(每秒 setInterval)与 topMeta 同步逻辑保持不变
---
## 2026-08-10 | 多页面冗余元素精简(03 / 07 / 08 / user-02 / user-04)
### 业务背景
结算设备多个页面在前期原型迭代过程中积累了一些「demo 占位文案 / 重复信息 / 与新业务模型不符的冗余字段」,需要在新业务模型(用户类型三态体系、支付方式六态体系)落地后统一清理,避免视觉噪声与信息歧义。
### 变更清单
- **`03-checkout.html` 收银页**:
- 删除顶栏「本单应付」字段(应付金额已由右栏底部按钮统一展示,顶栏重复显示)
- 删除余额信息卡的「👑 会员等级 - 金牌会员 LV5」行(用户身份徽标已由 `user-type` 三态徽标承担,等级信息冗余)
- 删除顶部操作按钮组的「🗑️ 清空」按钮(支付前不应允许整体清空订单,业务上属于危险操作)
- 同步删除相关 CSS(`.balance-info__level`)、JS 变量(`USER_LEVEL``topPayable` 同步逻辑)、`clearAll()` 函数、`DEMO_SCENES.normal/low-balance.level` 字段
- **`07-statistics.html` 统计页**:
- KPI 卡片对比文案统一改为「较昨日午餐」(原为「已结 80 / 进行 16」「退款率 1.8%」等不统一的 demo 文案)
- 支付方式柱状图标签语义化:「刷脸」→「余额支付」、「现金」→「现金支付」、「餐券」→「餐券抵扣」(对齐 03-checkout 的支付方式体系)
- **`08-reconcile.html` 补齐明细页**:
- 删除右列差额汇总的「企业补贴优惠 -¥5.00」一行(企业补贴模型已不适用,且与「补齐未识别」业务路径无关)
- **`user-02-checkout-sync.html` 用户副屏取餐同步页**:
- 删除热量环中央的「略少 76%」提示文字(占位文案无实际业务含义,且与下方 `/ 950 kcal` 信息冗余)
- **`user-04-success.html` 用户副屏支付成功页**:
- 删除二维码引导区的「🔥 今日已有 1,284 人查看」营销文案(原型 demo 文案,与真实业务数据无关)
### 不变更
- 各页面的核心业务结构、支付流程、URL 参数驱动逻辑保持不变
- 用户类型徽标体系、支付方式徽标体系保持不变
---
## 2026-08-10 | 订单列表(05)+订单详情(06)增加用户类型展示
### 业务背景
@@ -997,7 +997,6 @@
<div class="calorie-ring__center">
<div class="calorie-ring__value">720</div>
<div class="calorie-ring__unit">/ 950 kcal</div>
<div class="calorie-ring__hint">略少 76%</div>
</div>
</div>
@@ -559,7 +559,6 @@
<div class="qr-guide__text">
<div class="qr-guide__title">&#x1F4F1; 扫码查看详细营养报告</div>
<div class="qr-guide__sub">小程序内可查看每日营养摄入趋势、健康建议、餐品推荐</div>
<span class="qr-guide__tag">&#x1F525; 今日已有 1,284 人查看</span>
</div>
</div>
</div>