Compare commits

..
9 Commits
Author SHA1 Message Date
fengpu a131d5545e Merge branch 'master' of https://gitea.rk-health.com/yixiong/platform-prototype 2026-08-03 18:41:50 +08:00
fengpuandClaude Opus 4.7 1404c38497 docs: 用户群体画像重构为5类+CHANGELOG与里程碑同步
- 营养管理功能说明.md:用户群体由 7 类精简为 5 类(管理端 4 类、小程序端 1 类),小程序端 VIP 定制用户与美食创作者并入「普通用户」;调整营养师/食堂管理员职责范围与涵盖岗位
- CHANGELOG.md:补充自有餐品库改名与内容中心新增条目
- 记忆文件:同步里程碑日期描述

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 18:40:24 +08:00
fengpuandClaude Opus 4.7 799be643b2 feat: 小程序端-人脸认证流程+钱包明细详情
- me-home.html 用户卡片新增「未认证/已认证」人脸标签,未认证点击跳转人脸录入
- me-account.html 增加演示切换开关用于切换人脸认证状态
- 新增 me-face-verify.html(人脸认证引导/采集)和 me-face-info.html(认证信息展示)
- me-wallet.html 钱包页扩展收支明细/账户卡片等内容
- 新增 me-wallet-detail.html 钱包明细详情页

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 18:40:00 +08:00
fengpuandClaude Opus 4.7 836b05383d feat: 设备端-结算设备菜单化+营养评分屏扩展+LED屏重构
- checkout-device/index.html 由占位改为三菜单入口(结算/小票/查询),增加 PAD 外壳样式
- 新增 checkout-device/02-print-receipt.html 打印小票原型
- nutrition-score-screen/index.html 大幅扩展:实时营养得分看板、用户排行榜、餐品热度
- led-food-display/index.html 简化为引用实拍图版本,新增 led-v2.jpg
- canteen-device/index.html 导航:结算设备、营养得分屏标记为「已设计」

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 18:39:43 +08:00
fengpuandClaude Opus 4.7 e157638168 feat: 用户管理扩展-账户管理+人脸采集入口
- din-user-mgmt.html 会员表格新增「账号余额」列,「查看/编辑」改为「详情/账户管理/人脸采集」三个操作
- 新增 din-user-account.html 账户管理详情页(充值/消费/退款记录等)
- 同步沿用侧边栏自有餐品库改名+终端管理分组重构

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 18:39:17 +08:00
fengpuandClaude Opus 4.7 d32141e6ba feat: 餐品新增页扩展宣传视频上传功能
- din-dish-add.html 制作步骤新增「宣传视频」上传区,与原有「制作视频」并列
- 同步沿用侧边栏自有餐品库改名+终端管理分组重构

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 18:39:03 +08:00
fengpuandClaude Opus 4.7 8e49ebc020 feat: 自有餐品库改名+终端管理重构为分组菜单
- 自由餐品库 → 自有餐品库,路径 din-dish-free.html → din-dish-own.html
- 「终端管理」升级为可展开二级菜单组,下设「设备管理」(din-terminal.html) 和「内容中心」(din-content-center.html 新增)
- 同步更新全部营养用餐分组页面侧边栏及 index.html SITE_MAP

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 18:38:46 +08:00
fengpuandClaude Opus 4.7 b19d24bef8 chore: memory-sync 记忆体系同步(2026-08-03)
更新 project_progress.md 新增自由餐品库里程碑,lint 全项通过。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 14:08:06 +08:00
fengpuandClaude Opus 4.7 aa3d439471 feat: 营养用餐侧边栏新增「自由餐品库」菜单项
在「餐品总库」与「算法标准」之间新增自由餐品库入口,页面内容暂复制自餐品总库,后续将做差异化。同步更新全部 26 个营养用餐分组页面侧边栏及 SITE_MAP。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-03 14:06:19 +08:00
48 changed files with 5131 additions and 371 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
_Last synced: 2026-07-24 | Base commit: `91621bc`_
_Last synced: 2026-08-03 | Base commit: `aa3d439`_
# 记忆索引
| 文件 | 描述 | 类型 | 最后更新 |
+2 -2
View File
@@ -2,12 +2,12 @@
name: 记忆健康检查报告
description: memory-lint 最新一次执行的检查结果与待处理项
type: lint
last_updated: 2026-07-24
last_updated: 2026-08-03
---
# 记忆健康检查报告
> _执行时间: 2026-07-24 | Base commit: `91621bc` | Last synced: 2026-07-24_
> _执行时间: 2026-08-03 | Base commit: `aa3d439` | Last synced: 2026-08-03_
## 健康概览
+4 -3
View File
@@ -2,8 +2,8 @@
name: 项目进度
description: 各模块原型开发进度与里程碑
type: project
last_updated: 2026-07-24
commit: 91621bc
last_updated: 2026-08-03
commit: aa3d439
---
# 项目进度
@@ -99,10 +99,11 @@ commit: 91621bc
Web 管理端和员工端 APP 的其余模块(系统管理、运动管理、健康评估、知识普及、健康数据、心脑血管/糖尿病/癌症预防等)均为待开发状态。
## 总计
- 已完成/设计中:567+ 个 HTML 原型页面
- 已完成/设计中:568+ 个 HTML 原型页面
- 覆盖模块:18 个(含三端+其他终端)
## 近期里程碑
- 2026-08-03:营养管理后台新增「自有餐品库」二级菜单——在营养用餐侧边栏"餐品总库"与"算法标准"之间新增 `din-dish-own.html`(自有餐品库),页面内容暂复制自餐品总库后续将做差异化,同步更新全部 26 个营养用餐分组页面的侧边栏导航及 `index.html` SITE_MAP。
- 2026-07-24:营养管理后台主子表重构——净菜订单/外售/内供三页面数据模型全面重构为主子表(一单多品),表格展开显示菜品明细,弹窗重构为动态明细行构建器(支持多菜品添加/删除/联动/汇总),确认收货改为行级逐条确认;采购管理新增验收数量列及验收明细弹窗(逐项校验);净菜进柜/组配/烹制新增关联订单列与订单进度展示;净菜加工移除质检结果列。小程序端订单详情新增支付信息卡片(含支付方式/流水号/时间)与餐线订单渲染;我的订单新增餐线订单卡片与Tab切换过滤(全部/待取餐/待配送/已完成)。体重管理新增 diet-advice-v2 膳食建议v2、eat-detail-v2 饮食详情v2、scan-checkin 扫码打卡、weight-detail-v2 体重详情v2 四个重构版页面。
- 2026-07-23:营养管理后台与小程序多项增强——后台:场所新增/编辑页重构(营业时段/场所图片/资质证照 5 卡片区)、食材与餐品总库新增分类管理弹窗(树形增删改查)、食材新增/编辑页支持从已有食材复制;小程序:净菜包订单按状态差异化展示(待配送/配送中/已完成)、履约信息时间选择器重构为日期+时段联动下拉面板、新增收货地址管理页 me-address.html、健康档案移除营养目标分区;同步更新 CHANGELOG 及导航页。
- 2026-07-21:导航页 popover 事件冒泡回归再次修复(同一根因第二次出现)——0603113 提交将 popover 重构为延迟渲染模式(首次点击"X 页"才动态构建 DOM),但 `togglePop` 函数内 `insertAdjacentHTML` 模板漏加 `onclick="event.stopPropagation()"`,导致同样的双 tab 打开回归。修复方式:在延迟构建模板中补上 stopPropagationcommit f671764)。系统性教训:嵌套 onclick 结构中子容器的 stopPropagation 在代码重构时极易遗漏,后续涉及 popover/弹层 DOM 创建位置变更时需检查事件冒泡拦截。
+1 -1
View File
@@ -1,4 +1,4 @@
<!-- Last updated: 2026-07-24 | Commit: 91621bc -->
<!-- Last updated: 2026-08-03 | Commit: aa3d439 -->
# 项目约束
本项目所有工作必须严格遵守 `项目背景.md` 中定义的全部规范,包括:
+3 -1
View File
@@ -294,13 +294,15 @@ const SITE_MAP = [
{ file:'sup-clean-external.html', label:'净菜外售' },
{ file:'din-user-mgmt.html', label:'用户管理' },
{ file:'din-dish-all.html', label:'菜品总库' },
{ file:'din-dish-own.html', label:'自有餐品库' },
{ file:'din-dish-all-dialog.html', label:'菜品总库弹窗', dialog:true },
{ file:'din-dish-add.html', label:'新增菜品' },
{ file:'din-ingredient.html', label:'食材管理' },
{ file:'din-ingredient-dialog.html', label:'食材管理弹窗', dialog:true },
{ file:'din-ingredient-detail.html', label:'食材详情' },
{ file:'din-recipe-mgmt.html', label:'食谱管理' },
{ file:'din-terminal.html', label:'终端管理' },
{ file:'din-terminal.html', label:'设备管理' },
{ file:'din-content-center.html', label:'内容中心' },
{ file:'din-order-meal.html', label:'点餐订单' },
{ file:'din-order-line.html', label:'排队取餐' },
{ file:'din-order-pickup.html', label:'取餐管理' },
@@ -0,0 +1,890 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>结算设备 - 打印小票预览 | 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
/* 外层视口:全屏深色背景,承载居中 PAD 外壳 */
body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #05080f;
color: #fff;
min-height: 100vh;
min-width: 1280px;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
/* PAD 设备外壳 */
.device-frame {
width: 1280px;
height: 720px;
background: #0e1326;
border-radius: 24px;
overflow: hidden;
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.04);
position: relative;
display: flex;
flex-direction: column;
}
/* 顶部导航栏 */
.top-bar {
display: flex;
align-items: center;
height: 44px;
background: #1a2b4a;
padding: 0 20px;
flex-shrink: 0;
}
.back-btn {
font-size: 13px;
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
cursor: pointer;
transition: color 0.2s;
}
.back-btn:hover { color: #fff; }
.top-bar__title {
flex: 1;
text-align: center;
font-size: 15px;
font-weight: 600;
color: rgba(255, 255, 255, 0.85);
}
.top-bar__meta {
font-size: 12px;
color: rgba(255, 255, 255, 0.5);
}
/* 主体:左右两列 */
.page-body {
display: grid;
grid-template-columns: 340px 1fr;
height: calc(100% - 44px);
overflow: hidden;
}
/* ===== 左列:订单列表 ===== */
.left-col {
display: flex;
flex-direction: column;
border-right: 1px solid rgba(255, 255, 255, 0.06);
background: rgba(26, 43, 74, 0.3);
}
.filter-bar {
padding: 12px 16px;
display: flex;
gap: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
flex-shrink: 0;
}
.filter-select {
flex: 1;
background: rgba(0, 0, 0, 0.25);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
padding: 6px 10px;
font-size: 12px;
color: rgba(255, 255, 255, 0.85);
font-family: inherit;
outline: none;
cursor: pointer;
}
.filter-select:focus { border-color: #4a90d9; }
.order-list {
flex: 1;
overflow-y: auto;
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 8px;
scrollbar-width: none;
}
.order-list::-webkit-scrollbar { display: none; }
.order-card {
background: #1a2b4a;
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 8px;
padding: 10px 12px;
cursor: pointer;
transition: all 0.2s;
position: relative;
}
.order-card:hover {
border-color: rgba(74, 144, 217, 0.4);
background: rgba(74, 144, 217, 0.08);
}
.order-card.active {
border-color: #4a90d9;
background: rgba(74, 144, 217, 0.12);
box-shadow: 0 0 0 1px rgba(74, 144, 217, 0.3);
}
.order-card.printed {
opacity: 0.55;
}
.order-card__row1 {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
.order-card__id {
font-size: 12px;
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
font-family: Menlo, Consolas, monospace;
}
.meal-tag {
font-size: 10px;
line-height: 1;
padding: 3px 7px;
border-radius: 9px;
font-weight: 600;
letter-spacing: 0.3px;
}
.meal-tag--breakfast {
background: rgba(250, 140, 22, 0.18);
color: #fb923c;
border: 1px solid rgba(250, 140, 22, 0.35);
}
.meal-tag--lunch {
background: rgba(37, 99, 235, 0.18);
color: #60a5fa;
border: 1px solid rgba(37, 99, 235, 0.35);
}
.meal-tag--dinner {
background: rgba(34, 197, 94, 0.18);
color: #4ade80;
border: 1px solid rgba(34, 197, 94, 0.35);
}
.order-card__row2 {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
}
.order-card__time {
font-family: Menlo, Consolas, monospace;
}
.order-card__amount {
font-size: 14px;
font-weight: 700;
color: #0AC4A8;
}
.order-card__status {
position: absolute;
top: 8px;
right: 8px;
font-size: 9px;
line-height: 1;
padding: 3px 6px;
border-radius: 8px;
font-weight: 600;
letter-spacing: 0.3px;
}
.order-card__status--printed {
background: rgba(255, 255, 255, 0.06);
color: rgba(255, 255, 255, 0.4);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.order-card__status--pending {
background: rgba(234, 88, 12, 0.18);
color: #fb923c;
border: 1px solid rgba(234, 88, 12, 0.35);
}
/* ===== 右列:小票预览 ===== */
.right-col {
display: flex;
flex-direction: column;
overflow: hidden;
}
.receipt-stage {
flex: 1;
padding: 24px 32px;
overflow-y: auto;
display: flex;
align-items: flex-start;
justify-content: center;
scrollbar-width: none;
background: linear-gradient(180deg, rgba(10, 16, 32, 0.4), rgba(10, 16, 32, 0.8));
}
.receipt-stage::-webkit-scrollbar { display: none; }
/* 热敏小票纸 */
.receipt-paper {
width: 300px;
background: #fff;
color: #1a1a2e;
border-radius: 4px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
padding: 16px 18px;
font-family: Menlo, Consolas, "Courier New", monospace;
font-size: 11px;
line-height: 1.6;
position: relative;
}
/* 模拟热敏纸撕扯边缘 */
.receipt-paper::before,
.receipt-paper::after {
content: "";
position: absolute;
left: 0;
right: 0;
height: 6px;
background-image: linear-gradient(45deg, transparent 50%, #fff 50%), linear-gradient(-45deg, transparent 50%, #fff 50%);
background-size: 8px 6px;
background-repeat: repeat-x;
}
.receipt-paper::before {
top: -6px;
}
.receipt-paper::after {
bottom: -6px;
transform: rotate(180deg);
}
/* 食堂抬头 */
.receipt-header { text-align: center; padding: 4px 0 8px; }
.receipt-name {
font-size: 14px;
font-weight: 700;
color: #1a1a2e;
margin-bottom: 4px;
font-family: -apple-system, "Microsoft YaHei", sans-serif;
}
.receipt-sub {
font-size: 10px;
color: #666;
line-height: 1.5;
}
/* 分割线 */
.receipt-divider {
border-top: 1px dashed #999;
margin: 8px 0;
}
.receipt-divider--double {
border-top: 1px dashed #999;
border-bottom: 1px dashed #999;
height: 4px;
}
/* 通用信息行 */
.receipt-info { display: flex; flex-direction: column; gap: 2px; }
.receipt-row {
display: flex;
justify-content: space-between;
align-items: baseline;
font-size: 11px;
}
.receipt-row__label { color: #666; }
.receipt-row__value { color: #1a1a2e; font-weight: 600; }
/* 消费明细表 */
.receipt-table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
font-family: Menlo, Consolas, monospace;
}
.receipt-table th {
font-weight: 600;
color: #666;
padding: 4px 0;
border-bottom: 1px dashed #999;
text-align: left;
}
.receipt-table th:nth-child(2),
.receipt-table th:nth-child(3),
.receipt-table th:nth-child(4) { text-align: right; }
.receipt-table td {
padding: 3px 0;
color: #1a1a2e;
vertical-align: top;
}
.receipt-table td:nth-child(2),
.receipt-table td:nth-child(3),
.receipt-table td:nth-child(4) { text-align: right; }
.receipt-table td:nth-child(3) { color: #666; }
/* 金额汇总 */
.receipt-amount {
display: flex;
flex-direction: column;
gap: 3px;
}
.receipt-amount__payable {
font-size: 13px;
font-weight: 700;
color: #1a1a2e;
}
.receipt-amount__paid {
font-size: 12px;
font-weight: 600;
color: #0a7c64;
}
/* 营养摄入摘要 */
.receipt-nutrition-title {
text-align: center;
font-size: 10px;
color: #666;
margin-bottom: 6px;
}
.receipt-nutrition-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 4px;
margin-bottom: 6px;
}
.nutrition-item {
text-align: center;
padding: 4px 2px;
background: #f5f5f5;
border-radius: 3px;
}
.nutrition-item__label {
font-size: 9px;
color: #999;
margin-bottom: 2px;
}
.nutrition-item__value {
font-size: 12px;
font-weight: 700;
color: #1a1a2e;
}
.nutrition-item__value small {
font-size: 9px;
font-weight: 400;
color: #999;
margin-left: 1px;
}
.receipt-score {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 10px;
background: linear-gradient(90deg, #f0f7ff, #e6f4ff);
border-radius: 4px;
border: 1px solid #cce4ff;
}
.receipt-score__label {
font-size: 10px;
color: #4a90d9;
font-weight: 600;
}
.receipt-score__value {
font-size: 16px;
font-weight: 700;
color: #2570c0;
}
/* 二维码 */
.receipt-qr {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 6px 0;
}
.receipt-qr__img {
width: 84px;
height: 84px;
}
.receipt-qr__text {
font-size: 9px;
color: #666;
text-align: center;
}
/* 底部提示 */
.receipt-footer {
text-align: center;
padding: 4px 0;
}
.receipt-footer__line1 {
font-size: 11px;
font-weight: 600;
color: #1a1a2e;
margin-bottom: 2px;
font-family: -apple-system, "Microsoft YaHei", sans-serif;
}
.receipt-footer__line2 {
font-size: 10px;
color: #999;
}
/* 底部操作栏 */
.action-bar {
display: flex;
gap: 12px;
justify-content: center;
padding: 12px 20px;
background: rgba(10, 16, 32, 0.6);
border-top: 1px solid rgba(255, 255, 255, 0.06);
flex-shrink: 0;
}
.btn {
padding: 10px 28px;
border: none;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
}
.btn--primary {
background: #4a90d9;
color: #fff;
}
.btn--primary:hover {
background: #3a7bc8;
box-shadow: 0 4px 14px rgba(74, 144, 217, 0.35);
}
.btn--primary:active { transform: scale(0.97); }
.btn--ghost {
background: rgba(255, 255, 255, 0.04);
color: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.15);
}
.btn--ghost:hover {
background: rgba(255, 255, 255, 0.08);
color: #fff;
border-color: rgba(255, 255, 255, 0.3);
}
.btn--success {
background: #52c41a;
color: #fff;
}
.btn--success:hover {
background: #45a818;
box-shadow: 0 4px 14px rgba(82, 196, 26, 0.35);
}
.btn--success:active { transform: scale(0.97); }
/* Toast */
.toast {
position: fixed;
top: 60px;
left: 50%;
transform: translateX(-50%) translateY(-20px);
background: rgba(0, 0, 0, 0.85);
color: #fff;
padding: 12px 28px;
border-radius: 8px;
font-size: 13px;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, transform 0.3s, visibility 0.3s;
white-space: nowrap;
pointer-events: none;
max-width: 90%;
text-align: center;
line-height: 1.5;
}
.toast.show {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
}
.toast--success { background: rgba(82, 196, 26, 0.92); }
.toast--warn { background: rgba(250, 173, 22, 0.92); }
</style>
</head>
<body>
<div class="device-frame">
<!-- 顶部导航 -->
<div class="top-bar">
<a class="back-btn" href="index.html">&#x25C0; 返回</a>
<span class="top-bar__title">打印小票预览</span>
<span class="top-bar__meta" id="topMeta">2026-08-03</span>
</div>
<!-- 主体 -->
<div class="page-body">
<!-- 左列:订单列表 -->
<div class="left-col">
<div class="filter-bar">
<select class="filter-select" id="mealFilter" onchange="renderOrderList()">
<option value="all">全部餐次</option>
<option value="breakfast">早餐</option>
<option value="lunch">午餐</option>
<option value="dinner">晚餐</option>
</select>
<select class="filter-select" id="statusFilter" onchange="renderOrderList()">
<option value="all">全部状态</option>
<option value="pending">未打印</option>
<option value="printed">已打印</option>
</select>
</div>
<div class="order-list" id="orderList"></div>
</div>
<!-- 右列:小票预览 -->
<div class="right-col">
<div class="receipt-stage" id="receiptStage"></div>
<div class="action-bar">
<button class="btn btn--ghost" onclick="window.location.href='index.html'">&#x25C0; 返回</button>
<button class="btn btn--success" id="printBtn" onclick="handlePrint()">&#x1F5A8;&#xFE0F; 打印小票</button>
</div>
</div>
</div>
</div>
<!-- Toast -->
<div class="toast" id="toast"></div>
<script>
/* ==================== Mock 订单数据 ==================== */
var mockOrders = [
{
id: 'CO-20260803-001',
mealType: 'lunch',
mealLabel: '午餐',
line: 'A 区 1 号餐线',
cashier: '张三',
time: '2026-08-03 12:15:30',
items: [
{ name: '宫保鸡丁', price: 18.00, qty: 1, subtotal: 18.00 },
{ name: '番茄炒蛋', price: 8.00, qty: 1, subtotal: 8.00 },
{ name: '米饭', price: 2.00, qty: 1, subtotal: 2.00 },
{ name: '紫菜蛋花汤', price: 5.00, qty: 1, subtotal: 5.00 }
],
total: 33.00, discount: 3.00, payable: 30.00, paid: 30.00,
payMethod: '人脸支付', balance: 1280.50,
nutrition: { calories: 685, protein: 28, fat: 18, carb: 95, score: 82 },
printed: true
},
{
id: 'CO-20260803-008',
mealType: 'breakfast',
mealLabel: '早餐',
line: 'A 区 1 号餐线',
cashier: '张三',
time: '2026-08-03 07:42:18',
items: [
{ name: '豆浆', price: 3.00, qty: 1, subtotal: 3.00 },
{ name: '油条', price: 2.50, qty: 2, subtotal: 5.00 },
{ name: '茶叶蛋', price: 4.00, qty: 1, subtotal: 4.00 }
],
total: 12.00, discount: 0, payable: 12.00, paid: 12.00,
payMethod: '餐卡支付', balance: 1292.50,
nutrition: { calories: 425, protein: 15, fat: 12, carb: 60, score: 75 },
printed: true
},
{
id: 'CO-20260803-015',
mealType: 'lunch',
mealLabel: '午餐',
line: 'A 区 2 号餐线',
cashier: '李四',
time: '2026-08-03 12:32:45',
items: [
{ name: '红烧肉', price: 22.00, qty: 1, subtotal: 22.00 },
{ name: '清炒时蔬', price: 6.00, qty: 1, subtotal: 6.00 },
{ name: '鱼香肉丝', price: 12.00, qty: 1, subtotal: 12.00 },
{ name: '米饭', price: 2.00, qty: 1, subtotal: 2.00 },
{ name: '冬瓜排骨汤', price: 3.00, qty: 1, subtotal: 3.00 }
],
total: 45.00, discount: 5.00, payable: 40.00, paid: 40.00,
payMethod: '人脸支付', balance: 1240.50,
nutrition: { calories: 720, protein: 35, fat: 28, carb: 88, score: 88 },
printed: false
},
{
id: 'CO-20260803-022',
mealType: 'dinner',
mealLabel: '晚餐',
line: 'A 区 1 号餐线',
cashier: '王五',
time: '2026-08-03 18:15:20',
items: [
{ name: '清蒸鲈鱼', price: 25.00, qty: 1, subtotal: 25.00 },
{ name: '蒜蓉西兰花', price: 6.00, qty: 1, subtotal: 6.00 },
{ name: '紫米饭', price: 3.00, qty: 1, subtotal: 3.00 }
],
total: 28.00, discount: 0, payable: 28.00, paid: 28.00,
payMethod: '扫码支付', balance: 1212.50,
nutrition: { calories: 510, protein: 32, fat: 14, carb: 65, score: 90 },
printed: false
},
{
id: 'CO-20260803-029',
mealType: 'lunch',
mealLabel: '午餐',
line: 'A 区 3 号餐线',
cashier: '张三',
time: '2026-08-03 12:48:10',
items: [
{ name: '番茄炒蛋', price: 8.00, qty: 1, subtotal: 8.00 },
{ name: '麻婆豆腐', price: 10.00, qty: 1, subtotal: 10.00 },
{ name: '米饭', price: 2.00, qty: 1, subtotal: 2.00 },
{ name: '番茄蛋汤', price: 5.00, qty: 1, subtotal: 5.00 }
],
total: 25.00, discount: 0, payable: 25.00, paid: 25.00,
payMethod: '人脸支付', balance: 1187.50,
nutrition: { calories: 480, protein: 22, fat: 15, carb: 70, score: 78 },
printed: false
},
{
id: 'CO-20260803-035',
mealType: 'breakfast',
mealLabel: '早餐',
line: 'A 区 2 号餐线',
cashier: '李四',
time: '2026-08-03 08:15:30',
items: [
{ name: '皮蛋瘦肉粥', price: 5.00, qty: 1, subtotal: 5.00 },
{ name: '鲜肉包子', price: 3.00, qty: 1, subtotal: 3.00 }
],
total: 8.00, discount: 0, payable: 8.00, paid: 8.00,
payMethod: '餐卡支付', balance: 1195.50,
nutrition: { calories: 320, protein: 12, fat: 8, carb: 48, score: 70 },
printed: false
}
];
/* 食堂抬头 */
var canteenInfo = {
name: 'CQ 能源公司员工食堂',
address: '重庆市渝北区星光大道 88 号',
phone: '023-7788-XXXX'
};
var currentOrderId = mockOrders[0].id;
/* ==================== 渲染:订单列表 ==================== */
function renderOrderList() {
var mealFilter = document.getElementById('mealFilter').value;
var statusFilter = document.getElementById('statusFilter').value;
var filtered = mockOrders.filter(function(o) {
if (mealFilter !== 'all' && o.mealType !== mealFilter) return false;
if (statusFilter === 'pending' && o.printed) return false;
if (statusFilter === 'printed' && !o.printed) return false;
return true;
});
var listEl = document.getElementById('orderList');
if (filtered.length === 0) {
listEl.innerHTML = '<div style="text-align:center;padding:24px 8px;color:rgba(255,255,255,0.3);font-size:12px;">无符合条件的订单</div>';
return;
}
var html = filtered.map(function(o) {
var mealTagClass = 'meal-tag--' + o.mealType;
var printedClass = o.printed ? ' printed' : '';
var activeClass = o.id === currentOrderId ? ' active' : '';
var statusHtml = o.printed
? '<span class="order-card__status order-card__status--printed">已打印</span>'
: '<span class="order-card__status order-card__status--pending">未打印</span>';
return '<div class="order-card' + activeClass + printedClass + '" onclick="selectOrder(\'' + o.id + '\')">'
+ statusHtml
+ '<div class="order-card__row1">'
+ '<span class="order-card__id">' + o.id + '</span>'
+ '<span class="meal-tag ' + mealTagClass + '">' + o.mealLabel + '</span>'
+ '</div>'
+ '<div class="order-card__row2">'
+ '<span class="order-card__time">' + o.time + '</span>'
+ '<span class="order-card__amount">¥' + o.payable.toFixed(2) + '</span>'
+ '</div>'
+ '</div>';
}).join('');
listEl.innerHTML = html;
}
/* ==================== 渲染:小票预览 ==================== */
function renderReceipt() {
var order = mockOrders.filter(function(o) { return o.id === currentOrderId; })[0];
if (!order) {
document.getElementById('receiptStage').innerHTML = '<div style="color:rgba(255,255,255,0.3);font-size:12px;">请选择左侧订单查看小票</div>';
updatePrintBtn();
return;
}
var itemsHtml = order.items.map(function(it) {
return '<tr>'
+ '<td>' + it.name + '</td>'
+ '<td>' + it.price.toFixed(2) + '</td>'
+ '<td>' + it.qty + '</td>'
+ '<td>' + it.subtotal.toFixed(2) + '</td>'
+ '</tr>';
}).join('');
var discountRow = order.discount > 0
? '<div class="receipt-row"><span class="receipt-row__label">优惠</span><span class="receipt-row__value" style="color:#d4380d;">-' + order.discount.toFixed(2) + '</span></div>'
: '';
var html = ''
+ '<div class="receipt-paper">'
/* 食堂抬头 */
+ '<div class="receipt-header">'
+ '<div class="receipt-name">' + canteenInfo.name + '</div>'
+ '<div class="receipt-sub">' + canteenInfo.address + '</div>'
+ '<div class="receipt-sub">电话:' + canteenInfo.phone + '</div>'
+ '</div>'
+ '<div class="receipt-divider receipt-divider--double"></div>'
/* 订单信息 */
+ '<div class="receipt-info">'
+ '<div class="receipt-row"><span class="receipt-row__label">订单号</span><span class="receipt-row__value">' + order.id + '</span></div>'
+ '<div class="receipt-row"><span class="receipt-row__label">餐线</span><span class="receipt-row__value">' + order.line + '</span></div>'
+ '<div class="receipt-row"><span class="receipt-row__label">收银员</span><span class="receipt-row__value">' + order.cashier + '</span></div>'
+ '<div class="receipt-row"><span class="receipt-row__label">就餐时间</span><span class="receipt-row__value">' + order.time + '</span></div>'
+ '</div>'
+ '<div class="receipt-divider"></div>'
/* 消费明细表 */
+ '<table class="receipt-table">'
+ '<thead><tr><th>餐品</th><th>单价</th><th>份数</th><th>小计</th></tr></thead>'
+ '<tbody>' + itemsHtml + '</tbody>'
+ '</table>'
+ '<div class="receipt-divider"></div>'
/* 金额汇总 */
+ '<div class="receipt-amount">'
+ '<div class="receipt-row"><span class="receipt-row__label">合计</span><span class="receipt-row__value">' + order.total.toFixed(2) + '</span></div>'
+ discountRow
+ '<div class="receipt-row receipt-amount__payable"><span class="receipt-row__label">应付</span><span>¥' + order.payable.toFixed(2) + '</span></div>'
+ '<div class="receipt-row receipt-amount__paid"><span class="receipt-row__label">实付</span><span>¥' + order.paid.toFixed(2) + '</span></div>'
+ '</div>'
+ '<div class="receipt-divider"></div>'
/* 支付信息 */
+ '<div class="receipt-info">'
+ '<div class="receipt-row"><span class="receipt-row__label">支付方式</span><span class="receipt-row__value">' + order.payMethod + '</span></div>'
+ '<div class="receipt-row"><span class="receipt-row__label">餐卡余额</span><span class="receipt-row__value">¥' + order.balance.toFixed(2) + '</span></div>'
+ '</div>'
+ '<div class="receipt-divider"></div>'
/* 营养摄入摘要 */
+ '<div class="receipt-nutrition-title">- - - 营养摄入摘要 - - -</div>'
+ '<div class="receipt-nutrition-grid">'
+ '<div class="nutrition-item"><div class="nutrition-item__label">热量</div><div class="nutrition-item__value">' + order.nutrition.calories + '<small>kcal</small></div></div>'
+ '<div class="nutrition-item"><div class="nutrition-item__label">蛋白质</div><div class="nutrition-item__value">' + order.nutrition.protein + '<small>g</small></div></div>'
+ '<div class="nutrition-item"><div class="nutrition-item__label">脂肪</div><div class="nutrition-item__value">' + order.nutrition.fat + '<small>g</small></div></div>'
+ '<div class="nutrition-item"><div class="nutrition-item__label">碳水</div><div class="nutrition-item__value">' + order.nutrition.carb + '<small>g</small></div></div>'
+ '</div>'
+ '<div class="receipt-score">'
+ '<span class="receipt-score__label">营养得分</span>'
+ '<span class="receipt-score__value">' + order.nutrition.score + '</span>'
+ '</div>'
+ '<div class="receipt-divider"></div>'
/* 二维码 */
+ '<div class="receipt-qr">'
+ getQRCodeSVG()
+ '<div class="receipt-qr__text">扫码查看电子小票与营养详情</div>'
+ '</div>'
+ '<div class="receipt-divider receipt-divider--double"></div>'
/* 底部提示 */
+ '<div class="receipt-footer">'
+ '<div class="receipt-footer__line1">合理膳食 均衡营养</div>'
+ '<div class="receipt-footer__line2">谢谢惠顾,欢迎下次光临</div>'
+ '</div>'
+ '</div>';
document.getElementById('receiptStage').innerHTML = html;
updatePrintBtn();
}
/* 简化版二维码 SVG (21x21 模块,模拟外观) */
function getQRCodeSVG() {
var matrix = [
'111111101010111111100',
'100000101011100000110',
'101110101110101110110',
'101110100101101110010',
'101110101101101110000',
'100000101001100000110',
'111111101010111111100',
'000000001101000000000',
'101011011001010110010',
'011010001100011001010',
'101101100110010100100',
'011000101010011011010',
'101011010011001011100',
'000000011001010100010',
'111111101100101101000',
'100000101011011010110',
'101110101010100100100',
'101110101101011011010',
'101110100110010100110',
'100000101011010010010',
'111111101101100101100'
];
var size = 21;
var unit = 4;
var dim = size * unit;
var rects = '<rect x="0" y="0" width="' + dim + '" height="' + dim + '" fill="#fff"/>';
for (var r = 0; r < size; r++) {
for (var c = 0; c < size; c++) {
if (matrix[r].charAt(c) === '1') {
rects += '<rect x="' + (c * unit) + '" y="' + (r * unit) + '" width="' + unit + '" height="' + unit + '" fill="#1a1a2e"/>';
}
}
}
return '<svg class="receipt-qr__img" viewBox="0 0 ' + dim + ' ' + dim + '" xmlns="http://www.w3.org/2000/svg">' + rects + '</svg>';
}
/* 切换订单 */
function selectOrder(orderId) {
currentOrderId = orderId;
renderOrderList();
renderReceipt();
}
/* 打印按钮状态切换 */
function updatePrintBtn() {
var order = mockOrders.filter(function(o) { return o.id === currentOrderId; })[0];
var btn = document.getElementById('printBtn');
if (order && order.printed) {
btn.className = 'btn btn--primary';
btn.innerHTML = '&#x1F5A8;&#xFE0F; 重新打印';
} else {
btn.className = 'btn btn--success';
btn.innerHTML = '&#x1F5A8;&#xFE0F; 打印小票';
}
}
/* 打印处理 */
function handlePrint() {
var order = mockOrders.filter(function(o) { return o.id === currentOrderId; })[0];
if (!order) {
showToast('请先选择订单', 'warn');
return;
}
order.printed = true;
renderOrderList();
updatePrintBtn();
showToast('订单 ' + order.id + ' 小票打印成功', 'success');
}
/* Toast */
function showToast(msg, type) {
var el = document.getElementById('toast');
el.textContent = msg;
el.className = 'toast';
if (type === 'success') el.classList.add('toast--success');
if (type === 'warn') el.classList.add('toast--warn');
el.classList.add('show');
clearTimeout(el._timer);
el._timer = setTimeout(function() { el.classList.remove('show'); }, 2200);
}
/* 初始化 */
renderOrderList();
renderReceipt();
</script>
</body>
</html>
@@ -6,29 +6,181 @@
<title>结算设备 - 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
/* 外层视口:全屏深色背景,承载居中 PAD 外壳 */
body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden;
background: #05080f;
color: #fff;
min-height: 100vh;
min-width: 1280px;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
/* PAD 设备外壳 */
.device-frame {
width: 1280px;
height: 720px;
background: #0e1326;
border-radius: 24px;
overflow: hidden;
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.04);
position: relative;
display: flex;
flex-direction: column;
}
/* 顶部导航栏 */
.top-bar {
display: flex;
align-items: center;
height: 44px;
background: #1a2b4a;
padding: 0 20px;
flex-shrink: 0;
}
.top-bar__title {
flex: 1;
text-align: center;
font-size: 15px;
font-weight: 600;
color: rgba(255, 255, 255, 0.85);
}
/* 主内容区:三菜单网格 */
.main-content {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 28px;
padding: 56px 80px;
height: calc(100% - 44px);
align-content: center;
}
/* 菜单卡片 */
.card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 18px;
padding: 36px 24px;
background: #1a2b4a;
border-radius: 16px;
text-decoration: none;
cursor: pointer;
transition: all 0.2s;
border: 1px solid rgba(255, 255, 255, 0.06);
text-align: center;
position: relative;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
border-color: rgba(74, 144, 217, 0.3);
}
/* 禁用卡片(待开发) */
.card--disabled {
pointer-events: none;
background: rgba(26, 43, 74, 0.4);
opacity: 0.55;
}
.card--disabled:hover {
transform: none;
box-shadow: none;
border-color: rgba(255, 255, 255, 0.06);
}
/* 图标圆底 */
.card__icon {
font-size: 44px;
width: 84px;
height: 84px;
border-radius: 50%;
background: rgba(74, 144, 217, 0.12);
display: flex;
align-items: center;
justify-content: center;
}
.card--disabled .card__icon {
background: rgba(255, 255, 255, 0.04);
}
.card__title {
font-size: 17px;
font-weight: 600;
color: #fff;
}
.card--disabled .card__title {
color: rgba(255, 255, 255, 0.6);
}
.card__desc {
font-size: 12px;
color: rgba(255, 255, 255, 0.45);
line-height: 1.5;
}
.card--disabled .card__desc {
color: rgba(255, 255, 255, 0.3);
}
/* 右上角状态标签 */
.card__status {
position: absolute;
top: 10px;
right: 10px;
font-size: 10px;
line-height: 1;
padding: 4px 8px;
border-radius: 9px;
font-weight: 600;
letter-spacing: 0.3px;
}
.card__status--done {
background: rgba(34, 197, 94, 0.18);
color: #4ade80;
border: 1px solid rgba(34, 197, 94, 0.35);
}
.card__status--pending {
background: rgba(234, 88, 12, 0.18);
color: #fb923c;
border: 1px solid rgba(234, 88, 12, 0.35);
}
.top-bar { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.back-btn { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); text-decoration: none; transition: color 0.2s; }
.back-btn:hover { color: #fff; }
.top-bar__title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.main { display: flex; align-items: center; justify-content: center; height: calc(100% - 44px); }
.placeholder { text-align: center; color: rgba(255,255,255,0.3); }
.placeholder__icon { font-size: 48px; margin-bottom: 16px; }
.placeholder__text { font-size: 14px; }
</style>
</head>
<body>
<div class="top-bar">
<span class="top-bar__title">结算设备</span>
</div>
<div class="main">
<div class="placeholder">
<div class="placeholder__icon">&#x1F6A7;</div>
<div class="placeholder__text">结算设备 &#x9875;&#x9762;&#x5F85;&#x5F00;&#x53D1;</div>
<div class="device-frame">
<!-- 顶部导航 -->
<div class="top-bar">
<span class="top-bar__title">结算设备</span>
</div>
<!-- 主内容:三菜单卡片 -->
<div class="main-content">
<!-- 1. 员工登录(待开发) -->
<a class="card card--disabled" href="javascript:void(0)">
<span class="card__status card__status--pending">待开发</span>
<div class="card__icon">&#x1F510;</div>
<div class="card__title">员工登录</div>
<div class="card__desc">人脸识别 / 账号密码登录</div>
</a>
<!-- 2. 结算台(待开发) -->
<a class="card card--disabled" href="javascript:void(0)">
<span class="card__status card__status--pending">待开发</span>
<div class="card__icon">&#x1F4B3;</div>
<div class="card__title">结算台</div>
<div class="card__desc">智能结算与餐卡支付</div>
</a>
<!-- 3. 打印小票(已设计) -->
<a class="card" href="02-print-receipt.html">
<span class="card__status card__status--done">已设计</span>
<div class="card__icon">&#x1F5A8;&#xFE0F;</div>
<div class="card__title">打印小票</div>
<div class="card__desc">餐后结算小票预览与打印</div>
</a>
</div>
</div>
</body>
</html>
</html>
+3 -3
View File
@@ -264,16 +264,16 @@
<div class="card__desc">档口菜品营养分析与称重结算</div>
</a>
<a class="card" href="checkout-device/index.html">
<span class="card__status card__status--none">无需设计</span>
<span class="card__status card__status--done">设计</span>
<div class="card__icon">&#x1F9FE;</div>
<div class="card__name">结算设备</div>
<div class="card__desc">智能结算与支付</div>
</a>
<a class="card" href="nutrition-score-screen/index.html">
<span class="card__status card__status--none">无需设计</span>
<span class="card__status card__status--done">设计</span>
<div class="card__icon">&#x1F4CA;</div>
<div class="card__name">营养得分屏</div>
<div class="card__desc">个人营养评分与健康建议</div>
<div class="card__desc">实时营养得分看板、用户排行榜与餐品热度</div>
</a>
<a class="card" href="led-food-display/index.html">
<span class="card__status card__status--done">已设计</span>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

@@ -8,244 +8,30 @@
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #030303;
color: #fff;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.led-scroll {
width: 100vw;
.led-display {
overflow-x: auto;
overflow-y: hidden;
display: flex;
align-items: center;
scrollbar-width: none;
}
.led-scroll::-webkit-scrollbar { display: none; }
.led-strip {
display: flex;
width: 4800px;
height: 312px;
background: #060606;
overflow: hidden;
flex-shrink: 0;
scrollbar-width: thin;
scrollbar-color: #333 #060606;
}
/* ---- 格子 ---- */
.led-cell {
height: 312px;
flex-shrink: 0;
position: relative;
background-size: cover;
background-position: center;
display: flex;
align-items: flex-end;
justify-content: center;
}
/* LOGO 格 */
.led-cell--logo {
width: 360px;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: #1a1a1a;
font-size: 28px;
font-weight: 800;
letter-spacing: 4px;
text-align: center;
line-height: 1.4;
}
/* 餐品格 */
.led-cell--food {
width: 292px;
}
/* ---- 格间分割条 ---- */
.led-divider {
width: 4px;
height: 312px;
flex-shrink: 0;
background: #0a0a0a;
position: relative;
}
.led-divider::before {
content: '';
position: absolute;
left: 1px;
top: 16px;
bottom: 16px;
width: 1px;
background: linear-gradient(180deg,
transparent 0%,
rgba(255,255,255,0.12) 15%,
rgba(255,255,255,0.2) 50%,
rgba(255,255,255,0.12) 85%,
transparent 100%);
}
/* ---- 底部信息卡片 ---- */
.info-card {
width: calc(100% - 12px);
margin-bottom: 8px;
background: rgba(255,255,255,0.88);
backdrop-filter: blur(6px);
border-radius: 10px;
padding: 8px 10px 10px;
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
}
.info-card__name {
font-size: 22px;
font-weight: 800;
color: #1a1a1a;
letter-spacing: 1.5px;
text-align: center;
line-height: 1.2;
}
.info-card__tags {
display: flex;
gap: 5px;
flex-wrap: wrap;
justify-content: center;
}
.info-card__tag {
padding: 2px 8px;
border-radius: 20px;
font-size: 10px;
font-weight: 600;
color: #fff;
letter-spacing: 0.3px;
white-space: nowrap;
background: #4caf50;
}
.info-card__stats {
display: flex;
gap: 16px;
align-items: baseline;
}
.info-card__stat {
display: flex;
align-items: baseline;
gap: 1px;
}
.info-card__stat-num {
font-size: 22px;
font-weight: 800;
color: #1a1a1a;
line-height: 1;
}
.info-card__stat-unit {
font-size: 10px;
font-weight: 500;
color: rgba(0,0,0,0.4);
margin-left: 1px;
}
.info-card__stat-sep {
width: 1px;
height: 14px;
background: rgba(0,0,0,0.1);
align-self: center;
.led-display img {
display: block;
height: 50vh;
width: auto;
}
</style>
</head>
<body>
<div class="led-scroll">
<div class="led-strip" id="ledStrip">
<div class="led-cell led-cell--logo">欢迎光临,数字味道</div>
</div>
<div class="led-display">
<img src="img/led-v2.jpg" alt="LED餐品展示">
</div>
<script>
/*
* 15 道餐品背景:
* - img: 真实菜品图 URL(优先)
* - bg: 菜品专属渐变色(图片未提供时的默认背景)
* 有了真实原图后,将图片放入 img/ 目录,改 img 为本地路径即可生效。
*/
var foodList = [
{ name:'宫保鸡丁', price:6.8, calorie:156, tags:['高蛋白质','富含硒','增强免疫力'],
bg:'', img:'img/01-宫保鸡丁/u=1690708677,295588643&fm=253&app=138&f=JPEG.jpg' },
{ name:'鱼香肉丝', price:7.2, calorie:142, tags:['高蛋白质','低脂肪','富含铁'],
bg:'', img:'img/02-鱼香肉丝/443533a7bdbe4a1d862b93aa3f41defa.webp' },
{ name:'番茄炒蛋', price:4.5, calorie:98, tags:['高蛋白质','富含维C','抗氧化'],
bg:'', img:'img/03-番茄炒蛋/u=966521940,2056770917&fm=3074&app=3074&f=JPEG.jpg' },
{ name:'麻婆豆腐', price:3.8, calorie:85, tags:['高蛋白质','富含钙','低胆固醇'],
bg:'', img:'img/04-麻婆豆腐/v2-75bda6021dc7ea0b7ebffea61b37a35d_r.jpg' },
{ name:'红烧肉', price:9.5, calorie:242, tags:['高蛋白质','富含铁','补气血'],
bg:'', img:'img/05-红烧肉/2f738bd4b31c8701f98edc526f3b863f0708ff1a.webp' },
{ name:'清炒时蔬', price:3.0, calorie:45, tags:['低热量','富含纤维','高维生素'],
bg:'', img:'img/06-清炒时蔬/06-清炒时蔬.jpg' },
{ name:'糖醋排骨', price:8.8, calorie:198, tags:['高蛋白质','富含钙','强筋健骨'],
bg:'', img:'img/07-糖醋排骨/e1fe9925bc315c60e16d9b1ef1be4e0348547763.webp' },
{ name:'水煮鱼', price:7.5, calorie:128, tags:['高蛋白质','富含Omega3','低脂肪'],
bg:'', img:'img/08-水煮鱼/下载.jpg' },
{ name:'回锅肉', price:8.0, calorie:185, tags:['高蛋白质','富含铁','增强体力'],
bg:'', img:'img/09-回锅肉/u=383043788,2156989392&fm=253&app=138&f=JPEG.jpg' },
{ name:'蒜蓉西兰花', price:4.2, calorie:52, tags:['低热量','富含维C','高纤维'],
bg:'', img:'img/10-蒜蓉西兰花/8d7cb3525ac74301bc86f074af8cc13b.webp' },
{ name:'干煸豆角', price:3.5, calorie:62, tags:['低热量','富含纤维','高蛋白'],
bg:'', img:'img/11-干煸豆角/12-酸辣土豆丝.png' },
{ name:'酸辣土豆丝', price:2.8, calorie:55, tags:['低脂肪','富含维C','高纤维'],
bg:'', img:'img/12-酸辣土豆丝/u=18640018,2947141880&fm=3074&app=3074&f=JPEG.jpg' },
{ name:'口水鸡', price:7.8, calorie:168, tags:['高蛋白质','低脂肪','富含硒'],
bg:'', img:'img/13-口水鸡/u=873728758,3455945663&fm=253&app=120&f=JPEG.jpg' },
{ name:'葱爆牛肉', price:8.5, calorie:178, tags:['高蛋白质','富含铁','增强体力'],
bg:'', img:'img/14-葱爆牛肉/0.png' },
{ name:'地三鲜', price:3.2, calorie:72, tags:['低热量','富含纤维','高维生素'],
bg:'', img:'img/15-地三鲜/42d50814ed454bd8b2fede901de64808.webp' }
];
var strip = document.getElementById('ledStrip');
foodList.forEach(function(food) {
/* 每个餐品格前插入分割条 */
var divider = document.createElement('div');
divider.className = 'led-divider';
strip.appendChild(divider);
var cell = document.createElement('div');
cell.className = 'led-cell led-cell--food';
/* 有菜品图用图,没有则用专属渐变色 */
cell.style.background = food.img
? 'url(' + food.img + ') center/cover no-repeat'
: food.bg;
var tagsHtml = food.tags.map(function(t) {
return '<span class="info-card__tag">' + t + '</span>';
}).join('');
cell.innerHTML =
'<div class="info-card">' +
'<div class="info-card__name">' + food.name + '</div>' +
'<div class="info-card__tags">' + tagsHtml + '</div>' +
'<div class="info-card__stats">' +
'<div class="info-card__stat">' +
'<span class="info-card__stat-num">&yen;' + food.price.toFixed(1) + '</span>' +
'<span class="info-card__stat-unit">/100g</span>' +
'</div>' +
'<div class="info-card__stat-sep"></div>' +
'<div class="info-card__stat">' +
'<span class="info-card__stat-num">' + food.calorie + '</span>' +
'<span class="info-card__stat-unit">kcal/100g</span>' +
'</div>' +
'</div>' +
'</div>';
strip.appendChild(cell);
});
</script>
</body>
</html>
@@ -6,29 +6,840 @@
<title>营养得分屏 - 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
html, body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden;
color: #fff;
}
.top-bar { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.back-btn { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); text-decoration: none; transition: color 0.2s; }
.back-btn:hover { color: #fff; }
.top-bar__title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.main { display: flex; align-items: center; justify-content: center; height: calc(100% - 44px); }
.placeholder { text-align: center; color: rgba(255,255,255,0.3); }
.placeholder__icon { font-size: 48px; margin-bottom: 16px; }
.placeholder__text { font-size: 14px; }
body {
background: #05080f;
min-height: 100vh;
min-width: 1280px;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
/* PAD 设备外壳 */
.device-frame {
width: 1280px;
height: 720px;
border-radius: 24px;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
background: radial-gradient(ellipse at center, #16305c 0%, #0c1a36 55%, #060d20 100%);
border: 1px solid rgba(64, 158, 255, 0.35);
box-shadow:
0 24px 64px rgba(0, 0, 0, 0.6),
0 0 0 1px rgba(64, 158, 255, 0.25),
inset 0 0 80px rgba(64, 158, 255, 0.08);
}
/* ============ 顶部信息栏 ============ */
.top-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 70px;
padding: 0 28px;
background: linear-gradient(180deg, rgba(15, 38, 78, 0.85) 0%, rgba(10, 26, 56, 0.4) 100%);
border-bottom: 1px solid rgba(64, 158, 255, 0.25);
flex-shrink: 0;
}
.top-left {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 220px;
}
.top-left__date {
font-size: 16px;
font-weight: 500;
color: rgba(255, 255, 255, 0.95);
letter-spacing: 0.5px;
}
.top-left__week {
font-size: 12px;
color: rgba(150, 200, 255, 0.7);
}
.top-center {
display: flex;
gap: 56px;
align-items: center;
}
.kpi {
text-align: center;
position: relative;
}
.kpi__value {
font-size: 38px;
font-weight: 700;
color: #ff8a3c;
line-height: 1;
font-family: "DIN Alternate", "Microsoft YaHei", sans-serif;
text-shadow: 0 0 18px rgba(255, 138, 60, 0.35);
}
.kpi__value span.unit {
font-size: 14px;
font-weight: 400;
color: rgba(255, 200, 150, 0.85);
margin-left: 4px;
}
.kpi__label {
font-size: 12px;
color: rgba(180, 210, 255, 0.7);
margin-top: 6px;
letter-spacing: 1px;
}
.kpi + .kpi::before {
content: "";
position: absolute;
left: -28px;
top: 8px;
bottom: 8px;
width: 1px;
background: linear-gradient(180deg, transparent, rgba(64, 158, 255, 0.3), transparent);
}
.top-right {
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
color: rgba(255, 255, 255, 0.85);
min-width: 220px;
justify-content: flex-end;
}
.top-right__city { font-weight: 500; }
.top-right__divider {
width: 1px;
height: 14px;
background: rgba(64, 158, 255, 0.3);
}
.top-right__weather {
display: inline-flex;
align-items: center;
gap: 4px;
}
.top-right__meal {
padding: 3px 10px;
background: rgba(255, 138, 60, 0.15);
border: 1px solid rgba(255, 138, 60, 0.4);
border-radius: 12px;
font-size: 12px;
color: #ffb380;
}
/* ============ 实时播报横条(贯穿全屏横向滚动) ============ */
.ticker {
flex-shrink: 0;
height: 44px;
display: flex;
align-items: stretch;
background: linear-gradient(90deg, rgba(255, 138, 60, 0.18) 0%, rgba(255, 138, 60, 0.04) 30%, rgba(64, 158, 255, 0.04) 100%);
border-bottom: 1px solid rgba(64, 158, 255, 0.22);
overflow: hidden;
position: relative;
}
.ticker__label {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 6px;
padding: 0 16px 0 22px;
background: linear-gradient(90deg, rgba(255, 138, 60, 0.35) 0%, rgba(255, 138, 60, 0.1) 100%);
border-right: 1px solid rgba(255, 138, 60, 0.3);
font-size: 13px;
font-weight: 600;
color: #ffd9b0;
letter-spacing: 1px;
}
.ticker__track {
flex: 1;
display: flex;
align-items: center;
overflow: hidden;
position: relative;
mask-image: linear-gradient(90deg, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 40px, #000 calc(100% - 40px), transparent 100%);
}
.ticker__content {
display: flex;
align-items: center;
gap: 24px;
padding-left: 24px;
white-space: nowrap;
animation: ticker-scroll 60s linear infinite;
}
.ticker:hover .ticker__content { animation-play-state: paused; }
@keyframes ticker-scroll {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.ticker__item {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 13.5px;
color: rgba(255, 255, 255, 0.9);
}
.ticker__rank {
width: 22px;
height: 22px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, #1e3f7a, #122752);
border: 1px solid rgba(64, 158, 255, 0.45);
color: #8ec5ff;
}
.ticker__rank.top1 { background: linear-gradient(135deg, #ffd700, #ff9500); color: #fff; border-color: rgba(255,215,0,0.6); }
.ticker__rank.top2 { background: linear-gradient(135deg, #d0d5e0, #98a0b0); color: #fff; border-color: rgba(192,197,208,0.6); }
.ticker__rank.top3 { background: linear-gradient(135deg, #cd7f32, #8b5a2b); color: #fff; border-color: rgba(205,127,50,0.6); }
.ticker__name.anonymous { color: rgba(180, 200, 230, 0.5); font-style: italic; }
.ticker__score {
color: #ff8a3c;
font-weight: 700;
font-family: "DIN Alternate", "Microsoft YaHei", sans-serif;
}
.ticker__time {
color: rgba(180, 210, 255, 0.55);
font-size: 12px;
font-variant-numeric: tabular-nums;
}
.ticker__sep {
color: rgba(64, 158, 255, 0.3);
}
/* ============ 主内容区 ============ */
.main {
flex: 1;
display: grid;
grid-template-columns: 1.35fr 1fr;
gap: 16px;
padding: 14px 22px;
min-height: 0;
}
/* 通用面板 */
.panel {
background: linear-gradient(180deg, rgba(20, 50, 100, 0.55) 0%, rgba(12, 30, 65, 0.55) 100%);
border: 1px solid rgba(64, 158, 255, 0.22);
border-radius: 10px;
padding: 12px 14px;
display: flex;
flex-direction: column;
min-height: 0;
}
.panel__title {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: #fff;
letter-spacing: 1px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(64, 158, 255, 0.18);
margin-bottom: 6px;
flex-shrink: 0;
}
.panel__title::before {
content: "";
width: 3px;
height: 14px;
background: linear-gradient(180deg, #ff8a3c, #ffb380);
border-radius: 2px;
}
.panel__title em {
font-style: normal;
color: rgba(180, 210, 255, 0.6);
font-weight: 400;
font-size: 12px;
margin-left: auto;
}
/* === 左栏:排行榜(纵向滚动) === */
.rank-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.rank-table thead th {
font-size: 12px;
font-weight: 500;
color: rgba(180, 210, 255, 0.65);
text-align: left;
padding: 4px 8px 6px;
border-bottom: 1px solid rgba(64, 158, 255, 0.18);
letter-spacing: 0.5px;
position: sticky;
top: 0;
background: linear-gradient(180deg, rgba(20, 50, 100, 0.95), rgba(15, 38, 78, 0.95));
z-index: 2;
}
.rank-table tbody td {
padding: 5px 8px;
font-size: 13px;
color: rgba(255, 255, 255, 0.88);
border-bottom: 1px solid rgba(64, 158, 255, 0.06);
}
.rank-table .col-rank { width: 56px; text-align: center; }
.rank-table .col-score { width: 90px; text-align: right; }
.rank-table .col-time { width: 100px; text-align: right; }
.rank-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
background: linear-gradient(135deg, #1e3f7a, #122752);
border: 1px solid rgba(64, 158, 255, 0.45);
font-size: 12px;
font-weight: 700;
color: #8ec5ff;
}
.rank-badge.top1 {
background: linear-gradient(135deg, #ffd700, #ff9500);
border-color: rgba(255, 215, 0, 0.6);
color: #fff;
box-shadow: 0 0 8px rgba(255, 215, 0, 0.35);
}
.rank-badge.top2 {
background: linear-gradient(135deg, #d0d5e0, #98a0b0);
border-color: rgba(192, 197, 208, 0.6);
color: #fff;
}
.rank-badge.top3 {
background: linear-gradient(135deg, #cd7f32, #8b5a2b);
border-color: rgba(205, 127, 50, 0.6);
color: #fff;
}
.user-cell {
display: flex;
align-items: center;
gap: 8px;
}
.avatar {
width: 26px;
height: 26px;
border-radius: 50%;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
color: #fff;
}
.avatar.c1 { background: linear-gradient(135deg, #ff7a8a, #ff4d6d); }
.avatar.c2 { background: linear-gradient(135deg, #ffb168, #ff8a3c); }
.avatar.c3 { background: linear-gradient(135deg, #6ec5ff, #409eff); }
.avatar.c4 { background: linear-gradient(135deg, #b07cff, #7c4dff); }
.avatar.c5 { background: linear-gradient(135deg, #5cd6a8, #2bb673); }
.avatar.c6 { background: linear-gradient(135deg, #ffd166, #f4a623); }
.avatar.c-anon {
background: rgba(120, 140, 170, 0.25);
border: 1px dashed rgba(180, 200, 230, 0.4);
color: rgba(180, 200, 230, 0.5);
}
.user-name { font-size: 13px; }
.user-name.anonymous { color: rgba(180, 200, 230, 0.45); font-style: italic; }
.score-cell {
font-size: 16px;
font-weight: 700;
color: #ff8a3c;
font-family: "DIN Alternate", "Microsoft YaHei", sans-serif;
}
.score-cell.low { color: #ffc183; }
.time-cell {
color: rgba(200, 220, 255, 0.75);
font-variant-numeric: tabular-nums;
font-size: 12.5px;
}
/* 纵向循环滚动容器 */
.rank-scroller {
flex: 1;
overflow: hidden;
position: relative;
min-height: 0;
}
.rank-scroller__inner {
animation: rank-scroll 40s linear infinite;
}
.rank-scroller:hover .rank-scroller__inner { animation-play-state: paused; }
@keyframes rank-scroll {
0% { transform: translateY(0); }
100% { transform: translateY(-50%); }
}
/* 行交替背景 */
.rank-scroller__inner tr:nth-child(odd) td { background: rgba(64, 158, 255, 0.04); }
.rank-scroller__inner tr:nth-child(even) td { background: rgba(64, 158, 255, 0.01); }
/* === 右栏 === */
.right-col {
display: grid;
grid-template-rows: 1fr auto;
gap: 14px;
min-height: 0;
}
/* 餐品热度 */
.hot-list {
display: flex;
flex-direction: column;
gap: 9px;
overflow: hidden;
}
.hot-item {
display: grid;
grid-template-columns: 22px 1fr 56px;
align-items: center;
gap: 10px;
}
.hot-item__rank {
font-size: 13px;
font-weight: 700;
color: rgba(255, 255, 255, 0.55);
text-align: center;
font-family: "DIN Alternate", "Microsoft YaHei", sans-serif;
}
.hot-item__rank.top { color: #ff8a3c; }
.hot-item__bar-wrap {
position: relative;
height: 28px;
background: rgba(64, 158, 255, 0.08);
border-radius: 14px;
overflow: hidden;
}
.hot-item__bar {
position: absolute;
left: 0; top: 0; bottom: 0;
border-radius: 14px;
display: flex;
align-items: center;
padding-left: 12px;
font-size: 12.5px;
color: #fff;
font-weight: 500;
white-space: nowrap;
box-shadow: 0 0 12px rgba(0, 0, 0, 0.2) inset;
}
.hot-item__bar.b1 { background: linear-gradient(90deg, #ff4d6d, #ff7a8a); }
.hot-item__bar.b2 { background: linear-gradient(90deg, #ff7a3c, #ffa168); }
.hot-item__bar.b3 { background: linear-gradient(90deg, #ffc83c, #ffd968); color: #5b3b00; }
.hot-item__bar.b4 { background: linear-gradient(90deg, #7cd166, #a5e58f); color: #1e4d10; }
.hot-item__bar.b5 { background: linear-gradient(90deg, #5cc6a8, #8adcc4); color: #0c3a2a; }
.hot-item__bar.b6 { background: linear-gradient(90deg, #4cb5d6, #82cee0); color: #0a3040; }
.hot-item__count {
font-size: 12px;
color: rgba(200, 220, 255, 0.7);
text-align: right;
font-variant-numeric: tabular-nums;
}
.hot-item__count strong {
color: #ffb380;
font-size: 14px;
font-weight: 700;
font-family: "DIN Alternate", "Microsoft YaHei", sans-serif;
}
/* 营养健康宣传图 */
.promo {
background: linear-gradient(135deg, #d8f3c4 0%, #a8e088 60%, #7ed06d 100%);
border-radius: 10px;
padding: 12px 16px;
display: grid;
grid-template-columns: 1fr 100px;
gap: 10px;
align-items: center;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 6px 18px rgba(126, 208, 109, 0.25);
position: relative;
overflow: hidden;
}
.promo::before {
content: "";
position: absolute;
top: -30px;
right: -30px;
width: 100px;
height: 100px;
background: radial-gradient(circle, rgba(255, 255, 255, 0.4) 0%, transparent 70%);
border-radius: 50%;
}
.promo__title {
font-size: 20px;
font-weight: 700;
color: #1f4d10;
letter-spacing: 1px;
margin-bottom: 4px;
}
.promo__subtitle {
font-size: 12px;
color: #2d5a1c;
margin-bottom: 8px;
}
.promo__tags {
display: flex;
gap: 6px;
}
.promo__tag {
padding: 3px 9px;
background: rgba(255, 255, 255, 0.65);
border-radius: 12px;
font-size: 11px;
color: #1f4d10;
font-weight: 600;
letter-spacing: 1px;
}
.promo__plate {
width: 100px;
height: 100px;
position: relative;
}
.promo__plate .plate {
position: absolute;
inset: 0;
border-radius: 50%;
background: linear-gradient(135deg, #ffffff 0%, #e8e8e8 100%);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), inset 0 0 0 6px #f5f5f5;
}
.promo__plate .food {
position: absolute;
border-radius: 50%;
}
.promo__plate .f1 {
width: 38px; height: 38px;
top: 16px; left: 16px;
background: radial-gradient(circle at 30% 30%, #6bbf3a, #3d8b1f);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.promo__plate .f2 {
width: 32px; height: 32px;
top: 20px; right: 14px;
background: radial-gradient(circle at 30% 30%, #ff6b6b, #c92a2a);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.promo__plate .f3 {
width: 36px; height: 36px;
bottom: 12px; left: 28px;
background: radial-gradient(circle at 30% 30%, #fffef0, #e8e2c8);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.promo__plate .f4 {
width: 26px; height: 26px;
bottom: 20px; right: 20px;
background: radial-gradient(circle at 30% 30%, #ff9500, #cc6600);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
/* ============ 底部品牌 ============ */
.footer {
height: 32px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
font-size: 12px;
color: rgba(150, 180, 220, 0.5);
letter-spacing: 2px;
border-top: 1px solid rgba(64, 158, 255, 0.12);
background: rgba(8, 18, 40, 0.5);
flex-shrink: 0;
}
.footer__brand {
color: rgba(180, 210, 255, 0.7);
font-weight: 500;
}
.footer__sep {
color: rgba(64, 158, 255, 0.3);
}
.panel { scrollbar-width: none; }
.panel::-webkit-scrollbar { display: none; }
</style>
</head>
<body>
<div class="top-bar">
<span class="top-bar__title">营养得分屏</span>
</div>
<div class="main">
<div class="placeholder">
<div class="placeholder__icon">&#x1F6A7;</div>
<div class="placeholder__text">营养得分屏 &#x9875;&#x9762;&#x5F85;&#x5F00;&#x53D1;</div>
<div class="device-frame">
<!-- ====== 顶部信息栏 ====== -->
<div class="top-bar">
<div class="top-left">
<div class="top-left__date">2026年3月9日 12:32:56</div>
<div class="top-left__week">周一 · 第二职工食堂</div>
</div>
<div class="top-center">
<div class="kpi">
<div class="kpi__value">108<span class="unit"></span></div>
<div class="kpi__label">用餐总人数</div>
</div>
<div class="kpi">
<div class="kpi__value">919<span class="unit">千卡</span></div>
<div class="kpi__label">人均热量</div>
</div>
<div class="kpi">
<div class="kpi__value">80<span class="unit"></span></div>
<div class="kpi__label">人均营养得分</div>
</div>
</div>
<div class="top-right">
<span class="top-right__city">西安</span>
<span class="top-right__weather">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="4.5" fill="#ffd23f"/>
<g stroke="#ffd23f" stroke-width="1.8" stroke-linecap="round">
<line x1="12" y1="2" x2="12" y2="4.5"/>
<line x1="12" y1="19.5" x2="12" y2="22"/>
<line x1="2" y1="12" x2="4.5" y2="12"/>
<line x1="19.5" y1="12" x2="22" y2="12"/>
<line x1="4.5" y1="4.5" x2="6.3" y2="6.3"/>
<line x1="17.7" y1="17.7" x2="19.5" y2="19.5"/>
<line x1="4.5" y1="19.5" x2="6.3" y2="17.7"/>
<line x1="17.7" y1="6.3" x2="19.5" y2="4.5"/>
</g>
</svg>
2-7℃
</span>
<span class="top-right__divider"></span>
<span class="top-right__meal">午餐 10:30-13:30</span>
</div>
</div>
<!-- ====== 实时播报横条(横向滚动跑马灯,贯穿全屏) ====== -->
<div class="ticker">
<div class="ticker__label">
<svg width="14" height="14" viewBox="0 0 24 24" fill="#ff8a3c">
<path d="M3 10v4a1 1 0 0 0 1 1h3l4 4V5L7 9H4a1 1 0 0 0-1 1z"/>
<path d="M16 8a4 4 0 0 1 0 8" stroke="#ff8a3c" stroke-width="2" fill="none" stroke-linecap="round"/>
</svg>
实时播报
</div>
<div class="ticker__track">
<div class="ticker__content" id="tickerContent">
<!-- 内容由 JS 注入两份实现无缝滚动 -->
</div>
</div>
</div>
<!-- ====== 主内容区 ====== -->
<div class="main">
<!-- ===== 左栏:用户排行榜(纵向循环滚动) ===== -->
<div class="panel">
<div class="panel__title">
用户营养得分排行榜
<em>实时滚动 · 已用餐用户</em>
</div>
<table class="rank-table">
<thead>
<tr>
<th class="col-rank">排名</th>
<th>用户</th>
<th class="col-score">健康得分</th>
<th class="col-time">下单时间</th>
</tr>
</thead>
</table>
<div class="rank-scroller">
<table class="rank-table rank-scroller__inner" id="rankTable">
<tbody id="rankBody">
<!-- 内容由 JS 注入两份实现无缝滚动 -->
</tbody>
</table>
</div>
</div>
<!-- ===== 右栏 ===== -->
<div class="right-col">
<!-- 餐品热度排行榜 -->
<div class="panel">
<div class="panel__title">
餐品热度排行榜
<em>今日下单 TOP 6</em>
</div>
<div class="hot-list">
<div class="hot-item">
<div class="hot-item__rank top">1</div>
<div class="hot-item__bar-wrap">
<div class="hot-item__bar b1" style="width: 96%;">酸汤肥牛</div>
</div>
<div class="hot-item__count"><strong>96</strong></div>
</div>
<div class="hot-item">
<div class="hot-item__rank top">2</div>
<div class="hot-item__bar-wrap">
<div class="hot-item__bar b2" style="width: 82%;">蘑菇炒油菜</div>
</div>
<div class="hot-item__count"><strong>82</strong></div>
</div>
<div class="hot-item">
<div class="hot-item__rank top">3</div>
<div class="hot-item__bar-wrap">
<div class="hot-item__bar b3" style="width: 71%;">芹菜炒豆干</div>
</div>
<div class="hot-item__count"><strong>71</strong></div>
</div>
<div class="hot-item">
<div class="hot-item__rank">4</div>
<div class="hot-item__bar-wrap">
<div class="hot-item__bar b4" style="width: 64%;">饱拌心灵美</div>
</div>
<div class="hot-item__count"><strong>64</strong></div>
</div>
<div class="hot-item">
<div class="hot-item__rank">5</div>
<div class="hot-item__bar-wrap">
<div class="hot-item__bar b5" style="width: 58%;">双色菜花</div>
</div>
<div class="hot-item__count"><strong>58</strong></div>
</div>
<div class="hot-item">
<div class="hot-item__rank">6</div>
<div class="hot-item__bar-wrap">
<div class="hot-item__bar b6" style="width: 52%;">韭菜炒鸡蛋</div>
</div>
<div class="hot-item__count"><strong>52</strong></div>
</div>
</div>
</div>
<!-- 营养健康宣传 -->
<div class="promo">
<div class="promo__text">
<div class="promo__title">营养健康</div>
<div class="promo__subtitle">营养好搭配,饮食有数</div>
<div class="promo__tags">
<span class="promo__tag">天然</span>
<span class="promo__tag">有机</span>
<span class="promo__tag">绿色</span>
</div>
</div>
<div class="promo__plate">
<div class="plate"></div>
<div class="food f1"></div>
<div class="food f2"></div>
<div class="food f3"></div>
<div class="food f4"></div>
</div>
</div>
</div>
</div>
<!-- ====== 底部品牌 ====== -->
<div class="footer">
<span class="footer__brand">数味智餐</span>
<span class="footer__sep">·</span>
<span>蚁熊智能餐养平台</span>
</div>
</div>
<script>
// ============ Mock 数据 ============
// 用户排行榜数据(含字段:排名/姓名首字符/姓名/头像色/是否匿名/得分/下单时间)
var rankData = [
{ rank: 1, initial: '陈', name: '陈*夏', color: 'c1', anon: false, score: 89, time: '12:35:20' },
{ rank: 2, initial: '张', name: '张*', color: 'c2', anon: false, score: 88, time: '12:33:18' },
{ rank: 3, initial: '?', name: '匿名用户', color: 'c-anon', anon: true, score: 86, time: '12:30:41' },
{ rank: 4, initial: '孙', name: '孙*', color: 'c3', anon: false, score: 84, time: '12:28:25' },
{ rank: 5, initial: '李', name: '李*欣', color: 'c4', anon: false, score: 83, time: '12:27:02' },
{ rank: 6, initial: '王', name: '王*庚', color: 'c5', anon: false, score: 80, time: '12:22:22' },
{ rank: 7, initial: '刘', name: '刘*', color: 'c6', anon: false, score: 79, time: '12:19:37' },
{ rank: 8, initial: '刘', name: '刘*', color: 'c1', anon: false, score: 77, time: '12:17:58' },
{ rank: 9, initial: '王', name: '王*', color: 'c2', anon: false, score: 76, time: '12:15:46' },
{ rank: 10, initial: '张', name: '张*', color: 'c4', anon: false, score: 72, time: '12:13:30' },
{ rank: 11, initial: '赵', name: '赵*宇', color: 'c3', anon: false, score: 70, time: '12:11:25' },
{ rank: 12, initial: '?', name: '匿名用户', color: 'c-anon', anon: true, score: 68, time: '12:09:14' },
{ rank: 13, initial: '钱', name: '钱*晨', color: 'c5', anon: false, score: 67, time: '12:07:02' },
{ rank: 14, initial: '周', name: '周*飞', color: 'c6', anon: false, score: 65, time: '12:05:58' },
{ rank: 15, initial: '吴', name: '吴*娜', color: 'c2', anon: false, score: 64, time: '12:04:10' },
{ rank: 16, initial: '郑', name: '郑*豪', color: 'c1', anon: false, score: 62, time: '12:02:33' },
{ rank: 17, initial: '冯', name: '冯*瑶', color: 'c4', anon: false, score: 60, time: '12:00:48' },
{ rank: 18, initial: '陈', name: '陈*伟', color: 'c3', anon: false, score: 58, time: '11:58:36' },
{ rank: 19, initial: '褚', name: '褚*林', color: 'c5', anon: false, score: 56, time: '11:56:20' },
{ rank: 20, initial: '卫', name: '卫*萍', color: 'c6', anon: false, score: 54, time: '11:54:05' }
];
// 头像颜色循环(用于实时播报,与排行榜同步)
function buildRankBadge(rank) {
var cls = '';
if (rank === 1) cls = ' top1';
else if (rank === 2) cls = ' top2';
else if (rank === 3) cls = ' top3';
return '<span class="rank-badge' + cls + '">' + rank + '</span>';
}
function buildAvatar(d) {
if (d.anon) {
return '<span class="avatar c-anon">?</span>';
}
return '<span class="avatar ' + d.color + '">' + d.initial + '</span>';
}
// ============ 渲染:排行榜(两份数据无缝衔接) ============
function renderRankTable() {
var body = document.getElementById('rankBody');
var html = '';
// 重复两份实现无缝循环
for (var dup = 0; dup < 2; dup++) {
for (var i = 0; i < rankData.length; i++) {
var d = rankData[i];
var scoreClass = d.score < 75 ? ' low' : '';
var nameClass = d.anon ? ' anonymous' : '';
html += '<tr>' +
'<td class="col-rank">' + buildRankBadge(d.rank) + '</td>' +
'<td><div class="user-cell">' + buildAvatar(d) +
'<span class="user-name' + nameClass + '">' + d.name + '</span></div></td>' +
'<td class="col-score"><span class="score-cell' + scoreClass + '">' + d.score + '</span></td>' +
'<td class="col-time time-cell">' + d.time + '</td>' +
'</tr>';
}
}
body.innerHTML = html;
}
// ============ 渲染:实时播报(横向滚动跑马灯) ============
function renderTicker() {
var ticker = document.getElementById('tickerContent');
var html = '';
// 重复两份实现无缝循环
for (var dup = 0; dup < 2; dup++) {
for (var i = 0; i < rankData.length; i++) {
var d = rankData[i];
var nameClass = d.anon ? ' anonymous' : '';
html += '<span class="ticker__item">' +
'<span class="ticker__rank' + (d.rank === 1 ? ' top1' : d.rank === 2 ? ' top2' : d.rank === 3 ? ' top3' : '') + '">' + d.rank + '</span>' +
'<span class="ticker__name' + nameClass + '">' + d.name + '</span>' +
'<span class="ticker__score">' + d.score + '分</span>' +
'<span class="ticker__time">' + d.time + '</span>' +
'</span>';
if (i < rankData.length - 1) {
html += '<span class="ticker__sep">·</span>';
}
}
// 两份之间加分隔
html += '<span class="ticker__sep">·</span>';
}
ticker.innerHTML = html;
}
renderRankTable();
renderTicker();
</script>
</body>
</html>
</html>
@@ -64,6 +64,26 @@
.info-value.gold { color: #d4a017; }
.info-row.editable { cursor: pointer; }
.edit-icon { font-size: 12px; font-style: normal; opacity: 0.6; }
/* 演示切换栏 */
.demo-bar {
display: flex; align-items: center; gap: 8px; padding: 10px 16px;
background: #fffbe6; border-bottom: 1px solid #ffe58f; font-size: 12px; color: #8c6d1f;
}
.demo-bar .demo-label { font-weight: 600; }
.demo-bar .demo-state { font-weight: 600; min-width: 48px; }
/* 切换开关 */
.toggle-switch { position: relative; display: inline-block; width: 44px; height: 24px; }
.toggle-switch input { display: none; }
.toggle-slider {
position: absolute; top: 0; left: 0; right: 0; bottom: 0;
background: #d9d9d9; border-radius: 24px; cursor: pointer; transition: 0.3s;
}
.toggle-slider::before {
content: ''; position: absolute; left: 3px; bottom: 3px; width: 18px; height: 18px;
background: #fff; border-radius: 50%; transition: 0.3s;
}
input:checked + .toggle-slider { background: #52c41a; }
input:checked + .toggle-slider::before { transform: translateX(20px); }
</style>
</head>
<body>
@@ -86,6 +106,15 @@
</div>
<span class="avatar-tip">点击修改头像</span>
</div>
<!-- [原型演示] 人脸认证状态切换 -->
<div class="demo-bar">
<span class="demo-label">演示切换:</span>
<label class="toggle-switch">
<input type="checkbox" id="faceToggle" onchange="toggleFaceState()">
<span class="toggle-slider"></span>
</label>
<span class="demo-state" id="demoStateLabel">未认证</span>
</div>
<!-- 信息列表 -->
<div class="info-group">
<div class="info-row"><span class="info-label">姓名</span><span class="info-value">张三</span></div>
@@ -97,9 +126,47 @@
<div class="info-row"><span class="info-label">单位</span><span class="info-value">CQ能源</span></div>
<div class="info-row"><span class="info-label">部门</span><span class="info-value">采油一厂</span></div>
<div class="info-row"><span class="info-label">会员等级</span><span class="info-value gold">黄金会员</span></div>
<!-- [交互] 已认证:点击查看人脸信息/重新录入;未认证:点击前往录入 -->
<div class="info-row editable" id="faceRow" onclick="handleFaceClick()">
<span class="info-label">人脸信息</span>
<span class="info-value" id="faceValue" style="color:#fa8c16;">未认证<i class="edit-icon"></i></span>
</div>
</div>
</div>
<div class="home-indicator"></div>
</div>
<script>
var isFaceVerified = false;
/* [交互] 切换开关:改变人脸认证状态 */
function toggleFaceState() {
isFaceVerified = document.getElementById('faceToggle').checked;
updateFaceRow();
}
/* [交互] 根据状态更新人脸信息行 */
function updateFaceRow() {
var valEl = document.getElementById('faceValue');
var labelEl = document.getElementById('demoStateLabel');
if (isFaceVerified) {
valEl.style.color = '#52c41a';
valEl.innerHTML = '已认证<i class="edit-icon"></i>';
labelEl.textContent = '已认证';
} else {
valEl.style.color = '#fa8c16';
valEl.innerHTML = '未认证<i class="edit-icon"></i>';
labelEl.textContent = '未认证';
}
}
/* [交互] 点击人脸信息行:已认证→人脸详情页;未认证→人脸录入页 */
function handleFaceClick() {
if (isFaceVerified) {
window.location.href = 'me-face-info.html';
} else {
window.location.href = 'me-face-verify.html';
}
}
</script>
</body>
</html>
@@ -0,0 +1,111 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>人脸信息 | 智能餐养小程序</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #2a2a2a; display: flex; justify-content: center; align-items: flex-start;
min-height: 100vh; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
padding: 24px 0 40px;
}
.phone-shell {
width: 390px; height: 844px; background: #fff; border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0; color: #333;
}
.status-bar .time { font-size: 15px; font-weight: 700; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px;
border-bottom: 0.5px solid #f0f0f0; flex-shrink: 0;
}
.nav-back { font-size: 20px; color: #333; text-decoration: none; width: 32px; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #333; }
.nav-placeholder { width: 32px; }
.page-content { flex: 1; overflow-y: auto; background: #f5f5f5; }
.page-content::-webkit-scrollbar { display: none; }
.home-indicator {
height: 20px; display: flex; align-items: center; justify-content: center; background: #fff; flex-shrink: 0;
}
.home-indicator::after { content: ''; width: 120px; height: 5px; background: #1a1a1a; border-radius: 3px; opacity: 0.2; }
/* 人脸照片区域 */
.face-photo-section {
background: #fff; padding: 30px 16px; display: flex; flex-direction: column;
align-items: center; margin-bottom: 8px;
}
.face-photo-wrap {
width: 120px; height: 120px; border-radius: 50%;
background: linear-gradient(135deg, #e6f7ff, #bae7ff);
display: flex; align-items: center; justify-content: center;
font-size: 60px; border: 4px solid #1677ff; margin-bottom: 10px;
position: relative;
}
.face-photo-badge {
position: absolute; bottom: 2px; right: 2px; width: 28px; height: 28px;
background: #52c41a; border-radius: 50%; display: flex; align-items: center;
justify-content: center; font-size: 14px; color: #fff;
border: 2px solid #fff;
}
/* 信息列表 */
.info-group { background: #fff; margin-bottom: 8px; }
.info-row {
display: flex; align-items: center; justify-content: space-between;
padding: 16px; border-bottom: 0.5px solid #f5f5f5;
}
.info-row:last-child { border-bottom: none; }
.info-label { font-size: 14px; color: #666; }
.info-value { font-size: 14px; color: #333; font-weight: 500; }
.info-value.green { color: #52c41a; }
/* 重新录入按钮 */
.btn-section { padding: 24px 16px; text-align: center; }
.btn-reenroll {
display: inline-block; padding: 13px 40px; border: 1px solid #1677ff;
border-radius: 22px; background: #fff; color: #1677ff;
font-size: 15px; cursor: pointer; text-decoration: none;
}
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">09:41</span>
<div class="icons"><span>📶</span><span>WiFi</span><span>🔋</span></div>
</div>
<div class="nav-bar">
<a class="nav-back" href="me-account.html"></a>
<span class="nav-title">人脸信息</span>
<span class="nav-placeholder"></span>
</div>
<div class="page-content">
<!-- 人脸照片 -->
<div class="face-photo-section">
<div class="face-photo-wrap">
🙂
<span class="face-photo-badge"></span>
</div>
</div>
<!-- 人脸信息详情 -->
<div class="info-group">
<div class="info-row"><span class="info-label">认证状态</span><span class="info-value green">已认证</span></div>
<div class="info-row"><span class="info-label">录入时间</span><span class="info-value">2026-08-01 14:30</span></div>
<div class="info-row"><span class="info-label">清晰度</span><span class="info-value green">95%</span></div>
</div>
<!-- [交互] 点击跳转人脸重新录入 -->
<div class="btn-section">
<a class="btn-reenroll" href="me-face-verify.html">重新录入</a>
</div>
</div>
<div class="home-indicator"></div>
</div>
</body>
</html>
@@ -0,0 +1,317 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>人脸认证 | 智能餐养小程序</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #2a2a2a; display: flex; justify-content: center; align-items: flex-start;
min-height: 100vh; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
padding: 24px 0 40px;
}
.phone-shell {
width: 390px; height: 844px; background: #fff; border-radius: 44px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3), inset 0 0 0 2px #e0e0e0;
overflow: hidden; position: relative; display: flex; flex-direction: column;
}
.status-bar {
height: 44px; background: #fff; display: flex; align-items: center;
justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0; color: #333;
}
.status-bar .time { font-size: 15px; font-weight: 700; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar {
height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px;
border-bottom: 0.5px solid #f0f0f0; flex-shrink: 0;
}
.nav-back { font-size: 20px; color: #333; text-decoration: none; width: 32px; }
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: 600; color: #333; }
.nav-placeholder { width: 32px; }
.page-content { flex: 1; overflow-y: auto; background: #f5f5f5; }
.page-content::-webkit-scrollbar { display: none; }
.home-indicator {
height: 20px; display: flex; align-items: center; justify-content: center; background: #fff; flex-shrink: 0;
}
.home-indicator::after { content: ''; width: 120px; height: 5px; background: #1a1a1a; border-radius: 3px; opacity: 0.2; }
/* 步骤指示器 */
.step-bar {
display: flex; align-items: center; justify-content: center; padding: 20px 40px; background: #fff;
}
.step-dot {
width: 28px; height: 28px; border-radius: 50%; background: #e8e8e8;
display: flex; align-items: center; justify-content: center;
font-size: 12px; color: #999; font-weight: 600;
}
.step-dot.active { background: #1677ff; color: #fff; }
.step-dot.done { background: #52c41a; color: #fff; }
.step-line { flex: 1; height: 2px; background: #e8e8e8; margin: 0 8px; }
.step-line.done { background: #52c41a; }
/* 主体区域 */
.verify-body {
display: flex; flex-direction: column; align-items: center; padding: 20px 16px;
}
/* 摄像头取景框 */
.camera-frame {
width: 240px; height: 320px; position: relative; margin: 0 auto 20px;
border-radius: 50% / 40%; overflow: hidden;
background: #1a1a1a; box-shadow: 0 0 0 4px rgba(22,119,255,0.3), 0 8px 32px rgba(0,0,0,0.2);
}
.camera-frame::before {
content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
border: 2px dashed rgba(255,255,255,0.3); border-radius: inherit;
margin: 12px; pointer-events: none;
}
/* 取景框四角装饰 */
.camera-corners { position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; }
.camera-corner {
position: absolute; width: 20px; height: 20px;
border-color: #1677ff; border-style: solid; border-width: 0;
}
.camera-corner--tl { top: 10px; left: 10px; border-top-width: 3px; border-left-width: 3px; border-radius: 6px 0 0 0; }
.camera-corner--tr { top: 10px; right: 10px; border-top-width: 3px; border-right-width: 3px; border-radius: 0 6px 0 0; }
.camera-corner--bl { bottom: 10px; left: 10px; border-bottom-width: 3px; border-left-width: 3px; border-radius: 0 0 0 6px; }
.camera-corner--br { bottom: 10px; right: 10px; border-bottom-width: 3px; border-right-width: 3px; border-radius: 0 0 6px 0; }
/* 人脸占位 */
.face-placeholder {
position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
display: flex; flex-direction: column; align-items: center; gap: 8px; color: rgba(255,255,255,0.5);
}
.face-placeholder .face-icon { font-size: 60px; line-height: 1; }
.face-placeholder .face-tip { font-size: 12px; }
/* 扫描线动画 */
.scan-line {
position: absolute; left: 0; right: 0; height: 2px;
background: linear-gradient(90deg, transparent, rgba(22,119,255,0.8), transparent);
animation: scanAnim 2s ease-in-out infinite;
}
@keyframes scanAnim {
0%, 100% { top: 15%; }
50% { top: 85%; }
}
/* 提示文字 */
.verify-tip {
font-size: 14px; color: #666; text-align: center; margin-bottom: 24px; line-height: 1.6;
}
.verify-tip .icon { font-size: 18px; }
/* 认证按钮 */
.btn-start {
width: 200px; padding: 14px 0; border: none; border-radius: 24px;
background: linear-gradient(135deg, #1677ff, #4096ff);
color: #fff; font-size: 16px; font-weight: 600; cursor: pointer;
box-shadow: 0 6px 20px rgba(22,119,255,0.3);
letter-spacing: 2px; display: block; margin: 0 auto; text-align: center;
text-decoration: none;
}
.btn-start:disabled {
background: #ccc; box-shadow: none; cursor: not-allowed;
}
/* 认证结果卡片 */
.result-card {
width: 100%; background: #fff; border-radius: 12px; padding: 24px 16px;
text-align: center; margin-bottom: 16px;
}
.result-card .result-icon { font-size: 56px; line-height: 1; margin-bottom: 10px; }
.result-card .result-title { font-size: 18px; font-weight: 700; color: #333; margin-bottom: 6px; }
.result-card .result-desc { font-size: 13px; color: #999; margin-bottom: 16px; }
.result-card .result-meta { display: flex; justify-content: center; gap: 20px; }
.result-card .meta-item { text-align: center; }
.result-card .meta-val { font-size: 14px; font-weight: 600; color: #333; }
.result-card .meta-label { font-size: 11px; color: #999; }
.btn-group { display: flex; gap: 12px; justify-content: center; }
.btn-outline {
padding: 12px 28px; border: 1px solid #1677ff; border-radius: 22px;
background: #fff; color: #1677ff; font-size: 14px; cursor: pointer;
text-decoration: none; display: inline-block;
}
.btn-primary {
padding: 12px 28px; border: none; border-radius: 22px;
background: linear-gradient(135deg, #1677ff, #4096ff);
color: #fff; font-size: 14px; cursor: pointer; font-weight: 500;
text-decoration: none; display: inline-block;
}
/* 注意事项 */
.notice-box {
background: #fffbe6; border: 1px solid #ffe58f; border-radius: 8px;
padding: 12px 14px; margin-top: 16px; width: 100%;
}
.notice-box .notice-title { font-size: 13px; font-weight: 600; color: #d48806; margin-bottom: 6px; }
.notice-box .notice-item { font-size: 12px; color: #8c6d1f; line-height: 1.8; padding-left: 8px; }
</style>
</head>
<body>
<div class="phone-shell">
<div class="status-bar">
<span class="time">09:41</span>
<div class="icons"><span>📶</span><span>WiFi</span><span>🔋</span></div>
</div>
<div class="nav-bar">
<a class="nav-back" href="me-home.html" id="btnBack"></a>
<span class="nav-title">人脸认证</span>
<span class="nav-placeholder"></span>
</div>
<div class="page-content">
<!-- 步骤指示器 -->
<div class="step-bar">
<div class="step-dot active" id="step1">1</div>
<div class="step-line" id="line1"></div>
<div class="step-dot" id="step2">2</div>
<div class="step-line" id="line2"></div>
<div class="step-dot" id="step3">3</div>
</div>
<!-- [交互] 阶段一:准备认证 -->
<div class="verify-body" id="phasePrepare">
<div class="camera-frame">
<div class="camera-corners">
<span class="camera-corner camera-corner--tl"></span>
<span class="camera-corner camera-corner--tr"></span>
<span class="camera-corner camera-corner--bl"></span>
<span class="camera-corner camera-corner--br"></span>
</div>
<div class="face-placeholder">
<span class="face-icon">👤</span>
<span class="face-tip">请将面部置于取景框内</span>
</div>
</div>
<p class="verify-tip">
<span class="icon">💡</span> 请确保光线充足、面部无遮挡<br>按提示完成眨眼、张嘴等动作
</p>
<!-- [交互] 点击开始人脸认证采集 -->
<button class="btn-start" onclick="startVerify()">开始认证</button>
</div>
<!-- [交互] 阶段二:认证进行中(默认隐藏) -->
<div class="verify-body" id="phaseCapturing" style="display:none;">
<div class="camera-frame" style="box-shadow: 0 0 0 4px rgba(22,119,255,0.6), 0 8px 32px rgba(0,0,0,0.2);">
<div class="camera-corners">
<span class="camera-corner camera-corner--tl" style="border-color:#52c41a;"></span>
<span class="camera-corner camera-corner--tr" style="border-color:#52c41a;"></span>
<span class="camera-corner camera-corner--bl" style="border-color:#52c41a;"></span>
<span class="camera-corner camera-corner--br" style="border-color:#52c41a;"></span>
</div>
<div class="scan-line" id="scanLine"></div>
<div class="face-placeholder">
<span class="face-icon">🙂</span>
<span class="face-tip" id="captureTip">正在采集中…</span>
</div>
</div>
<p class="verify-tip" style="color:#1677ff;font-weight:500;">
<span class="icon">📷</span> 请保持面部稳定,不要移动
</p>
<!-- [交互] 模拟采集进度,完成后自动跳转结果 -->
<div style="width:200px;height:4px;background:#e8e8e8;border-radius:2px;margin:0 auto 20px;overflow:hidden;">
<div id="progressBar" style="width:0%;height:100%;background:linear-gradient(90deg,#1677ff,#52c41a);border-radius:2px;transition:width 0.3s;"></div>
</div>
</div>
<!-- [交互] 阶段三:认证结果(默认隐藏) -->
<div class="verify-body" id="phaseResult" style="display:none;">
<div class="result-card">
<div class="result-icon" id="resultIcon"></div>
<div class="result-title" id="resultTitle">认证成功</div>
<div class="result-desc" id="resultDesc">人脸信息已录入,可用于后续快速验证</div>
<div class="result-meta">
<div class="meta-item"><div class="meta-val" id="metaTime">--</div><div class="meta-label">认证时间</div></div>
<div class="meta-item"><div class="meta-val" style="color:#52c41a;" id="metaScore">--</div><div class="meta-label">清晰度</div></div>
</div>
</div>
<div class="btn-group">
<!-- [交互] 点击重新采集人脸 -->
<button class="btn-outline" onclick="redoVerify()">重新采集</button>
<!-- [交互] 点击返回账号信息页 -->
<a class="btn-primary" href="me-account.html">完成</a>
</div>
<div class="notice-box">
<div class="notice-title">📌 温馨提示</div>
<div class="notice-item">• 人脸信息仅用于身份核验,加密存储不外泄</div>
<div class="notice-item">• 如需更新人脸信息,可在"账号信息→人脸信息"中重新录入</div>
</div>
</div>
</div>
<div class="home-indicator"></div>
</div>
<script>
/* [交互] 开始认证:切换到采集阶段,模拟采集进度 */
function startVerify() {
document.getElementById('phasePrepare').style.display = 'none';
document.getElementById('phaseCapturing').style.display = 'flex';
document.getElementById('step1').classList.remove('active');
document.getElementById('step1').classList.add('done');
document.getElementById('step1').textContent = '✓';
document.getElementById('step2').classList.add('active');
document.getElementById('line1').classList.add('done');
/* 模拟采集进度 0→100%,约3秒完成 */
var bar = document.getElementById('progressBar');
var tip = document.getElementById('captureTip');
var tips = ['正在采集中…', '请眨眼…', '请张嘴…', '请缓慢转头…'];
var idx = 0;
var p = 0;
var timer = setInterval(function() {
p += Math.random() * 12 + 4;
if (p >= 100) {
p = 100; bar.style.width = '100%';
clearInterval(timer);
tip.textContent = '采集完成 ✓';
setTimeout(showResult, 500);
} else {
bar.style.width = p + '%';
if (p > 25 && idx === 0) { tip.textContent = tips[1]; idx++; }
if (p > 50 && idx === 1) { tip.textContent = tips[2]; idx++; }
if (p > 75 && idx === 2) { tip.textContent = tips[3]; idx++; }
}
}, 180);
}
/* [交互] 显示认证结果 */
function showResult() {
document.getElementById('phaseCapturing').style.display = 'none';
document.getElementById('phaseResult').style.display = 'flex';
document.getElementById('step2').classList.remove('active');
document.getElementById('step2').classList.add('done');
document.getElementById('step2').textContent = '✓';
document.getElementById('step3').classList.add('active');
document.getElementById('line2').classList.add('done');
/* 模拟结果数据 */
var now = new Date();
document.getElementById('metaTime').textContent =
now.getFullYear()+'-'+
String(now.getMonth()+1).padStart(2,'0')+'-'+
String(now.getDate()).padStart(2,'0')+' '+
String(now.getHours()).padStart(2,'0')+':'+
String(now.getMinutes()).padStart(2,'0');
document.getElementById('metaScore').textContent = (92 + Math.floor(Math.random()*7)) + '%';
}
/* [交互] 重新采集:回到准备阶段 */
function redoVerify() {
if (!confirm('确定重新采集人脸信息吗?')) return;
document.getElementById('phaseResult').style.display = 'none';
document.getElementById('phasePrepare').style.display = 'flex';
/* 重置步骤条 */
['step1','step2','step3'].forEach(function(id) {
var el = document.getElementById(id);
el.classList.remove('done','active');
el.textContent = id === 'step1' ? '1' : id === 'step2' ? '2' : '3';
});
document.getElementById('step1').classList.add('active');
document.getElementById('line1').classList.remove('done');
document.getElementById('line2').classList.remove('done');
}
</script>
</body>
</html>
@@ -53,6 +53,16 @@
}
.user-dept { font-size: 12px; opacity: 0.8; margin-top: 3px; }
.user-extra { display: flex; gap: 12px; margin-top: 4px; font-size: 11px; opacity: 0.85; }
/* 人脸认证标签 */
.face-tag {
display: inline-flex; align-items: center; gap: 3px;
padding: 1px 7px; border-radius: 10px; font-size: 10px; font-weight: 500;
text-decoration: none; vertical-align: middle;
}
.face-tag--verified { background: rgba(82, 196, 26, 0.25); color: #d9f7be; }
.face-tag--unverified {
background: rgba(250, 140, 22, 0.25); color: #ffd591;
}
.user-arrow {
width: 28px; display: flex; align-items: center; justify-content: center;
font-size: 18px; opacity: 0.7; flex-shrink: 0; cursor: pointer;
@@ -112,10 +122,12 @@
<div class="user-meta">
<div class="user-name-row">
<span class="user-name">张三</span>
<span class="vip-badge">黄金会员</span>
<!-- [交互] 未认证时点击跳转人脸录入页;已认证时切换为 face-tag--verified -->
<a class="face-tag face-tag--unverified" href="me-face-verify.html">未认证</a>
</div>
<div class="user-dept">CQ能源 · 采油一厂</div>
<div class="user-extra">
<span class="vip-badge">黄金会员</span>
<span>积分 <b>1,280</b></span>
<span>余额 <b>¥368.50</b></span>
</div>
@@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>交易详情 | 智能餐养小程序</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #2a2a2a; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; padding: 24px 0 40px; }
.phone-shell { width: 390px; height: 844px; background: #fff; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.3), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
.status-bar { height: 44px; background: #fff; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; font-size: 12px; font-weight: 600; flex-shrink: 0; }
.status-bar .time { font-size: 15px; font-weight: 700; }
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; border-bottom: 0.5px solid #f0f0f0; flex-shrink: 0; position: relative; }
.nav-bar .back { font-size: 22px; color: #1677ff; cursor: pointer; text-decoration: none; line-height: 1; }
.nav-bar .title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; }
.page-content { flex: 1; overflow-y: auto; background: #f5f5f5; }
.page-content::-webkit-scrollbar { display: none; }
/* 交易头部 */
.detail-header { background: #fff; padding: 24px 16px; text-align: center; }
.detail-icon-wrap { width: 56px; height: 56px; border-radius: 16px; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px; }
.detail-icon-wrap.food { background: #e6f7ff; }
.detail-icon-wrap.recharge { background: #f6ffed; }
.detail-name { font-size: 16px; font-weight: 500; color: #333; margin-bottom: 8px; }
.detail-amount-big { font-size: 32px; font-weight: 700; letter-spacing: -1px; }
.detail-amount-big.expense { color: #333; }
.detail-amount-big.income { color: #52c41a; }
/* 信息卡片 */
.info-card { background: #fff; margin: 12px 16px; border-radius: 12px; overflow: hidden; }
.info-card .card-title { font-size: 13px; font-weight: 600; color: #333; padding: 14px 16px 8px; }
.info-row { display: flex; padding: 10px 16px; font-size: 14px; border-bottom: 0.5px solid #f5f5f5; }
.info-row:last-child { border-bottom: none; }
.info-label { width: 80px; color: #999; flex-shrink: 0; }
.info-value { flex: 1; color: #333; text-align: right; }
.info-value.success { color: #52c41a; }
.home-indicator { height: 34px; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 8px; background: #f5f5f5; flex-shrink: 0; }
.home-indicator::after { content: ''; width: 134px; height: 5px; background: #000; border-radius: 3px; opacity: 0.2; }
</style>
</head>
<body>
<div class="phone-shell">
<!-- 状态栏 -->
<div class="status-bar">
<span class="time">09:41</span>
<div class="icons">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M2 9l10-7 10 7v11a2 2 0 01-2 2h-4v-6H8v6H4a2 2 0 01-2-2V9z" fill="#333"/></svg>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M1 9l2-2c5.5-5.5 14-5.5 19.5 0l2 2M5 13l2-2c3.5-3.5 9-3.5 12.5 0l2 2M9 17l3-3 3 3" stroke="#333" stroke-width="2"/></svg>
<svg width="20" height="12" viewBox="0 0 20 12"><rect x="0" y="1" width="16" height="10" rx="2" stroke="#333" fill="none" stroke-width="1.2"/><rect x="2" y="3" width="10" height="6" rx="1" fill="#333"/><path d="M17 4v4a2 2 0 002-2v0a2 2 0 00-2-2z" fill="#333"/></svg>
</div>
</div>
<!-- 导航栏 -->
<div class="nav-bar">
<a class="back" href="me-wallet.html">&#8249;</a>
<span class="title">交易详情</span>
</div>
<!-- 页面内容 -->
<div class="page-content">
<!-- 交易头部 -->
<div class="detail-header">
<div class="detail-icon-wrap" id="detailIcon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-name" id="detailName">午餐消费</div>
<div class="detail-amount-big expense" id="detailAmount">-¥28.50</div>
</div>
<!-- 交易信息 -->
<div class="info-card">
<div class="card-title">交易信息</div>
<div class="info-row">
<span class="info-label">交易时间</span>
<span class="info-value" id="infoTime">2026年8月3日 11:32:15</span>
</div>
<div class="info-row">
<span class="info-label">交易单号</span>
<span class="info-value" id="infoOrderNo">NM20260803113215001</span>
</div>
<div class="info-row">
<span class="info-label">支付流水号</span>
<span class="info-value" id="infoPayNo">WX2026080311321500123456</span>
</div>
<div class="info-row">
<span class="info-label">交易类型</span>
<span class="info-value" id="infoType">消费支出</span>
</div>
<div class="info-row" id="merchantRow" style="display:none">
<span class="info-label">商户名称</span>
<span class="info-value" id="infoMerchant">二号食堂</span>
</div>
<div class="info-row">
<span class="info-label">支付方式</span>
<span class="info-value" id="infoMethod">微信支付</span>
</div>
<div class="info-row">
<span class="info-label">交易前余额</span>
<span class="info-value" id="infoBefore">¥256.50</span>
</div>
<div class="info-row">
<span class="info-label">交易后余额</span>
<span class="info-value" id="infoAfter">¥228.00</span>
</div>
<div class="info-row">
<span class="info-label">交易状态</span>
<span class="info-value success" id="infoStatus">交易成功</span>
</div>
</div>
</div>
<!-- 底部指示器 -->
<div class="home-indicator"></div>
</div>
<script>
/* 从 URL hash 解析交易参数 */
(function() {
var hash = window.location.hash.replace('#', '');
if (!hash) return;
var params = {};
hash.split('&').forEach(function(pair) {
var parts = pair.split('=');
if (parts.length === 2) {
params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
}
});
var type = params.type || 'meal';
/* 类型图标 */
var iconWrap = document.getElementById('detailIcon');
var iconSvg = iconWrap.querySelector('svg');
if (type === 'recharge') {
iconWrap.className = 'detail-icon-wrap recharge';
iconSvg.innerHTML = '<circle cx="12" cy="12" r="9" stroke="#52c41a" stroke-width="1.5"/><path d="M12 8v8M8 12h8" stroke="#52c41a" stroke-width="1.5" stroke-linecap="round"/>';
}
/* 交易名称 */
if (params.name) document.getElementById('detailName').textContent = params.name;
/* 金额 */
var amountEl = document.getElementById('detailAmount');
if (params.amount) {
amountEl.textContent = params.amount;
amountEl.className = 'detail-amount-big ' + (params.amount.indexOf('+') === 0 ? 'income' : 'expense');
}
/* 时间 */
if (params.time) document.getElementById('infoTime').textContent = params.time;
/* 单号 */
if (params.orderNo) document.getElementById('infoOrderNo').textContent = params.orderNo;
/* 支付流水号 */
if (params.payNo) document.getElementById('infoPayNo').textContent = params.payNo;
/* 交易类型文本 */
if (type === 'recharge') {
document.getElementById('infoType').textContent = '余额充值';
} else {
document.getElementById('infoType').textContent = '消费支出';
/* 消费类型显示商户名称 */
if (params.merchant) {
document.getElementById('infoMerchant').textContent = params.merchant;
document.getElementById('merchantRow').style.display = 'flex';
}
}
/* 支付方式 */
if (params.payMethod) document.getElementById('infoMethod').textContent = params.payMethod;
/* 余额 */
if (params.balance) document.getElementById('infoAfter').textContent = '¥' + params.balance;
if (params.before) document.getElementById('infoBefore').textContent = '¥' + params.before;
})();
</script>
</body>
</html>
@@ -25,14 +25,25 @@
.action-btn.primary { background: #fff; color: #1677ff; }
.action-btn.secondary { background: rgba(255,255,255,0.25); color: #fff; border: 1px solid rgba(255,255,255,0.5); }
.section-title { font-size: 15px; font-weight: 600; color: #333; padding: 16px 16px 12px; }
.transaction-list { background: #fff; margin: 0 16px; border-radius: 10px; overflow: hidden; }
.transaction-item { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 0.5px solid #f5f5f5; }
.transaction-item:last-child { border-bottom: none; }
.transaction-info .transaction-name { font-size: 15px; color: #333; }
.transaction-info .transaction-time { font-size: 12px; color: #999; margin-top: 4px; }
.transaction-amount { font-size: 15px; font-weight: 500; }
.transaction-amount.expense { color: #333; }
.transaction-amount.income { color: #52c41a; }
/* 月份分组 */
.month-group { margin: 0 16px 4px; }
.month-label { font-size: 13px; color: #999; padding: 12px 0 8px; }
/* 明细列表 */
.detail-list { background: #fff; border-radius: 10px; overflow: hidden; }
.detail-item { display: flex; align-items: center; padding: 14px 16px; border-bottom: 0.5px solid #f5f5f5; cursor: pointer; transition: background 0.15s; }
.detail-item:active { background: #f5f5f5; }
.detail-item:last-child { border-bottom: none; }
.detail-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-right: 12px; }
.detail-icon.food { background: #e6f7ff; }
.detail-icon.recharge { background: #f6ffed; }
.detail-info { flex: 1; min-width: 0; }
.detail-name { font-size: 15px; color: #333; }
.detail-time { font-size: 12px; color: #999; margin-top: 4px; }
.detail-right { text-align: right; flex-shrink: 0; margin-left: 12px; }
.detail-amount { font-size: 15px; font-weight: 500; }
.detail-amount.expense { color: #333; }
.detail-amount.income { color: #52c41a; }
.detail-balance { font-size: 12px; color: #999; margin-top: 4px; }
.home-indicator { height: 34px; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 8px; background: #f5f5f5; flex-shrink: 0; }
.home-indicator::after { content: ''; width: 134px; height: 5px; background: #000; border-radius: 3px; opacity: 0.2; }
</style>
@@ -67,36 +78,170 @@
</div>
</div>
<!-- 最近交易 -->
<div class="section-title">最近交易</div>
<div class="transaction-list">
<div class="transaction-item">
<div class="transaction-info">
<div class="transaction-name">午餐消费</div>
<div class="transaction-time">今天 11:32</div>
<!-- 余额明细 -->
<div class="section-title">余额明细</div>
<!-- 2026年8月 -->
<div class="month-group">
<div class="month-label">2026年8月</div>
<div class="detail-list">
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">午餐消费</div>
<div class="detail-time">8月3日 11:32</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥28.50</div>
<div class="detail-balance">余额 ¥228.00</div>
</div>
</div>
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">早餐消费</div>
<div class="detail-time">8月3日 07:45</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥15.00</div>
<div class="detail-balance">余额 ¥256.50</div>
</div>
</div>
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">午餐消费</div>
<div class="detail-time">8月1日 12:10</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥32.00</div>
<div class="detail-balance">余额 ¥271.50</div>
</div>
</div>
<div class="detail-item">
<div class="detail-icon recharge">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="#52c41a" stroke-width="1.5"/><path d="M12 8v8M8 12h8" stroke="#52c41a" stroke-width="1.5" stroke-linecap="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">充值</div>
<div class="detail-time">8月1日 09:00</div>
</div>
<div class="detail-right">
<div class="detail-amount income">+¥200.00</div>
<div class="detail-balance">余额 ¥303.50</div>
</div>
</div>
<div class="transaction-amount expense">-¥28.5</div>
</div>
<div class="transaction-item">
<div class="transaction-info">
<div class="transaction-name">早餐消费</div>
<div class="transaction-time">今天 07:45</div>
</div>
<!-- 2026年7月 -->
<div class="month-group">
<div class="month-label">2026年7月</div>
<div class="detail-list">
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">午餐消费</div>
<div class="detail-time">7月31日 11:45</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥35.00</div>
<div class="detail-balance">余额 ¥103.50</div>
</div>
</div>
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">晚餐消费</div>
<div class="detail-time">7月30日 18:20</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥42.00</div>
<div class="detail-balance">余额 ¥138.50</div>
</div>
</div>
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">早餐消费</div>
<div class="detail-time">7月30日 07:30</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥12.00</div>
<div class="detail-balance">余额 ¥180.50</div>
</div>
</div>
<div class="detail-item">
<div class="detail-icon recharge">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="#52c41a" stroke-width="1.5"/><path d="M12 8v8M8 12h8" stroke="#52c41a" stroke-width="1.5" stroke-linecap="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">充值</div>
<div class="detail-time">7月28日 10:15</div>
</div>
<div class="detail-right">
<div class="detail-amount income">+¥100.00</div>
<div class="detail-balance">余额 ¥192.50</div>
</div>
</div>
<div class="transaction-amount expense">-¥15.0</div>
</div>
<div class="transaction-item">
<div class="transaction-info">
<div class="transaction-name">充值</div>
<div class="transaction-time">昨天</div>
</div>
<!-- 2026年6月 -->
<div class="month-group">
<div class="month-label">2026年6月</div>
<div class="detail-list">
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">午餐消费</div>
<div class="detail-time">6月30日 12:00</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥28.00</div>
<div class="detail-balance">余额 ¥92.50</div>
</div>
</div>
<div class="transaction-amount income">+¥100.0</div>
</div>
<div class="transaction-item">
<div class="transaction-info">
<div class="transaction-name">午餐消费</div>
<div class="transaction-time">昨天</div>
<div class="detail-item">
<div class="detail-icon food">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><path d="M18 8h1a4 4 0 010 8h-1M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8zm4-4h8v4H6V4z" stroke="#1677ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">早餐消费</div>
<div class="detail-time">6月29日 08:00</div>
</div>
<div class="detail-right">
<div class="detail-amount expense">-¥10.00</div>
<div class="detail-balance">余额 ¥120.50</div>
</div>
</div>
<div class="detail-item">
<div class="detail-icon recharge">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="#52c41a" stroke-width="1.5"/><path d="M12 8v8M8 12h8" stroke="#52c41a" stroke-width="1.5" stroke-linecap="round"/></svg>
</div>
<div class="detail-info">
<div class="detail-name">充值</div>
<div class="detail-time">6月25日 14:30</div>
</div>
<div class="detail-right">
<div class="detail-amount income">+¥50.00</div>
<div class="detail-balance">余额 ¥130.50</div>
</div>
</div>
<div class="transaction-amount expense">-¥35.0</div>
</div>
</div>
</div>
@@ -104,5 +249,68 @@
<!-- 底部指示器 -->
<div class="home-indicator"></div>
</div>
<script>
/* 明细条目点击跳转至交易详情页 */
(function() {
var items = document.querySelectorAll('.detail-item');
items.forEach(function(item, index) {
item.addEventListener('click', function() {
var name = item.querySelector('.detail-name').textContent;
var amount = item.querySelector('.detail-amount').textContent;
var shortTime = item.querySelector('.detail-time').textContent;
var balance = item.querySelector('.detail-balance').textContent.replace('余额 ', '').replace('¥', '');
var icon = item.querySelector('.detail-icon');
var type = icon.classList.contains('recharge') ? 'recharge' : 'meal';
/* 从月份分组标签提取年份,拼接完整时间 */
var monthGroup = item.closest('.month-group');
var monthLabel = monthGroup.querySelector('.month-label').textContent;
var year = monthLabel.match(/\d{4}/)[0];
var timeMatch = shortTime.match(/(\d+)月(\d+)日\s+(\d+:\d+)/);
var sec = String(index * 7 % 60).padStart(2, '0');
var fullTime = year + '年' + timeMatch[1] + '月' + timeMatch[2] + '日 ' + timeMatch[3] + ':' + sec;
/* 计算交易前余额 */
var amountNum = parseFloat(amount.replace(/[^0-9.-]/g, ''));
var balanceNum = parseFloat(balance.replace(/[^0-9.]/g, ''));
var beforeNum = type === 'recharge' ? (balanceNum - amountNum) : (balanceNum - amountNum);
var before = beforeNum.toFixed(2);
/* 生成单号 */
var now = new Date();
var orderNo = 'NM' + now.getFullYear() +
String(now.getMonth() + 1).padStart(2, '0') +
String(now.getDate()).padStart(2, '0') +
String(now.getHours()).padStart(2, '0') +
String(now.getMinutes()).padStart(2, '0') +
String(now.getSeconds()).padStart(2, '0') +
String(index).padStart(3, '0');
/* 支付方式:消费用微信支付,充值交替使用不同方式 */
var payMethods = ['微信支付', '现金支付', '微信支付'];
var payMethod = type === 'meal' ? '微信支付' : payMethods[index % payMethods.length];
var params = [
'type=' + encodeURIComponent(type),
'name=' + encodeURIComponent(name),
'amount=' + encodeURIComponent(amount),
'time=' + encodeURIComponent(fullTime),
'balance=' + encodeURIComponent(balance),
'before=' + encodeURIComponent(before),
'orderNo=' + encodeURIComponent(orderNo),
'payMethod=' + encodeURIComponent(payMethod)
];
/* 消费类型添加商户名称 */
if (type === 'meal') {
params.push('merchant=' + encodeURIComponent('二号食堂'));
}
window.location.href = 'me-wallet-detail.html#' + params.join('&');
});
});
})();
</script>
</body>
</html>
</html>
+11 -1
View File
@@ -151,6 +151,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -169,7 +170,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -152,6 +152,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -170,7 +171,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -174,6 +174,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -192,7 +193,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -116,6 +116,7 @@ tr:hover td{background:#FAFAFA;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -134,7 +135,16 @@ tr:hover td{background:#FAFAFA;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -134,6 +134,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -152,7 +153,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+547
View File
@@ -0,0 +1,547 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>营养管理 - 内容中心 | 健康CQ原型</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:0.65;margin-right:2px;}
.sidebar-item.active .mi{opacity:0.9;}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{background:#fff;border-radius:var(--radius-md);padding:20px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;flex-wrap:wrap;}
.filter-item{display:flex;align-items:center;gap:8px;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;min-width:200px;}
.filter-input:focus{border-color:var(--primary);}
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;min-width:120px;}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.btn-danger{background:#fff;color:var(--danger);border:1px solid var(--danger);}.btn-danger:hover{background:#fff1f0;}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.table-card__title{font-size:var(--font-base);font-weight:600;}
.table-card__actions{display:flex;gap:8px;align-items:center;}
.table-card__content{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;}
.table-card__content table{min-width:100%;white-space:nowrap;}
table{width:100%;border-collapse:collapse;}
thead th{background:#FAFAFA;padding:12px 16px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
tbody td{padding:14px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);white-space:nowrap;}
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;white-space:nowrap;}
thead th:last-child{background:#FAFAFA;}
tbody tr:hover td.td-actions{background:#F8FBFF;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:var(--primary-light);color:var(--primary);}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-orange{background:#FFF7E6;color:var(--warning);}
.tag-red{background:#FFF1F0;color:var(--danger);}
.tag-gray{background:#F5F5F5;color:#999;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);}
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
.page-btn{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.45);z-index:1000;display:flex;align-items:center;justify-content:center;}
.modal{background:#fff;border-radius:8px;width:640px;max-height:80vh;overflow-y:auto;box-shadow:0 4px 12px rgba(0,0,0,0.15);}
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid #e8e8e8;}
.modal-title{font-size:16px;font-weight:600;color:#262626;}
.modal-close{font-size:22px;cursor:pointer;color:#8c8c8c;line-height:1;}
.modal-close:hover{color:#262626;}
.modal-body{padding:24px;}
.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid #e8e8e8;}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.form-item{display:flex;flex-direction:column;gap:4px;}
.form-item.full{grid-column:1/-1;}
.form-item label{font-size:13px;color:#8c8c8c;}
.form-item label .required{color:#ff4d4f;margin-left:2px;}
.form-item input,.form-item select,.form-item textarea{height:36px;padding:0 12px;border:1px solid #e8e8e8;border-radius:4px;font-size:13px;outline:none;width:100%;}
.form-item input:focus,.form-item select:focus,.form-item textarea:focus{border-color:#1890FF;}
.form-item textarea{height:80px;padding:8px 12px;resize:vertical;}
/* 内容类型面板切换 */
.content-tabs{display:flex;gap:0;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;}
.content-tabs__tab{flex:1;padding:8px 0;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;background:#FAFAFA;border-right:1px solid var(--border);transition:all .2s;user-select:none;}
.content-tabs__tab:last-child{border-right:none;}
.content-tabs__tab:hover{color:var(--primary);background:#fff;}
.content-tabs__tab.active{color:#fff;background:var(--primary);}
.content-panel{display:none;margin-top:12px;}
.content-panel.show{display:block;}
.upload-area{border:2px dashed #e8e8e8;border-radius:8px;padding:28px;text-align:center;color:#999;font-size:13px;cursor:pointer;transition:all .2s;}
.upload-area:hover{border-color:#1890FF;color:#1890FF;background:#F8FBFF;}
.upload-area__icon{font-size:28px;margin-bottom:6px;display:block;}
.upload-area__hint{font-size:12px;color:#bbb;margin-top:4px;}
.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:16px;}
.stat-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;}
.stat-card__icon{width:48px;height:48px;border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:24px;}
.stat-card__icon.blue{background:var(--primary-light);}
.stat-card__icon.green{background:#F6FFED;}
.stat-card__icon.orange{background:#FFF7E6;}
.stat-card__icon.red{background:#FFF1F0;}
.stat-card__info{display:flex;flex-direction:column;gap:4px;}
.stat-card__value{font-size:var(--font-xl);font-weight:700;color:var(--text-primary);}
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);}
.action-bar{display:flex;align-items:center;gap:8px;margin-bottom:16px;}
/* 页面信息栏 */
.page-header-bar{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:16px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
.page-header-bar__left{display:flex;align-items:center;gap:12px;}
.page-header-bar__title{font-size:var(--font-lg);font-weight:700;color:var(--text-primary);}
.page-header-bar__meta{font-size:var(--font-sm);color:var(--text-placeholder);}
.page-header-bar__right{display:flex;align-items:center;gap:12px;}
.page-header-bar__right label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.page-header-bar__right .canteen-select{height:36px;border:2px solid var(--primary);border-radius:var(--radius-sm);padding:0 32px 0 14px;font-size:var(--font-sm);color:var(--primary);font-weight:500;outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%231890FF'/%3E%3C/svg%3E") no-repeat right 12px center;appearance:none;cursor:pointer;min-width:140px;}
/* 右侧详情抽屉 */
.drawer-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:1000;}
.drawer-mask.show{display:block;}
.drawer{position:fixed;top:0;right:-520px;bottom:0;width:520px;background:#fff;z-index:1001;transition:right .3s ease;box-shadow:-4px 0 20px rgba(0,0,0,0.15);display:flex;flex-direction:column;}
.drawer.show{right:0;}
.drawer__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);flex-shrink:0;}
.drawer__title{font-size:var(--font-base);font-weight:600;}
.drawer__close{font-size:22px;cursor:pointer;color:var(--text-secondary);line-height:1;}.drawer__close:hover{color:var(--text-primary);}
.drawer__body{padding:24px;overflow-y:auto;flex:1;}
/* 抽屉-分区 */
.detail-section{border-top:1px dashed var(--border);padding-top:18px;margin-top:18px;}
.detail-section:first-child{border-top:none;padding-top:0;margin-top:0;}
.detail-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:14px;display:flex;align-items:center;gap:6px;}
.detail-section__title .dot{width:6px;height:6px;border-radius:50%;background:var(--primary);}
.detail-row{display:flex;margin-bottom:14px;}
.detail-label{width:100px;flex-shrink:0;font-size:var(--font-sm);color:var(--text-secondary);}
.detail-value{font-size:var(--font-sm);color:var(--text-primary);flex:1;}
/* 抽屉-内容预览 */
.drawer-preview{margin-top:12px;border-radius:8px;overflow:hidden;}
.drawer-preview img{width:100%;max-height:300px;object-fit:cover;border-radius:8px;border:1px solid var(--border);}
.drawer-preview .preview-text{padding:16px;background:#FAFAFA;border-radius:8px;font-size:var(--font-sm);line-height:1.7;white-space:pre-wrap;color:var(--text-primary);border:1px solid #f0f0f0;}
.drawer-preview .preview-video{width:100%;height:260px;background:#000;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;border-radius:8px;}
.drawer-preview .preview-html{border:1px solid var(--border);border-radius:8px;overflow:hidden;}
/* 确认弹窗 */
.confirm-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:1100;display:flex;align-items:center;justify-content:center;}
.confirm-modal{background:#fff;border-radius:8px;width:420px;box-shadow:0 4px 12px rgba(0,0,0,0.15);}
.confirm-icon{display:flex;align-items:center;justify-content:center;margin-bottom:16px;}
.confirm-icon svg{width:48px;height:48px;}
.confirm-msg{text-align:center;font-size:var(--font-base);color:var(--text-primary);margin-bottom:8px;font-weight:500;}
.confirm-sub{text-align:center;font-size:var(--font-sm);color:var(--text-secondary);}
</style>
</head>
<body>
<!-- 顶部导航 -->
<header class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
营养管理
</div>
<nav class="top-bar__nav">
<a href="trace-query.html">溯源总览</a>
<a href="arc-employee.html">档案</a>
<a href="farm-land.html">绿色种采</a>
<a href="sup-hygiene-check.html">卫生供应</a>
<a href="prod-qc-daily.html">健康生产</a>
<a href="din-dict-mgmt.html" class="active">营养用餐</a>
<a href="ano-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a href="../../index.html" class="btn-back">← 返回首页</a>
<div class="avatar"></div>
<span>系统管理员 ▾</span>
</div>
</header>
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="sidebar-menu__title">营养用餐</div>
<a href="din-canteen.html" class="sidebar-item"><span class="mi">🏢</span>场所管理</a>
<a href="din-dict-mgmt.html" class="sidebar-item"><span class="mi">📖</span>字典管理</a>
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
📐 算法标准
</div>
<div class="sidebar-subgroup__body">
<a href="din-algo-population.html" class="sidebar-item"><span class="mi">👥</span>人群基础标准</a>
<a href="din-algo-vip.html" class="sidebar-item"><span class="mi">💎</span>VIP定制标准</a>
</div>
</div>
<a href="din-recipe-mgmt.html" class="sidebar-item"><span class="mi">📋</span>食谱管理</a>
<a href="din-order-meal.html" class="sidebar-item"><span class="mi">🛒</span>营养点餐</a>
<a href="din-user-mgmt.html" class="sidebar-item"><span class="mi">🧑</span>用户管理</a>
<a href="din-order-line.html" class="sidebar-item"><span class="mi">⚖️</span>餐线管理</a>
<a href="din-order-pickup.html" class="sidebar-item"><span class="mi">🍽️</span>取餐管理</a>
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item active"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🤖 模型管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-model-llm.html" class="sidebar-item"><span class="mi">🧠</span>大模型管理</a>
<a href="din-model-small.html" class="sidebar-item"><span class="mi">⚙️</span>小模型管理</a>
</div>
</div>
<a href="din-innovation.html" class="sidebar-item"><span class="mi">🌟</span>味养创新</a>
</aside>
<!-- 主内容区 -->
<div class="main">
<!-- 页面信息栏 -->
<div class="page-header-bar">
<div class="page-header-bar__left">
<span class="page-header-bar__title">内容中心</span>
<span class="page-header-bar__meta">管理终端投放物料,支持按设备类型或指定设备配置</span>
</div>
<div class="page-header-bar__right">
<label>当前场所</label>
<select class="canteen-select" id="globalCanteen">
<option value="1">一场所(东区)</option>
<option value="2">二场所(西区)</option>
<option value="3">三场所(北区)</option>
</select>
</div>
</div>
<!-- 统计卡片 -->
<div class="stats-row">
<div class="stat-card"><div class="stat-card__icon blue">📦</div><div class="stat-card__info"><div class="stat-card__value">48</div><div class="stat-card__label">物料总数</div></div></div>
<div class="stat-card"><div class="stat-card__icon green"></div><div class="stat-card__info"><div class="stat-card__value">42</div><div class="stat-card__label">生效中</div></div></div>
<div class="stat-card"><div class="stat-card__icon red"></div><div class="stat-card__info"><div class="stat-card__value">6</div><div class="stat-card__label">已过期</div></div></div>
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item"><label>物料名称</label><input class="filter-input" type="text" placeholder="请输入物料名称"></div>
<div class="filter-item"><label>物料类型</label><select class="filter-select"><option>全部</option><option>图片</option><option>文字</option><option>HTML片段</option><option>视频</option></select></div>
<div class="filter-item"><label>关联设备类型</label><select class="filter-select"><option>全部</option><option>点餐屏</option><option>智能取餐柜</option><option>LED显示屏</option><option>餐线秤台</option></select></div>
<div class="filter-item"><label>状态</label><select class="filter-select"><option>全部</option><option>生效中</option><option>已过期</option></select></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary" onclick="openAddModal()"> 新增物料</button>
<button class="btn btn-default">🔄 刷新</button>
</div>
<!-- 数据表格 -->
<div class="table-card">
<div class="table-card__header">
<div class="table-card__title">物料列表</div>
</div>
<div class="table-card__content">
<table>
<thead><tr><th>物料编号</th><th>物料名称</th><th>物料类型</th><th>关联范围</th><th>关联设备</th><th>位置标识</th><th>排序</th><th>生效时间</th><th>到期时间</th><th>创建人</th><th>创建时间</th><th>状态</th><th>操作</th></tr></thead>
<tbody>
<tr data-id="MT-001" data-name="今日推荐-宫保鸡丁" data-type="图片" data-scope="按设备类型" data-device="点餐屏" data-position="首页横幅" data-sort="1" data-start="2026-04-22 08:00" data-end="2026-04-30 23:59" data-creator="张运营" data-ctime="2026-04-22 10:30" data-status="生效中" data-content-type="img">
<td>MT-001</td><td>今日推荐-宫保鸡丁</td><td><span class="tag tag-blue">图片</span></td><td>按设备类型</td><td>点餐屏</td><td>首页横幅</td><td>1</td><td>2026-04-22 08:00</td><td>2026-04-30 23:59</td><td>张运营</td><td>2026-04-22 10:30</td><td><span class="tag tag-green">生效中</span></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openEditModal(this)">编辑</a> <a class="link" style="color:var(--danger)" onclick="showDeleteConfirm(this)">删除</a></td>
</tr>
<tr data-id="MT-002" data-name="营养小贴士-春季养生" data-type="文字" data-scope="按设备类型" data-device="LED显示屏" data-position="底部滚动" data-sort="2" data-start="2026-04-20 08:00" data-end="2026-05-31 23:59" data-creator="张运营" data-ctime="2026-04-20 14:00" data-status="生效中" data-content-type="text">
<td>MT-002</td><td>营养小贴士-春季养生</td><td><span class="tag tag-blue">文字</span></td><td>按设备类型</td><td>LED显示屏</td><td>底部滚动</td><td>2</td><td>2026-04-20 08:00</td><td>2026-05-31 23:59</td><td>张运营</td><td>2026-04-20 14:00</td><td><span class="tag tag-green">生效中</span></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openEditModal(this)">编辑</a> <a class="link" style="color:var(--danger)" onclick="showDeleteConfirm(this)">删除</a></td>
</tr>
<tr data-id="MT-003" data-name="新品上市横幅" data-type="HTML片段" data-scope="指定设备" data-device="1号点餐屏" data-position="弹窗" data-sort="3" data-start="2026-04-25 08:00" data-end="2026-05-15 23:59" data-creator="李运营" data-ctime="2026-04-23 09:00" data-status="生效中" data-content-type="html">
<td>MT-003</td><td>新品上市横幅</td><td><span class="tag tag-blue">HTML片段</span></td><td>指定设备</td><td>1号点餐屏</td><td>弹窗</td><td>3</td><td>2026-04-25 08:00</td><td>2026-05-15 23:59</td><td>李运营</td><td>2026-04-23 09:00</td><td><span class="tag tag-green">生效中</span></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openEditModal(this)">编辑</a> <a class="link" style="color:var(--danger)" onclick="showDeleteConfirm(this)">删除</a></td>
</tr>
<tr data-id="MT-004" data-name="食堂宣传视频" data-type="视频" data-scope="指定设备" data-device="LED屏-01" data-position="全屏播放" data-sort="4" data-start="2026-04-15 08:00" data-end="2026-06-30 23:59" data-creator="王运营" data-ctime="2026-04-15 16:20" data-status="生效中" data-content-type="video">
<td>MT-004</td><td>食堂宣传视频</td><td><span class="tag tag-blue">视频</span></td><td>指定设备</td><td>LED屏-01</td><td>全屏播放</td><td>4</td><td>2026-04-15 08:00</td><td>2026-06-30 23:59</td><td>王运营</td><td>2026-04-15 16:20</td><td><span class="tag tag-green">生效中</span></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openEditModal(this)">编辑</a> <a class="link" style="color:var(--danger)" onclick="showDeleteConfirm(this)">删除</a></td>
</tr>
<tr data-id="MT-005" data-name="周一套餐海报" data-type="图片" data-scope="指定设备" data-device="智能取餐柜-01" data-position="屏幕保护" data-sort="5" data-start="2026-04-14 08:00" data-end="2026-04-20 23:59" data-creator="张运营" data-ctime="2026-04-14 11:00" data-status="已过期" data-content-type="img">
<td>MT-005</td><td>周一套餐海报</td><td><span class="tag tag-blue">图片</span></td><td>指定设备</td><td>智能取餐柜-01</td><td>屏幕保护</td><td>5</td><td>2026-04-14 08:00</td><td>2026-04-20 23:59</td><td>张运营</td><td>2026-04-14 11:00</td><td><span class="tag tag-gray">已过期</span></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openEditModal(this)">编辑</a> <a class="link" style="color:var(--danger)" onclick="showDeleteConfirm(this)">删除</a></td>
</tr>
<tr data-id="MT-006" data-name="端午活动弹窗" data-type="HTML片段" data-scope="按设备类型" data-device="点餐屏" data-position="弹窗" data-sort="6" data-start="2026-05-28 08:00" data-end="2026-06-05 23:59" data-creator="李运营" data-ctime="2026-04-23 10:00" data-status="生效中" data-content-type="html">
<td>MT-006</td><td>端午活动弹窗</td><td><span class="tag tag-blue">HTML片段</span></td><td>按设备类型</td><td>点餐屏</td><td>弹窗</td><td>6</td><td>2026-05-28 08:00</td><td>2026-06-05 23:59</td><td>李运营</td><td>2026-04-23 10:00</td><td><span class="tag tag-green">生效中</span></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openEditModal(this)">编辑</a> <a class="link" style="color:var(--danger)" onclick="showDeleteConfirm(this)">删除</a></td>
</tr>
</tbody>
</table>
</div>
<div class="pagination"><span>显示 6 条,共 48 条</span><button class="page-btn"></button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn">3</button><button class="page-btn"></button></div>
</div>
</div>
<!-- 新增/编辑物料弹窗 -->
<div class="modal-overlay" id="modalOverlay" style="display:none;">
<div class="modal">
<div class="modal-header">
<span class="modal-title" id="modalTitle">新增物料</span>
<span class="modal-close" onclick="closeModal()">&times;</span>
</div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item full">
<label>物料名称<span class="required">*</span></label>
<input type="text" id="matName" placeholder="请输入物料名称">
</div>
<div class="form-item full">
<label>物料内容<span class="required">*</span></label>
<div class="content-tabs" id="contentTabs">
<div class="content-tabs__tab active" data-panel="panelImg">图片</div>
<div class="content-tabs__tab" data-panel="panelText">文字</div>
<div class="content-tabs__tab" data-panel="panelHtml">HTML</div>
<div class="content-tabs__tab" data-panel="panelVideo">视频</div>
</div>
<div class="content-panel show" id="panelImg">
<div class="upload-area"><span class="upload-area__icon">🖼️</span>点击或拖拽上传图片<span class="upload-area__hint">支持 JPG / PNG / GIF,最大 10MB</span></div>
</div>
<div class="content-panel" id="panelText">
<textarea id="textContent" placeholder="请输入要展示的文字内容" style="height:120px;"></textarea>
</div>
<div class="content-panel" id="panelHtml">
<textarea id="htmlContent" placeholder="请输入 HTML 片段内容" style="height:140px;font-family:monospace;font-size:12px;"></textarea>
</div>
<div class="content-panel" id="panelVideo">
<div class="upload-area"><span class="upload-area__icon">🎬</span>点击或拖拽上传视频<span class="upload-area__hint">支持 MP4,最大 50MB</span></div>
</div>
</div>
<div class="form-item">
<label>关联范围<span class="required">*</span></label>
<select id="scopeType"><option value="">请选择</option><option value="deviceType">按设备类型</option><option value="device">指定设备</option></select>
</div>
<div class="form-item" id="scopeValueWrap" style="display:none;">
<label id="scopeValueLabel">设备类型<span class="required">*</span></label>
<select id="scopeValue"><option value="">请选择</option></select>
</div>
<div class="form-item">
<label>位置标识<span class="required">*</span></label>
<input type="text" id="matPosition" placeholder="请输入位置标识,如:首页横幅">
</div>
<div class="form-item">
<label>排序</label>
<input type="number" id="matSort" placeholder="数字越小越靠前" min="1" value="1">
</div>
<div class="form-item">
<label>生效时间<span class="required">*</span></label>
<input type="datetime-local" id="matStart">
</div>
<div class="form-item">
<label>到期时间<span class="required">*</span></label>
<input type="datetime-local" id="matEnd">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeModal()">取消</button>
<button class="btn btn-primary" id="btnSubmit">确定</button>
</div>
</div>
</div>
<!-- 详情抽屉 -->
<div class="drawer-mask" id="drawerMask" onclick="closeDrawer()"></div>
<div class="drawer" id="drawer">
<div class="drawer__header">
<span class="drawer__title" id="drawerTitle">物料详情</span>
<span class="drawer__close" onclick="closeDrawer()">&times;</span>
</div>
<div class="drawer__body" id="drawerBody"></div>
</div>
<script>
/* ===== 侧边栏 ===== */
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
/* ===== 新增弹窗 ===== */
var editMode=false;
function openAddModal(){
editMode=false;
document.getElementById('modalTitle').textContent='新增物料';
document.getElementById('btnSubmit').textContent='确定';
document.getElementById('modalOverlay').style.display='flex';
resetForm();
}
function closeModal(){document.getElementById('modalOverlay').style.display='none';}
document.getElementById('modalOverlay').addEventListener('click',function(e){if(e.target===this)closeModal();});
/* ===== 编辑弹窗 ===== */
function openEditModal(linkEl){
editMode=true;
var row=linkEl.closest('tr');
var d=row.dataset;
document.getElementById('modalTitle').textContent='编辑物料';
document.getElementById('btnSubmit').textContent='保存';
document.getElementById('matName').value=d.name;
document.getElementById('scopeType').value=d.scope==='按设备类型'?'deviceType':'device';
document.getElementById('scopeType').dispatchEvent(new Event('change'));
setTimeout(function(){document.getElementById('scopeValue').value=d.device;},50);
document.getElementById('matPosition').value=d.position;
document.getElementById('matSort').value=d.sort;
document.getElementById('matStart').value=d.start.replace(' ','T');
document.getElementById('matEnd').value=d.end.replace(' ','T');
// 切换内容面板
var panelMap={img:'panelImg',text:'panelText',html:'panelHtml',video:'panelVideo'};
var targetPanel=panelMap[d.contentType]||'panelImg';
document.querySelectorAll('.content-tabs__tab').forEach(function(t){t.classList.toggle('active',t.dataset.panel===targetPanel);});
document.querySelectorAll('.content-panel').forEach(function(p){p.classList.toggle('show',p.id===targetPanel);});
document.getElementById('modalOverlay').style.display='flex';
}
/* ===== 内容面板切换 ===== */
document.querySelectorAll('.content-tabs__tab').forEach(function(tab){
tab.addEventListener('click',function(){
document.querySelectorAll('.content-tabs__tab').forEach(function(t){t.classList.remove('active');});
this.classList.add('active');
document.querySelectorAll('.content-panel').forEach(function(p){p.classList.remove('show');});
document.getElementById(this.dataset.panel).classList.add('show');
});
});
/* ===== 关联范围切换 ===== */
document.getElementById('scopeType').addEventListener('change',function(){
var wrap=document.getElementById('scopeValueWrap');
var label=document.getElementById('scopeValueLabel');
var sel=document.getElementById('scopeValue');
sel.innerHTML='<option value="">请选择</option>';
if(this.value==='deviceType'){
label.textContent='设备类型';
['点餐屏','智能取餐柜','LED显示屏','餐线秤台'].forEach(function(v){sel.innerHTML+='<option>'+v+'</option>';});
wrap.style.display='block';
}else if(this.value==='device'){
label.textContent='指定设备';
['1号点餐屏','2号点餐屏','智能取餐柜-01','智能取餐柜-02','LED屏-01','LED屏-02'].forEach(function(v){sel.innerHTML+='<option>'+v+'</option>';});
wrap.style.display='block';
}else{
wrap.style.display='none';
}
});
/* ===== 重置表单 ===== */
function resetForm(){
document.getElementById('matName').value='';
document.getElementById('scopeType').value='';
document.getElementById('scopeValueWrap').style.display='none';
document.getElementById('matPosition').value='';
document.getElementById('matSort').value='1';
document.getElementById('matStart').value='';
document.getElementById('matEnd').value='';
document.querySelectorAll('.content-tabs__tab').forEach(function(t,i){t.classList.toggle('active',i===0);});
document.querySelectorAll('.content-panel').forEach(function(p,i){p.classList.toggle('show',i===0);});
}
/* ===== 详情抽屉 ===== */
function showDetail(linkEl){
var row=linkEl.closest('tr');
var d=row.dataset;
var statusTag='<span class="tag '+(d.status==='生效中'?'tag-green':'tag-gray')+'">'+d.status+'</span>';
var typeTag='<span class="tag tag-blue">'+d.type+'</span>';
// 渲染内容预览
var previewHtml='';
switch(d.contentType){
case 'img':
previewHtml='<div class="drawer-preview"><img src="https://placehold.co/600x360/1890FF/white?text='+encodeURIComponent(d.name)+'" alt="'+d.name+'"></div>';
break;
case 'text':
previewHtml='<div class="drawer-preview"><div class="preview-text">春季养生小贴士:\n1. 多吃时令蔬菜,如春笋、菠菜\n2. 适当增加蛋白质摄入\n3. 保持饮食清淡,少油少盐\n4. 多喝温水,促进新陈代谢\n5. 适量运动,增强体质</div></div>';
break;
case 'html':
previewHtml='<div class="drawer-preview"><div class="preview-html"><div style="background:linear-gradient(135deg,#ff6b35,#f7931e);color:#fff;padding:24px;text-align:center;"><h2 style="margin:0 0 8px;font-size:22px">'+d.name+'</h2><p style="margin:0;font-size:18px">限时特惠 美味来袭</p><p style="margin:8px 0 0;font-size:14px;opacity:0.85">尝鲜价 ¥19.9 | 原价 ¥29.9</p></div></div></div>';
break;
case 'video':
previewHtml='<div class="drawer-preview"><div class="preview-video">▶ '+d.name+'</div></div>';
break;
default:
previewHtml='<div class="drawer-preview" style="color:var(--text-placeholder);text-align:center;padding:40px;">暂无内容预览</div>';
}
document.getElementById('drawerTitle').textContent='物料详情 — '+d.name;
document.getElementById('drawerBody').innerHTML=
'<div class="detail-section">'+
'<div class="detail-section__title"><span class="dot"></span>基本信息</div>'+
'<div class="detail-row"><div class="detail-label">物料编号</div><div class="detail-value">'+d.id+'</div></div>'+
'<div class="detail-row"><div class="detail-label">物料名称</div><div class="detail-value">'+d.name+'</div></div>'+
'<div class="detail-row"><div class="detail-label">物料类型</div><div class="detail-value">'+typeTag+'</div></div>'+
'<div class="detail-row"><div class="detail-label">状态</div><div class="detail-value">'+statusTag+'</div></div>'+
'<div class="detail-row"><div class="detail-label">关联范围</div><div class="detail-value">'+d.scope+'</div></div>'+
'<div class="detail-row"><div class="detail-label">关联设备</div><div class="detail-value">'+d.device+'</div></div>'+
'<div class="detail-row"><div class="detail-label">位置标识</div><div class="detail-value">'+d.position+'</div></div>'+
'<div class="detail-row"><div class="detail-label">排序</div><div class="detail-value">'+d.sort+'</div></div>'+
'</div>'+
'<div class="detail-section">'+
'<div class="detail-section__title"><span class="dot"></span>时间信息</div>'+
'<div class="detail-row"><div class="detail-label">生效时间</div><div class="detail-value">'+d.start+'</div></div>'+
'<div class="detail-row"><div class="detail-label">到期时间</div><div class="detail-value">'+d.end+'</div></div>'+
'<div class="detail-row"><div class="detail-label">创建人</div><div class="detail-value">'+d.creator+'</div></div>'+
'<div class="detail-row"><div class="detail-label">创建时间</div><div class="detail-value">'+d.ctime+'</div></div>'+
'</div>'+
'<div class="detail-section">'+
'<div class="detail-section__title"><span class="dot"></span>物料内容预览</div>'+
previewHtml+
'</div>';
document.getElementById('drawerMask').classList.add('show');
document.getElementById('drawer').classList.add('show');
}
function closeDrawer(){
document.getElementById('drawerMask').classList.remove('show');
document.getElementById('drawer').classList.remove('show');
}
/* ===== 删除确认弹窗 ===== */
function showDeleteConfirm(linkEl){
var row=linkEl.closest('tr');
var overlay=document.createElement('div');
overlay.className='confirm-overlay';
overlay.innerHTML='<div class="confirm-modal"><div class="modal-header"><span class="modal-title">删除确认</span><span class="modal-close">&times;</span></div><div class="modal-body">'+
'<div class="confirm-icon"><svg viewBox="0 0 48 48" fill="none"><circle cx="24" cy="24" r="22" fill="#FFF1F0" stroke="#FF4D4F" stroke-width="2"/><path d="M24 14v14" stroke="#FF4D4F" stroke-width="3" stroke-linecap="round"/><circle cx="24" cy="34" r="2" fill="#FF4D4F"/></svg></div>'+
'<div class="confirm-msg">确定要删除该物料吗?</div>'+
'<div class="confirm-sub">'+row.dataset.id+' · '+row.dataset.name+'</div>'+
'</div><div class="modal-footer"><button class="btn btn-default close-confirm">取消</button><button class="btn btn-danger close-confirm">确定删除</button></div></div>';
document.body.appendChild(overlay);
overlay.addEventListener('click',function(e){if(e.target===overlay||e.target.classList.contains('close-confirm'))overlay.remove();});
}
</script>
</body>
</html>
+11 -1
View File
@@ -186,6 +186,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -204,7 +205,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item active"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -177,6 +177,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -195,7 +196,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item active"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -413,6 +413,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -431,7 +432,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item active"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -122,6 +122,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -140,7 +141,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -126,6 +126,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -144,7 +145,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+21 -2
View File
@@ -197,6 +197,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item active"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -215,7 +216,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -393,6 +403,12 @@ var STEP_HTML = [
<div class="upload-area__icon"></div>
<div class="upload-area__text">点击或拖拽上传制作视频</div>
<div class="upload-area__hint">支持 MP4 / MOV,不超过 500MB</div>
</div>
<div class="form-subtitle">🎬 宣传视频</div>
<div class="upload-area" onclick="triggerPromoVideoUpload()">
<div class="upload-area__icon"></div>
<div class="upload-area__text">点击或拖拽上传宣传视频</div>
<div class="upload-area__hint">支持 MP4 / MOV,不超过 500MB</div>
</div>`,
// Step 1: 餐品构成
@@ -648,7 +664,10 @@ function removeImage(btn){
if(addBtn) addBtn.style.display='';
}
function triggerVideoUpload(){
alert('选择视频文件(原型演示)');
alert('选择制作视频文件(原型演示)');
}
function triggerPromoVideoUpload(){
alert('选择宣传视频文件(原型演示)');
}
/* === 适配人群动态行 === */
+11 -1
View File
@@ -206,6 +206,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item active"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -224,7 +225,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+773
View File
@@ -0,0 +1,773 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>营养管理 - 自有餐品库 | 健康CQ原型</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:0.65;margin-right:2px;}
.sidebar-item.active .mi{opacity:0.9;}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:16px;}
.stat-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;}
.stat-card__icon{width:48px;height:48px;border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:24px;}
.stat-card__icon.blue{background:var(--primary-light);}
.stat-card__icon.green{background:#F6FFED;}
.stat-card__icon.orange{background:#FFF7E6;}
.stat-card__icon.red{background:#FFF1F0;}
.stat-card__info{display:flex;flex-direction:column;gap:4px;}
.stat-card__value{font-size:var(--font-xl);font-weight:700;color:var(--text-primary);}
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;text-decoration:none;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);margin-right:4px;}
.tag-blue{background:var(--primary-light);color:var(--primary);}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-orange{background:#FFF7E6;color:var(--warning);}
.tag-red{background:#FFF1F0;color:var(--danger);}
.tag-gray{background:#F5F5F5;color:#999;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}.link:hover{color:#40a9ff;}
/* 左右分栏布局 */
.master-detail{display:flex;gap:16px;height:calc(100vh - 56px - 24px - 24px - 100px);}
.panel-left{width:380px;flex-shrink:0;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);display:flex;flex-direction:column;overflow:hidden;}
.panel-right{flex:1;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);display:flex;flex-direction:column;overflow:hidden;}
.panel-left__header{padding:16px 20px 12px;border-bottom:1px solid var(--border);}
.panel-left__title{font-size:var(--font-base);font-weight:600;margin-bottom:12px;display:flex;align-items:center;justify-content:space-between;}
.panel-left__search{display:flex;gap:8px;margin-bottom:10px;}
.panel-left__search input{flex:1;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);outline:none;}
.panel-left__search input:focus{border-color:var(--primary);}
.panel-left__filter{display:flex;gap:8px;}
.panel-left__filter select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 10px;font-size:var(--font-xs);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;appearance:none;cursor:pointer;flex:1;}
.panel-left__actions{display:flex;gap:8px;padding:10px 20px;border-bottom:1px solid var(--border);flex-wrap:wrap;}
.panel-left__actions .btn{height:30px;font-size:var(--font-xs);padding:0 10px;}
.food-list{flex:1;overflow-y:auto;padding:4px 0;}
.food-item{display:flex;align-items:center;gap:12px;padding:12px 20px;cursor:pointer;transition:background .15s;border-left:3px solid transparent;}
.food-item:hover{background:#F8FBFF;}
.food-item.active{background:var(--primary-light);border-left-color:var(--primary);}
.food-item__icon{width:36px;height:36px;border-radius:var(--radius-sm);background:#F5F7FA;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;}
.food-item__info{flex:1;min-width:0;}
.food-item__name{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.food-item__meta{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;display:flex;gap:8px;}
.panel-left__footer{padding:10px 20px;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;}
.panel-left__footer span{font-size:var(--font-xs);color:var(--text-placeholder);}
.page-btn{width:26px;height:26px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-xs);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
/* 右侧详情面板 */
.panel-right__header{padding:16px 24px 0;border-bottom:1px solid var(--border);}
.panel-right__food-title{display:flex;align-items:center;gap:12px;margin-bottom:14px;}
.panel-right__food-title h2{font-size:var(--font-lg);font-weight:600;}
.panel-right__food-title .code{font-size:var(--font-sm);color:var(--text-placeholder);}
.detail-tabs{display:flex;gap:0;flex-wrap:wrap;}
.detail-tab{padding:10px 16px;font-size:var(--font-sm);cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .2s;display:flex;align-items:center;gap:5px;}
.detail-tab:hover{color:var(--primary);}
.detail-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:500;}
.panel-right__body{flex:1;overflow-y:auto;padding:20px 24px;}
.detail-panel{display:none;}
.detail-panel.active{display:block;}
/* 详情网格 */
.info-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px;}
.info-cell{background:#FAFBFC;border-radius:var(--radius-sm);padding:14px 16px;border:1px solid #F0F0F0;}
.info-cell__label{font-size:var(--font-xs);color:var(--text-placeholder);margin-bottom:4px;}
.info-cell__value{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
.info-section{margin-bottom:20px;}
.info-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid #F0F0F0;display:flex;align-items:center;justify-content:space-between;}
.info-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
.info-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.info-table td{padding:10px 12px;border-bottom:1px solid var(--border);color:var(--text-primary);}
.info-table tr:last-child td{border-bottom:none;}
.info-table tr:hover{background:#F8FBFF;}
.spec-detail-table th{padding:6px 12px;font-size:12px;}
.spec-detail-table td{padding:6px 12px;font-size:12px;}
/* 外售净菜包胶囊开关 */
.sale-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
.sale-toggle__switch{width:32px;height:18px;background:#D9D9D9;border-radius:9px;position:relative;transition:background .2s;flex-shrink:0;}
.sale-toggle__switch::after{content:'';position:absolute;left:2px;top:2px;width:14px;height:14px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 2px rgba(0,0,0,0.15);}
.sale-toggle.active .sale-toggle__switch{background:var(--success);}
.sale-toggle.active .sale-toggle__switch::after{transform:translateX(14px);}
.sale-toggle__label{font-size:11px;color:var(--text-placeholder);}
.sale-toggle.active .sale-toggle__label{color:var(--success);font-weight:500;}
/* 图片/视频网格 */
.media-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;}
.media-card{background:#FAFBFC;border-radius:var(--radius-md);border:1px solid #F0F0F0;overflow:hidden;}
.media-card__thumb{height:120px;background:#E8E8E8;display:flex;align-items:center;justify-content:center;font-size:36px;color:#999;}
.media-card__info{padding:10px 12px;}
.media-card__title{font-size:var(--font-sm);font-weight:500;margin-bottom:4px;}
.media-card__meta{font-size:var(--font-xs);color:var(--text-placeholder);}
/* 弹窗 */
.dialog-mask{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.45);z-index:200;align-items:center;justify-content:center;}
.dialog-mask.open{display:flex;}
.dialog{background:#fff;border-radius:var(--radius-lg);width:560px;max-width:90vw;box-shadow:var(--shadow-md);}
.dialog__header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);}
.dialog__title{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);}
.dialog__close{width:32px;height:32px;border:none;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);color:var(--text-placeholder);}
.dialog__close:hover{background:#F5F5F5;color:var(--text-primary);}
.dialog__body{padding:24px;}
.dialog__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
.form-group{margin-bottom:16px;}
.form-group label{display:block;font-size:var(--font-sm);color:var(--text-primary);margin-bottom:8px;font-weight:500;}
.form-group label .required{color:var(--danger);margin-left:2px;}
.form-input,.form-select{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
.form-input:focus,.form-select:focus{border-color:var(--primary);}
.form-select{appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;padding-right:28px;}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
/* 操作列固定右侧 */
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;white-space:nowrap;}
thead th:last-child{background:#FAFAFA;}
tbody tr:hover td.td-actions{background:#F8FBFF;}
/* 分类管理表格 */
.cat-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
.cat-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
.cat-table td{padding:8px 12px;border-bottom:1px solid #F5F5F5;color:var(--text-primary);}
.cat-table tbody tr:hover{background:#F8FBFF;}
.cat-toggle{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:1px solid var(--border);border-radius:3px;cursor:pointer;margin-right:6px;font-size:12px;line-height:1;background:#fff;color:var(--text-secondary);flex-shrink:0;vertical-align:middle;}
.cat-toggle:hover{border-color:var(--primary);color:var(--primary);}
.cat-toggle.leaf{visibility:hidden;}
.cat-table td.td-actions{white-space:nowrap;}
.cat-table td.td-actions .link{margin:0 4px;}
/* 状态胶囊开关 */
.status-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
.status-toggle__switch{width:36px;height:20px;background:#D9D9D9;border-radius:10px;position:relative;transition:background .2s;flex-shrink:0;}
.status-toggle__switch::after{content:'';position:absolute;left:2px;top:2px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,0.15);}
.status-toggle.active .status-toggle__switch{background:var(--success);}
.status-toggle.active .status-toggle__switch::after{transform:translateX(16px);}
.status-toggle__label{font-size:var(--font-xs);color:var(--text-placeholder);}
.status-toggle.active .status-toggle__label{color:var(--success);}
/* 页面信息栏 + 食堂切换 */
.page-header-bar{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:16px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
.page-header-bar__left{display:flex;align-items:center;gap:12px;}
.page-header-bar__title{font-size:var(--font-lg);font-weight:700;color:var(--text-primary);}
.page-header-bar__meta{font-size:var(--font-sm);color:var(--text-placeholder);}
.page-header-bar__right{display:flex;align-items:center;gap:12px;}
.page-header-bar__right label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.page-header-bar__right .canteen-select{height:36px;border:2px solid var(--primary);border-radius:var(--radius-sm);padding:0 32px 0 14px;font-size:var(--font-sm);color:var(--primary);font-weight:500;outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%231890FF'/%3E%3C/svg%3E") no-repeat right 12px center;appearance:none;cursor:pointer;min-width:140px;}
</style>
</head>
<body>
<!-- 顶部导航栏 -->
<header class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
营养管理
</div>
<nav class="top-bar__nav">
<a href="trace-query.html">溯源总览</a>
<a href="arc-employee.html">档案</a>
<a href="farm-land.html">绿色种采</a>
<a href="sup-hygiene-check.html">卫生供应</a>
<a href="prod-qc-daily.html">健康生产</a>
<a href="din-dict-mgmt.html" class="active">营养用餐</a>
<a href="ano-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a href="../../index.html" class="btn-back">← 返回首页</a>
<div class="avatar"></div>
<span>系统管理员 ▾</span>
</div>
</header>
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="sidebar-menu__title">营养用餐</div>
<a href="din-canteen.html" class="sidebar-item"><span class="mi">🏢</span>场所管理</a>
<a href="din-dict-mgmt.html" class="sidebar-item"><span class="mi">📖</span>字典管理</a>
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item active"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
📐 算法标准
</div>
<div class="sidebar-subgroup__body">
<a href="din-algo-population.html" class="sidebar-item"><span class="mi">👥</span>人群基础标准</a>
<a href="din-algo-vip.html" class="sidebar-item"><span class="mi">👑</span>VIP定制标准</a>
</div>
</div>
<a href="din-recipe-mgmt.html" class="sidebar-item"><span class="mi">📋</span>食谱管理</a>
<a href="din-order-meal.html" class="sidebar-item"><span class="mi">🍱</span>营养点餐</a>
<a href="din-user-mgmt.html" class="sidebar-item"><span class="mi">👤</span>用户管理</a>
<a href="din-order-line.html" class="sidebar-item"><span class="mi">🍳</span>餐线管理</a>
<a href="din-order-pickup.html" class="sidebar-item"><span class="mi">🎫</span>取餐管理</a>
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🤖 模型管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-model-llm.html" class="sidebar-item"><span class="mi">🧠</span>大模型管理</a>
<a href="din-model-small.html" class="sidebar-item"><span class="mi">⚙️</span>小模型管理</a>
</div>
</div>
<a href="din-innovation.html" class="sidebar-item"><span class="mi">💡</span>味养创新</a>
</aside>
<!-- 主内容 -->
<main class="main">
<!-- 页面信息栏 -->
<div class="page-header-bar">
<div class="page-header-bar__left">
<span class="page-header-bar__title">自有餐品库</span>
<span class="page-header-bar__meta">2026-08-03 · 数据实时更新</span>
</div>
<div class="page-header-bar__right">
<label>当前场所</label>
<select class="canteen-select">
<option value="1">一场所(东区)</option>
<option value="2">二场所(西区)</option>
<option value="3">三场所(北区)</option>
</select>
</div>
</div>
<!-- 统计卡片 -->
<div class="stats-row">
<div class="stat-card"><div class="stat-card__icon blue">🍽️</div><div class="stat-card__info"><div class="stat-card__value">486</div><div class="stat-card__label">餐品总数</div></div></div>
<div class="stat-card"><div class="stat-card__icon green"></div><div class="stat-card__info"><div class="stat-card__value">312</div><div class="stat-card__label">上架中</div></div></div>
<div class="stat-card"><div class="stat-card__icon orange">📊</div><div class="stat-card__info"><div class="stat-card__value">312道</div><div class="stat-card__label">已录入指标</div></div></div>
<div class="stat-card"><div class="stat-card__icon green">🆕</div><div class="stat-card__info"><div class="stat-card__value">18</div><div class="stat-card__label">本月新增</div></div></div>
</div>
<!-- 左右分栏 -->
<div class="master-detail">
<!-- 左侧:餐品列表 -->
<div class="panel-left">
<div class="panel-left__header">
<div class="panel-left__title">餐品列表</div>
<div class="panel-left__search">
<input type="text" placeholder="搜索餐品名称/编号...">
<button class="btn btn-primary" style="height:32px;padding:0 12px;font-size:var(--font-xs);">搜索</button>
</div>
<div class="panel-left__filter">
<select><option>全部分类</option><option>主食</option><option>荤菜</option><option>素菜</option><option>汤品</option><option>小吃</option></select>
<select><option>全部状态</option><option>启用</option><option>停用</option></select>
</div>
</div>
<div class="panel-left__actions">
<a href="din-dish-add.html" class="btn btn-primary"> 新增餐品</a>
<button class="btn btn-default">📤 批量导入</button>
<button class="btn btn-default" onclick="openDishCategoryDialog()">📂 餐品分类管理</button>
</div>
<div class="food-list" id="dishList"></div>
<div class="panel-left__footer">
<span>共 486 条</span>
<div style="display:flex;gap:4px;">
<button class="page-btn"></button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn">3</button><button class="page-btn"></button>
</div>
</div>
</div>
<!-- 右侧:餐品详情 -->
<div class="panel-right">
<div class="panel-right__header">
<div class="panel-right__food-title">
<h2 id="detailName">红烧肉</h2>
<span class="code" id="detailCode">DISH001</span>
<span class="tag tag-green" id="detailStatus">上架</span>
</div>
<div class="detail-tabs">
<div class="detail-tab active" onclick="switchDetailTab(0)">🍽️ 基本档案</div>
<div class="detail-tab" onclick="switchDetailTab(1)">🧩 餐品构成</div>
<div class="detail-tab" onclick="switchDetailTab(2)">🍳 烹饪工艺</div>
<div class="detail-tab" onclick="switchDetailTab(3)">📊 营养指标</div>
<div class="detail-tab" onclick="switchDetailTab(4)">👅 味道档案</div>
<div class="detail-tab" onclick="switchDetailTab(5)">🫦 口感档案</div>
<div class="detail-tab" onclick="switchDetailTab(6)">🎨 色泽档案</div>
</div>
</div>
<div class="panel-right__body" id="detailBody"></div>
</div>
</div>
</main>
<script>
// 餐品数据
const DISHES = [
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,cleanPrice:9,cleanSale:false,isDefault:true},{name:'小份',weight:120,price:8,cleanPrice:6,cleanSale:false},{name:'大份',weight:300,price:18,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',weightBasePrice:'6.00',weightVipPrice:'5.40',crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',
composition:[{name:'猪五花肉',netType:'红烧块',amount:160,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'生姜',netType:'姜片',amount:5,pct:'2.5%',state:'净菜',type:'辅材',allergen:'无',note:'—'},{name:'生抽',netType:'调味汁',amount:15,pct:'7.5%',state:'原料',type:'调料',allergen:'含麸质',note:'酱油'},{name:'冰糖',netType:'糖浆',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'上色提味'},{name:'料酒',netType:'料酒汁',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'去腥'}],
nutri:{calorie:385,protein:18.2,carb:12.4,fat:28.6,vitC:0.8,mineral:42,fiber:0.3,sodium:486,nutriGrade:'偏差',note:'铁2.4mg、锌1.8mg'},
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,oil:15,salt:3,temp:'100→80',output:200,steps:6},
taste:{sour:1,sweet:3,bitter:1,spicy:1,salty:4,smell:4.2,flavorScore:3.8,flavorGrade:'良好'},
color:{mainColor:'红褐色',uniformity:4,gloss:4,freshness:4,appearance:4,colorScore:4.0,colorGrade:'良好'},
texture:{hardness:2,sticky:3,crispy:1,smooth:4,elastic:3,textureScore:3.6,textureGrade:'良好'}
},
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{name:'标准份',weight:150,price:6,cleanPrice:4.5,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:4,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'3.00',weightVipPrice:'2.70',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',
composition:[{name:'时令青菜',netType:'青菜段',amount:120,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'蒜末',netType:'蒜末',amount:5,pct:'3.3%',state:'净菜',type:'调料',allergen:'无',note:'—'},{name:'食用油',netType:'净油',amount:8,pct:'5.3%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'1.3%',state:'原料',type:'调料',allergen:'无',note:'—'}],
nutri:{calorie:68,protein:2.1,carb:8.6,fat:3.2,vitC:28.5,mineral:86,fiber:2.4,sodium:186,nutriGrade:'达标',note:'铁1.2mg、钙45mg'},
cook:{mainMethod:'炒',subMethod:'—',heat:'大火',duration:5,oil:8,salt:2,temp:'200',output:150,steps:4},
taste:{sour:1,sweet:2,bitter:1,spicy:1,salty:2,smell:3.5,flavorScore:3.2,flavorGrade:'一般'},
color:{mainColor:'翠绿色',uniformity:5,gloss:4,freshness:5,appearance:4,colorScore:4.5,colorGrade:'优秀'},
texture:{hardness:3,sticky:1,crispy:4,smooth:3,elastic:2,textureScore:3.4,textureGrade:'良好'}
},
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{name:'标准份',weight:300,price:5,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',weightBasePrice:'1.67',weightVipPrice:'1.50',crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',
composition:[{name:'番茄',netType:'番茄块',amount:150,pct:'50%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'鸡蛋',netType:'鸡蛋液',amount:60,pct:'20%',state:'原料',type:'主材',allergen:'含蛋类',note:'—'},{name:'食用油',netType:'净油',amount:5,pct:'1.7%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'0.7%',state:'原料',type:'调料',allergen:'无',note:'—'}],
nutri:{calorie:45,protein:3.8,carb:4.2,fat:2.1,vitC:18.2,mineral:32,fiber:1.1,sodium:280,nutriGrade:'达标',note:'铁0.8mg、钾210mg'},
cook:{mainMethod:'煮',subMethod:'炒',heat:'中火',duration:12,oil:5,salt:2,temp:'100',output:300,steps:5},
taste:{sour:3,sweet:2,bitter:1,spicy:1,salty:2,smell:3.8,flavorScore:3.5,flavorGrade:'良好'},
color:{mainColor:'橙红色',uniformity:4,gloss:3,freshness:5,appearance:4,colorScore:4.0,colorGrade:'良好'},
texture:{hardness:1,sticky:2,crispy:1,smooth:5,elastic:2,textureScore:3.8,textureGrade:'良好'}
},
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{name:'标准份',weight:200,price:3,cleanPrice:2,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:2,cleanPrice:1.5,cleanSale:false},{name:'大份',weight:300,price:4,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'1.50',weightVipPrice:'1.35',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',
composition:[{name:'大米',netType:'熟米饭',amount:100,pct:'100%',state:'原料',type:'主材',allergen:'无',note:'—'}],
nutri:{calorie:232,protein:4.8,carb:51.2,fat:0.6,vitC:0,mineral:12,fiber:0.4,sodium:3,nutriGrade:'达标',note:'—'},
cook:{mainMethod:'蒸',subMethod:'—',heat:'大火→中火',duration:30,oil:0,salt:0,temp:'100',output:200,steps:2},
taste:{sour:1,sweet:2,bitter:1,spicy:1,salty:1,smell:3.0,flavorScore:2.8,flavorGrade:'一般'},
color:{mainColor:'乳白色',uniformity:5,gloss:4,freshness:4,appearance:4,colorScore:4.2,colorGrade:'良好'},
texture:{hardness:2,sticky:3,crispy:1,smooth:3,elastic:2,textureScore:3.2,textureGrade:'一般'}
},
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{name:'标准份',weight:180,price:14,cleanPrice:10,cleanSale:false,isDefault:true},{name:'大份',weight:280,price:20,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',weightBasePrice:'7.78',weightVipPrice:'7.00',crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',
composition:[{name:'猪里脊肉',netType:'里脊丝',amount:120,pct:'66.7%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'淀粉',netType:'淀粉糊',amount:20,pct:'11.1%',state:'原料',type:'辅材',allergen:'无',note:'挂糊'},{name:'白糖',netType:'糖浆',amount:25,pct:'13.9%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'},{name:'食醋',netType:'调味汁',amount:15,pct:'8.3%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'}],
nutri:{calorie:420,protein:22.4,carb:38.2,fat:18.6,vitC:1.2,mineral:28,fiber:0.6,sodium:620,nutriGrade:'未达标',note:'铁1.8mg'},
cook:{mainMethod:'炸',subMethod:'炒',heat:'大火',duration:15,oil:50,salt:1,temp:'180',output:180,steps:7},
taste:{sour:4,sweet:4,bitter:1,spicy:1,salty:3,smell:4.0,flavorScore:4.2,flavorGrade:'良好'},
color:{mainColor:'金黄色',uniformity:4,gloss:5,freshness:4,appearance:5,colorScore:4.5,colorGrade:'优秀'},
texture:{hardness:3,sticky:1,crispy:5,smooth:4,elastic:3,textureScore:4.0,textureGrade:'良好'}
}
];
let currentDish = 0;
let currentTab = 0;
function renderDishList(){
document.getElementById('dishList').innerHTML = DISHES.map((d,i)=>`
<div class="food-item${i===currentDish?' active':''}" onclick="selectDish(${i})">
<div class="food-item__icon">${d.icon}</div>
<div class="food-item__info">
<div class="food-item__name">${d.name}</div>
<div class="food-item__meta"><span>${d.id}</span><span>${d.category}</span><span class="tag tag-${d.status==='启用'?'green':'gray'}" style="margin:0;padding:1px 6px;">${d.status}</span></div>
</div>
</div>`).join('');
}
function selectDish(idx){ currentDish=idx; renderDishList(); renderDetail(); }
function levelTag(level){
const map = {'推荐':'tag-green','适量':'tag-orange','慎食':'tag-red'};
return '<span class="tag '+(map[level]||'tag-gray')+'">'+level+'</span>';
}
function switchDetailTab(idx){ currentTab=idx; document.querySelectorAll('.detail-tab').forEach((t,i)=>t.classList.toggle('active',i===idx)); renderDetail(); }
function renderDetail(){
const d = DISHES[currentDish];
document.getElementById('detailName').textContent = d.name;
document.getElementById('detailCode').textContent = d.id;
const s = document.getElementById('detailStatus');
s.textContent = d.status; s.className='tag tag-'+(d.status==='启用'?'green':'gray');
const body = document.getElementById('detailBody');
const renderers = [renderBasic,renderComposition,renderCook,renderNutri,renderTaste,renderTexture,renderColor];
body.innerHTML = renderers[currentTab](d);
}
function renderBasic(d){
var specRows = (d.specs||[]).map(function(s){
var statusHtml = s.isDefault ? '<span class="tag tag-blue" style="font-size:11px;">默认</span>' : '';
var cleanPrice = (s.cleanPrice!==undefined && s.cleanPrice!==null) ? parseFloat(s.cleanPrice).toFixed(2) : '—';
var cleanSaleActive = s.cleanSale === true;
var cleanSaleHtml = '<div class="sale-toggle'+(cleanSaleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(cleanSaleActive?'上架':'下架')+'</span></div>';
return '<tr><td>'+s.name+'</td><td>'+s.weight+'</td><td>'+s.price.toFixed(2)+'</td><td>'+cleanPrice+'</td><td>'+cleanSaleHtml+'</td><td>'+statusHtml+'</td></tr>';
}).join('');
return `<div class="info-section"><div class="info-section__title">基本信息 <span><a class="link" href="din-dish-add.html?mode=edit&tab=0">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">餐品编号</div><div class="info-cell__value">${d.id}</div></div>
<div class="info-cell"><div class="info-cell__label">餐品分类</div><div class="info-cell__value">${d.category}</div></div>
<div class="info-cell"><div class="info-cell__label">适用餐次</div><div class="info-cell__value">${d.meal}</div></div>
<div class="info-cell"><div class="info-cell__label">过敏原</div><div class="info-cell__value">${d.allergen}</div></div>
<div class="info-cell"><div class="info-cell__label">餐品标签</div><div class="info-cell__value">${d.tags.map(t=>'<span class="tag tag-blue">'+t+'</span>').join('')}</div></div>
<div class="info-cell"><div class="info-cell__label">称重基础价(元/100g)</div><div class="info-cell__value">${d.weightBasePrice||'—'}</div></div>
<div class="info-cell"><div class="info-cell__label">称重会员价(元/100g)</div><div class="info-cell__value">${d.weightVipPrice||'—'}</div></div>
<div class="info-cell"><div class="info-cell__label">来源</div><div class="info-cell__value"><span class="tag ${d.source==='系统内置'?'tag-blue':d.source==='味养创新'?'tag-orange':'tag-gray'}">${d.source||'—'}</span></div></div>
<div class="info-cell"><div class="info-cell__label">启用状态</div><div class="info-cell__value"><span class="tag tag-${d.status==='启用'?'green':'gray'}">${d.status}</span></div></div>
</div></div>
<div class="info-section"><div class="info-section__title">👥 适配人群</div>
<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;">${(d.crowd||[]).map(function(c){return '<div style="display:flex;align-items:center;gap:4px;"><span style="font-size:var(--font-sm);">'+c.name+'</span>'+levelTag(c.level)+'</div>';}).join('')}</div>
${d.crowdNote?'<div style="font-size:var(--font-sm);color:var(--text-secondary);background:#FFFBE6;padding:8px 12px;border-radius:var(--radius-sm);border:1px solid #FFE58F;">⚠️ '+d.crowdNote+'</div>':''}</div>
<div class="info-section"><div class="info-section__title">📐 餐品规格(共 ${(d.specs||[]).length} 项)</div>
<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>单位重量(g)</th><th>餐品售价(元/份)</th><th>净菜包售价(元/份)</th><th>外售净菜包</th><th>状态</th></tr></thead><tbody>${specRows}</tbody></table></div>
<div class="info-section"><div class="info-section__title">📷 菜品展示</div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">图片</div></div>
<div class="media-card"><div class="media-card__thumb">图片</div></div>
<div class="media-card"><div class="media-card__thumb">图片</div></div>
</div></div>
<div class="info-section"><div class="info-section__title">🎬 制作流程</div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">🎬</div></div>
</div></div>
<div class="info-section"><div class="info-section__title">🎬 宣传视频</div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">🎬</div><div class="media-card__info"><div class="media-card__title">${d.name} 宣传视频</div><div class="media-card__meta">1分30秒 · 48MB · <span class="link">播放</span></div></div></div>
</div></div>`;
}
function renderComposition(d){
const rows = (d.composition||[]).map(c=>`<tr><td>${c.name}</td><td>${c.netType}</td><td>${c.amount}</td><td>${c.pct}</td><td><span class="tag tag-${c.type==='主材'?'blue':c.type==='调料'?'orange':'gray'}">${c.type||'辅材'}</span></td><td>${c.allergen}</td><td>${c.note}</td></tr>`).join('');
return `<div class="info-section"><div class="info-section__title">食材构成(共 ${(d.composition||[]).length} 项) <span><a class="link" href="din-dish-add.html?mode=edit&tab=1">编辑</a></span></div>
<table class="info-table"><thead><tr><th>食材名称</th><th>净菜类型</th><th>用量(g/份)</th><th>占比(%)</th><th>食材类型</th><th>过敏原</th><th>备注</th></tr></thead><tbody>${rows}</tbody></table></div>`;
}
function renderCook(d){
const c = d.cook;
return `<div class="info-section"><div class="info-section__title">烹饪工艺 <span><a class="link" href="din-dish-add.html?mode=edit&tab=2">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">主烹饪法</div><div class="info-cell__value">${c.mainMethod}</div></div>
<div class="info-cell"><div class="info-cell__label">辅烹饪法</div><div class="info-cell__value">${c.subMethod}</div></div>
<div class="info-cell"><div class="info-cell__label">火候</div><div class="info-cell__value">${c.heat}</div></div>
<div class="info-cell"><div class="info-cell__label">烹饪时长</div><div class="info-cell__value">${c.duration}min</div></div>
<div class="info-cell"><div class="info-cell__label">油用量</div><div class="info-cell__value">${c.oil}g</div></div>
<div class="info-cell"><div class="info-cell__label">盐用量</div><div class="info-cell__value">${c.salt}g</div></div>
<div class="info-cell"><div class="info-cell__label">烹饪温度</div><div class="info-cell__value">${c.temp}℃</div></div>
<div class="info-cell"><div class="info-cell__label">出品量</div><div class="info-cell__value">${c.output}g</div></div>
<div class="info-cell"><div class="info-cell__label">工艺步骤数</div><div class="info-cell__value">${c.steps}步</div></div>
</div></div>`;
}
function renderNutri(d){
const n = d.nutri;
return `<div class="info-section"><div class="info-section__title">营养指标 <span><a class="link" href="din-dish-add.html?mode=edit&tab=3">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">热量 (kcal/100g)</div><div class="info-cell__value">${n.calorie}</div></div>
<div class="info-cell"><div class="info-cell__label">蛋白质 (g)</div><div class="info-cell__value">${n.protein}</div></div>
<div class="info-cell"><div class="info-cell__label">碳水化合物 (g)</div><div class="info-cell__value">${n.carb}</div></div>
<div class="info-cell"><div class="info-cell__label">脂肪 (g)</div><div class="info-cell__value">${n.fat}</div></div>
<div class="info-cell"><div class="info-cell__label">维生素C (mg)</div><div class="info-cell__value">${n.vitC}</div></div>
<div class="info-cell"><div class="info-cell__label">矿物质 (mg)</div><div class="info-cell__value">${n.mineral}</div></div>
<div class="info-cell"><div class="info-cell__label">膳食纤维 (g)</div><div class="info-cell__value">${n.fiber}</div></div>
<div class="info-cell"><div class="info-cell__label">钠 (mg)</div><div class="info-cell__value">${n.sodium}</div></div>
<div class="info-cell"><div class="info-cell__label">营养等级</div><div class="info-cell__value">${n.nutriGrade==='达标'?'<span class="tag tag-green">达标</span>':n.nutriGrade==='偏差'?'<span class="tag tag-orange">偏差</span>':'<span class="tag tag-red">未达标</span>'}</div></div>
</div></div>`;
}
function renderTaste(d){
const t = d.taste;
const gradeTag = t.flavorGrade==='优秀'?'green':t.flavorGrade==='良好'?'blue':t.flavorGrade==='一般'?'orange':'red';
return `<div class="info-section"><div class="info-section__title">味道档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=4">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">酸度 (1-5)</div><div class="info-cell__value">${t.sour}</div></div>
<div class="info-cell"><div class="info-cell__label">甜度 (1-5)</div><div class="info-cell__value">${t.sweet}</div></div>
<div class="info-cell"><div class="info-cell__label">苦度 (1-5)</div><div class="info-cell__value">${t.bitter}</div></div>
<div class="info-cell"><div class="info-cell__label">辣度 (1-5)</div><div class="info-cell__value">${t.spicy}</div></div>
<div class="info-cell"><div class="info-cell__label">咸度 (1-5)</div><div class="info-cell__value">${t.salty}</div></div>
<div class="info-cell"><div class="info-cell__label">气味评分 (1-5)</div><div class="info-cell__value">${t.smell}</div></div>
<div class="info-cell"><div class="info-cell__label">综合风味评分 (1-5)</div><div class="info-cell__value">${t.flavorScore}</div></div>
<div class="info-cell"><div class="info-cell__label">综合评级</div><div class="info-cell__value"><span class="tag tag-${gradeTag}">${t.flavorGrade}</span></div></div>
</div></div>`;
}
function renderColor(d){
const c = d.color;
const gradeTag = c.colorGrade==='优秀'?'green':c.colorGrade==='良好'?'blue':c.colorGrade==='一般'?'orange':'red';
return `<div class="info-section"><div class="info-section__title">色泽档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=5">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">主色调</div><div class="info-cell__value">${c.mainColor}</div></div>
<div class="info-cell"><div class="info-cell__label">色泽均匀度 (1-5)</div><div class="info-cell__value">${c.uniformity}</div></div>
<div class="info-cell"><div class="info-cell__label">光泽度 (1-5)</div><div class="info-cell__value">${c.gloss}</div></div>
<div class="info-cell"><div class="info-cell__label">新鲜度 (1-5)</div><div class="info-cell__value">${c.freshness}</div></div>
<div class="info-cell"><div class="info-cell__label">卖相评分 (1-5)</div><div class="info-cell__value">${c.appearance}</div></div>
<div class="info-cell"><div class="info-cell__label">综合色泽评分 (1-5)</div><div class="info-cell__value">${c.colorScore}</div></div>
<div class="info-cell"><div class="info-cell__label">综合评级</div><div class="info-cell__value"><span class="tag tag-${gradeTag}">${c.colorGrade}</span></div></div>
</div></div>`;
}
function renderTexture(d){
const t = d.texture;
const gradeTag = t.textureGrade==='优秀'?'green':t.textureGrade==='良好'?'blue':t.textureGrade==='一般'?'orange':'red';
return `<div class="info-section"><div class="info-section__title">口感档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=6">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">软硬度 (1-5)</div><div class="info-cell__value">${t.hardness}</div></div>
<div class="info-cell"><div class="info-cell__label">黏糯度 (1-5)</div><div class="info-cell__value">${t.sticky}</div></div>
<div class="info-cell"><div class="info-cell__label">酥脆度 (1-5)</div><div class="info-cell__value">${t.crispy}</div></div>
<div class="info-cell"><div class="info-cell__label">嫩滑度 (1-5)</div><div class="info-cell__value">${t.smooth}</div></div>
<div class="info-cell"><div class="info-cell__label">弹性 (1-5)</div><div class="info-cell__value">${t.elastic}</div></div>
<div class="info-cell"><div class="info-cell__label">综合口感评分 (1-5)</div><div class="info-cell__value">${t.textureScore}</div></div>
<div class="info-cell"><div class="info-cell__label">综合评级</div><div class="info-cell__value"><span class="tag tag-${gradeTag}">${t.textureGrade}</span></div></div>
</div></div>`;
}
function renderImages(d){
return `<div class="info-section"><div class="info-section__title">图片库 <span><button class="btn btn-primary" style="height:28px;font-size:var(--font-xs);padding:0 10px;">📤 上传图片</button></span></div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">📷</div><div class="media-card__info"><div class="media-card__meta">800×600 · 128KB · <span class="link">预览</span></div></div></div>
<div class="media-card"><div class="media-card__thumb">📷</div><div class="media-card__info"><div class="media-card__meta">1200×900 · 256KB · <span class="link">预览</span></div></div></div>
<div class="media-card"><div class="media-card__thumb">📷</div><div class="media-card__info"><div class="media-card__meta">800×600 · 96KB · <span class="link">预览</span></div></div></div>
</div></div>`;
}
function renderVideos(d){
return `<div class="info-section"><div class="info-section__title">视频库 <span><button class="btn btn-primary" style="height:28px;font-size:var(--font-xs);padding:0 10px;">📤 上传视频</button></span></div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">🎬</div><div class="media-card__info"><div class="media-card__title">标准制作流程</div><div class="media-card__meta">8分32秒 · 256MB · <span class="link">播放</span></div></div></div>
<div class="media-card"><div class="media-card__thumb">🎬</div><div class="media-card__info"><div class="media-card__title">食材溯源故事</div><div class="media-card__meta">2分15秒 · 86MB · <span class="link">播放</span></div></div></div>
</div></div>`;
}
// 侧边栏折叠
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 切换外售净菜包上架/下架状态
function toggleCleanSale(el){
var active = el.classList.toggle('active');
var label = el.querySelector('.sale-toggle__label');
if(label) label.textContent = active ? '上架' : '下架';
}
(function(){var a=document.querySelector('.sidebar-item.active');if(!a)return;var s=a.closest('.sidebar-subgroup');if(s)s.classList.add('open');})();
// 初始化
renderDishList();
renderDetail();
/* ========== 餐品分类管理 ========== */
var nextDishCatId = 100;
var dishCategories = [
{id:1, name:'主食', code:'01', level:1, sort:1, status:'启用', parentId:null},
{id:2, name:'荤菜', code:'02', level:1, sort:2, status:'启用', parentId:null},
{id:3, name:'素菜', code:'03', level:1, sort:3, status:'启用', parentId:null},
{id:4, name:'汤品', code:'04', level:1, sort:4, status:'启用', parentId:null},
{id:5, name:'小吃', code:'05', level:1, sort:5, status:'启用', parentId:null},
{id:6, name:'饮品', code:'06', level:1, sort:6, status:'启用', parentId:null},
{id:7, name:'甜品', code:'07', level:1, sort:7, status:'启用', parentId:null},
{id:8, name:'套餐', code:'08', level:1, sort:8, status:'禁用', parentId:null}
];
var expandedDishCats = {};
var editingDishCatId = null;
var editingDishParentId = null;
var MAX_DISH_LEVEL = 2;
function openDishCategoryDialog(){
document.getElementById('dishCategoryDialog').classList.add('open');
renderDishCatTable();
}
function closeDishCategoryDialog(){
document.getElementById('dishCategoryDialog').classList.remove('open');
}
function toggleDishCatStatus(id){
var cat = dishCategories.find(function(c){return c.id===id;});
if(!cat) return;
cat.status = cat.status==='启用'?'禁用':'启用';
renderDishCatTable();
}
function toggleDishCatExpand(id){
expandedDishCats[id] = !expandedDishCats[id];
renderDishCatTable();
}
function getDishCatChildren(parentId){
return dishCategories.filter(function(c){return c.parentId===parentId;}).sort(function(a,b){return a.sort-b.sort;});
}
function renderDishCatTable(){
var tops = dishCategories.filter(function(c){return c.parentId===null;}).sort(function(a,b){return a.sort-b.sort;});
var html = '';
tops.forEach(function(cat){
html += renderDishCatRow(cat);
if(expandedDishCats[cat.id]){
getDishCatChildren(cat.id).forEach(function(child){
html += renderDishCatRow(child, true);
});
}
});
document.getElementById('dishCatTableBody').innerHTML = html || '<tr><td colspan="6" style="text-align:center;color:var(--text-placeholder);padding:40px;">暂无分类数据</td></tr>';
}
function renderDishCatRow(cat, isChild){
var hasChildren = getDishCatChildren(cat.id).length > 0;
var isExpanded = expandedDishCats[cat.id];
var toggleHtml = hasChildren
? '<span class="cat-toggle" onclick="event.stopPropagation();toggleDishCatExpand('+cat.id+')">'+(isExpanded?'':'+')+'</span>'
: '<span class="cat-toggle leaf">+</span>';
var rowClass = isChild ? 'cat-row-l2' : '';
var activeClass = cat.status==='启用'?' active':'';
var statusLabel = cat.status==='启用'?'启用':'禁用';
var statusHtml = '<div class="status-toggle'+activeClass+'" onclick="event.stopPropagation();toggleDishCatStatus('+cat.id+')"><span class="status-toggle__switch"></span><span class="status-toggle__label">'+statusLabel+'</span></div>';
var canAddChild = cat.level < MAX_DISH_LEVEL;
var addChildBtn = canAddChild ? '<span class="link" onclick="openAddDishCat('+cat.id+')">新增下级</span>' : '';
return '<tr class="'+rowClass+'"><td class="cat-name">'+toggleHtml+cat.name+'</td><td>'+cat.code+'</td><td>'+cat.level+'</td><td>'+cat.sort+'</td><td>'+statusHtml+'</td><td class="td-actions"><span class="link" onclick="openEditDishCat('+cat.id+')">编辑</span><span class="link" style="color:var(--danger);" onclick="deleteDishCat('+cat.id+')">删除</span>'+addChildBtn+'</td></tr>';
}
function openAddDishCat(parentId){
editingDishCatId = null;
editingDishParentId = parentId || null;
document.getElementById('dishCatFormDialogTitle').textContent = parentId ? '新增下级分类' : '新增分类';
document.getElementById('dishCatFormName').value = '';
document.getElementById('dishCatFormCode').value = '';
document.getElementById('dishCatFormSort').value = '1';
document.getElementById('dishCatFormStatus').value = '启用';
document.getElementById('dishCatFormDialog').classList.add('open');
}
function openEditDishCat(id){
var cat = dishCategories.find(function(c){return c.id===id;});
if(!cat) return;
editingDishCatId = id;
editingDishParentId = cat.parentId;
document.getElementById('dishCatFormDialogTitle').textContent = '编辑分类';
document.getElementById('dishCatFormName').value = cat.name;
document.getElementById('dishCatFormCode').value = cat.code;
document.getElementById('dishCatFormSort').value = cat.sort;
document.getElementById('dishCatFormStatus').value = cat.status;
document.getElementById('dishCatFormDialog').classList.add('open');
}
function closeDishCatFormDialog(){
document.getElementById('dishCatFormDialog').classList.remove('open');
editingDishCatId = null;
editingDishParentId = null;
}
function saveDishCatForm(){
var name = document.getElementById('dishCatFormName').value.trim();
var code = document.getElementById('dishCatFormCode').value.trim();
var sort = parseInt(document.getElementById('dishCatFormSort').value) || 1;
var status = document.getElementById('dishCatFormStatus').value;
if(!name||!code){alert('请填写分类名称和分类编码');return;}
if(editingDishCatId){
var cat = dishCategories.find(function(c){return c.id===editingDishCatId;});
if(cat){cat.name=name;cat.code=code;cat.sort=sort;cat.status=status;}
} else {
var level = editingDishParentId ? 2 : 1;
dishCategories.push({id:nextDishCatId++,name:name,code:code,level:level,sort:sort,status:status,parentId:editingDishParentId});
if(editingDishParentId) expandedDishCats[editingDishParentId] = true;
}
closeDishCatFormDialog();
renderDishCatTable();
}
function deleteDishCat(id){
var cat = dishCategories.find(function(c){return c.id===id;});
if(!cat) return;
if(getDishCatChildren(id).length > 0){alert('该分类下存在子级分类,请先删除子级分类后再删除。');return;}
if(!confirm('确定要删除分类「'+cat.name+'」吗?删除后不可恢复。')) return;
dishCategories = dishCategories.filter(function(c){return c.id!==id;});
renderDishCatTable();
}
</script>
<!-- 新增食材弹窗 -->
<div class="dialog-mask" id="ingredientDialog">
<div class="dialog">
<div class="dialog__header">
<div class="dialog__title">新增食材</div>
<button class="dialog__close" onclick="closeIngredientDialog()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</button>
</div>
<div class="dialog__body">
<div class="form-row">
<div class="form-group"><label>食材名称<span class="required">*</span></label><input class="form-input" type="text" placeholder="请输入食材名称"></div>
<div class="form-group"><label>净菜类型<span class="required">*</span></label><select class="form-select"><option>请选择</option><option>土豆丝</option><option>土豆块</option><option>土豆泥</option><option>番茄块</option><option>蒜末</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>用量(g/份)<span class="required">*</span></label><input class="form-input" type="number" placeholder="请输入用量"></div>
<div class="form-group"><label>加工状态</label><select class="form-select"><option>请选择</option><option>原料</option><option>净菜</option><option>半成品</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>食材类型</label><select class="form-select"><option>辅材</option><option>主材</option><option>调料</option></select></div>
<div class="form-group"><label>过敏原</label><input class="form-input" type="text" placeholder="无则填无"></div>
</div>
<div class="form-group"><label>备注</label><input class="form-input" type="text" placeholder="请输入备注信息"></div>
</div>
<div class="dialog__footer">
<button class="btn btn-default" onclick="closeIngredientDialog()">取消</button>
<button class="btn btn-primary">确定</button>
</div>
</div>
</div>
<script>
function openIngredientDialog(){document.getElementById('ingredientDialog').classList.add('open');}
function closeIngredientDialog(){document.getElementById('ingredientDialog').classList.remove('open');}
</script>
<!-- 餐品分类管理弹窗 -->
<div class="dialog-mask" id="dishCategoryDialog">
<div class="dialog" style="width:820px;">
<div class="dialog__header">
<div class="dialog__title">餐品分类管理</div>
<button class="dialog__close" onclick="closeDishCategoryDialog()"></button>
</div>
<div class="dialog__body">
<!-- 工具栏 -->
<div style="margin-bottom:12px;">
<button class="btn btn-primary" onclick="openAddDishCat()" style="height:30px;font-size:var(--font-xs);"> 新增一级分类</button>
</div>
<!-- 分类表格 -->
<div style="overflow-x:auto;">
<table class="cat-table">
<thead><tr><th style="width:180px;">分类名称</th><th style="width:100px;">分类编码</th><th style="width:60px;">层级</th><th style="width:60px;">排序</th><th style="width:80px;">状态</th><th style="width:180px;">操作</th></tr></thead>
<tbody id="dishCatTableBody"></tbody>
</table>
</div>
</div>
<div class="dialog__footer">
<button class="btn btn-default" onclick="closeDishCategoryDialog()">关闭</button>
</div>
</div>
</div>
<!-- 餐品分类表单子弹窗 -->
<div class="dialog-mask" id="dishCatFormDialog">
<div class="dialog" style="width:480px;">
<div class="dialog__header">
<div class="dialog__title" id="dishCatFormDialogTitle">新增分类</div>
<button class="dialog__close" onclick="closeDishCatFormDialog()"></button>
</div>
<div class="dialog__body">
<div class="form-group"><label>分类名称<span class="required">*</span></label><input class="form-input" id="dishCatFormName" placeholder="请输入分类名称"></div>
<div class="form-row">
<div class="form-group"><label>分类编码<span class="required">*</span></label><input class="form-input" id="dishCatFormCode" placeholder="请输入分类编码"></div>
<div class="form-group"><label>排序</label><input class="form-input" id="dishCatFormSort" type="number" value="1"></div>
</div>
<div class="form-group"><label>状态</label><select class="form-select" id="dishCatFormStatus"><option>启用</option><option>禁用</option></select></div>
</div>
<div class="dialog__footer">
<button class="btn btn-default" onclick="closeDishCatFormDialog()">取消</button>
<button class="btn btn-primary" onclick="saveDishCatForm()">保存</button>
</div>
</div>
</div>
</body>
</html>
+11 -1
View File
@@ -167,6 +167,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item active"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -185,7 +186,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -210,6 +210,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item active"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -228,7 +229,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -168,6 +168,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup"><div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>📐 算法标准</div><div class="sidebar-subgroup__body"><a href="din-algo-population.html" class="sidebar-item"><span class="mi">👥</span>人群基础标准</a><a href="din-algo-vip.html" class="sidebar-item"><span class="mi">💎</span>VIP定制标准</a></div></div>
<a href="din-recipe-mgmt.html" class="sidebar-item"><span class="mi">📋</span>食谱管理</a>
<a href="din-order-meal.html" class="sidebar-item"><span class="mi">🛒</span>营养点餐</a>
@@ -177,7 +178,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup"><div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>🤖 模型管理</div><div class="sidebar-subgroup__body"><a href="din-model-llm.html" class="sidebar-item"><span class="mi">🧠</span>大模型管理</a><a href="din-model-small.html" class="sidebar-item"><span class="mi">⚙️</span>小模型管理</a></div></div>
<a href="din-innovation.html" class="sidebar-item active"><span class="mi">🌟</span>味养创新</a>
</aside>
+11 -1
View File
@@ -142,6 +142,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -160,7 +161,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -161,6 +161,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -179,7 +180,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -187,6 +187,7 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -205,7 +206,16 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -196,6 +196,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -214,7 +215,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -219,6 +219,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -237,7 +238,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -252,6 +252,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -270,7 +271,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💳</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -177,6 +177,7 @@ tbody tr:hover{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -195,7 +196,16 @@ tbody tr:hover{background:#F8FBFF;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item active"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item active"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+618
View File
@@ -0,0 +1,618 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>账户管理 | 健康CQ原型</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
/* 顶部导航 */
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
/* 侧边栏 */
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:0.65;margin-right:2px;}
.sidebar-item.active .mi{opacity:0.9;}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 通用组件 */
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.btn-danger{background:var(--danger);color:#fff;}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:var(--primary-light);color:var(--primary);}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-orange{background:#FFF7E6;color:var(--warning);}
.tag-red{background:#FFF1F0;color:var(--danger);}
.tag-gray{background:#F5F5F5;color:#999;}
.filter-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;appearance:none;cursor:pointer;min-width:120px;}
.filter-input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;min-width:140px;}
.filter-input:focus,.filter-select:focus{border-color:var(--primary);}
/* 页面头部 */
.page-header-bar{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:16px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
.page-header-bar__left{display:flex;align-items:center;gap:12px;}
.page-header-bar__title{font-size:var(--font-lg);font-weight:700;color:var(--text-primary);}
.page-header-bar__back{color:var(--primary);text-decoration:none;font-size:var(--font-sm);display:flex;align-items:center;gap:4px;}
.page-header-bar__back:hover{color:#40a9ff;}
/* 顶部账户操作区 */
.account-top{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-bottom:16px;}
/* 用户信息卡片 */
.user-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;}
.user-card__avatar{width:48px;height:48px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;color:#fff;font-size:20px;font-weight:600;flex-shrink:0;}
.user-card__info{flex:1;min-width:0;}
.user-card__name{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.user-card__code{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;}
.user-card__tags{display:flex;gap:6px;margin-top:6px;}
.user-card__balance{text-align:right;}
.user-card__balance-label{font-size:var(--font-xs);color:var(--text-placeholder);}
.user-card__balance-value{font-size:var(--font-xxl);font-weight:700;color:var(--text-primary);margin-top:4px;}
/* 账户设置卡片 */
.settings-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);}
.settings-card__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:14px;}
.settings-row{display:flex;gap:12px;margin-bottom:12px;flex-wrap:wrap;}
.settings-item{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0;}
.settings-item label{font-size:var(--font-xs);color:var(--text-secondary);}
.settings-item select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 8px;font-size:var(--font-sm);outline:none;appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;width:100%;}
.settings-item select:focus{border-color:var(--primary);}
.settings-item .readonly{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);height:32px;display:flex;align-items:center;}
/* 充值卡片 */
.recharge-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);}
.recharge-card__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:14px;}
.recharge-amount{display:flex;align-items:center;gap:0;margin-bottom:12px;}
.recharge-amount .currency{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);margin-right:2px;}
.recharge-amount input{border:none;border-bottom:2px solid var(--border);font-size:26px;font-weight:700;outline:none;width:140px;padding:2px 0;}
.recharge-amount input:focus{border-bottom-color:var(--primary);}
.recharge-form-row{display:flex;gap:8px;margin-bottom:12px;}
.recharge-form-item{display:flex;flex-direction:column;gap:4px;flex:1;}
.recharge-form-item label{font-size:var(--font-xs);color:var(--text-secondary);}
.recharge-form-item input,.recharge-form-item select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);outline:none;width:100%;}
.recharge-form-item select{padding-right:24px;appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;cursor:pointer;}
.recharge-form-item input:focus,.recharge-form-item select:focus{border-color:var(--primary);}
.recharge-preview{font-size:var(--font-xs);color:var(--text-secondary);margin-bottom:12px;display:none;}
.recharge-preview span{color:var(--primary);font-weight:600;}
/* 账单列表区 */
.bill-section{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.bill-section__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.bill-section__title{font-size:var(--font-base);font-weight:600;}
.filter-bar{padding:12px 24px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
.filter-item{display:flex;align-items:center;gap:8px;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
/* 表格 */
.table-wrap{overflow-x:auto;}
table{width:100%;border-collapse:collapse;}
thead th{background:#FAFAFA;padding:10px 12px;text-align:left;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
tbody td{padding:12px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid #f5f5f5;white-space:nowrap;}
tbody tr:last-child td{border-bottom:none;}
tbody tr:hover{background:#F8FBFF;}
.pagination{display:flex;align-items:center;justify-content:space-between;padding:12px 24px;border-top:1px solid var(--border);}
.pagination__info{font-size:var(--font-sm);color:var(--text-secondary);}
.pagination__btns{display:flex;align-items:center;gap:4px;}
.page-btn{width:30px;height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-xs);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn:disabled{color:#ccc;cursor:not-allowed;}
.page-btn:disabled:hover{background:#fff;color:#ccc;border-color:var(--border);}
/* 确认弹窗 */
.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.45);z-index:1000;display:none;align-items:center;justify-content:center;}
.modal-overlay.show{display:flex;}
.modal{background:#fff;border-radius:8px;width:440px;max-width:90vw;box-shadow:0 12px 48px rgba(0,0,0,.15);}
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.modal-title{font-size:var(--font-lg);font-weight:600;}
.modal-close{width:32px;height:32px;border:none;background:none;font-size:20px;cursor:pointer;border-radius:var(--radius-sm);color:var(--text-secondary);}
.modal-close:hover{background:#f5f5f5;}
.modal-body{padding:24px;font-size:var(--font-sm);color:var(--text-secondary);line-height:2;}
.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid var(--border);}
/* Toast */
.toast{position:fixed;top:80px;left:50%;transform:translateX(-50%) translateY(-20px);background:rgba(0,0,0,0.75);color:#fff;padding:10px 24px;border-radius:var(--radius-md);font-size:var(--font-sm);opacity:0;pointer-events:none;transition:all .3s;z-index:2000;}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
</style>
</head>
<body>
<!-- 顶部导航 -->
<header class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
营养管理
</div>
<nav class="top-bar__nav">
<a href="trace-query.html">溯源总览</a><a href="arc-employee.html">档案</a>
<a href="farm-land.html">绿色种采</a><a href="sup-hygiene-check.html">卫生供应</a>
<a href="prod-qc-daily.html">健康生产</a><a href="din-dict-mgmt.html" class="active">营养用餐</a>
<a href="ano-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a class="btn-back" href="../../index.html">← 返回首页</a>
<div class="avatar"></div><span>系统管理员 ▾</span>
</div>
</header>
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="sidebar-menu__title">营养用餐</div>
<a href="din-canteen.html" class="sidebar-item"><span class="mi">🏢</span>场所管理</a>
<a href="din-dict-mgmt.html" class="sidebar-item"><span class="mi">📖</span>字典管理</a>
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="this.parentElement.classList.toggle('open')">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>📐 算法标准
</div>
<div class="sidebar-subgroup__body">
<a href="din-algo-population.html" class="sidebar-item"><span class="mi">👥</span>人群基础标准</a>
<a href="din-algo-vip.html" class="sidebar-item"><span class="mi">💎</span>VIP定制标准</a>
</div>
</div>
<a href="din-recipe-mgmt.html" class="sidebar-item"><span class="mi">📋</span>食谱管理</a>
<a href="din-order-meal.html" class="sidebar-item"><span class="mi">🛒</span>营养点餐</a>
<a href="din-user-mgmt.html" class="sidebar-item active"><span class="mi">🧑</span>用户管理</a>
<a href="din-order-line.html" class="sidebar-item"><span class="mi">⚖️</span>餐线管理</a>
<a href="din-order-pickup.html" class="sidebar-item"><span class="mi">🍽️</span>取餐管理</a>
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="this.parentElement.classList.toggle('open')">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>🤖 模型管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-model-llm.html" class="sidebar-item"><span class="mi">🧠</span>大模型管理</a>
<a href="din-model-small.html" class="sidebar-item"><span class="mi">⚙️</span>小模型管理</a>
</div>
</div>
<a href="din-innovation.html" class="sidebar-item"><span class="mi">🌟</span>味养创新</a>
</aside>
<!-- 主内容区 -->
<div class="main">
<!-- 页面头部 -->
<div class="page-header-bar">
<div class="page-header-bar__left">
<a class="page-header-bar__back" href="din-user-mgmt.html">← 返回用户管理</a>
<span class="page-header-bar__title">账户管理</span>
</div>
</div>
<!-- 顶部:三栏布局 -->
<div class="account-top">
<!-- 用户信息 -->
<div class="user-card">
<div class="user-card__avatar" id="userAvatar"></div>
<div class="user-card__info">
<div class="user-card__name" id="userName">张伟</div>
<div class="user-card__code" id="userCode">EMP001</div>
<div class="user-card__tags">
<span class="tag tag-red" id="userLevel">钻石</span>
<span class="tag tag-green" id="userStatus">正常</span>
</div>
</div>
<div class="user-card__balance">
<div class="user-card__balance-label">当前余额</div>
<div class="user-card__balance-value" id="userBalance">¥256.50</div>
</div>
</div>
<!-- 账户设置 -->
<div class="settings-card">
<div class="settings-card__title">账户设置</div>
<div class="settings-row">
<div class="settings-item">
<label>会员等级</label>
<select id="editLevel" onchange="onLevelChange()">
<option value="普通会员">普通会员</option>
<option value="银卡会员">银卡会员</option>
<option value="金卡会员">金卡会员</option>
<option value="钻石会员">钻石会员</option>
</select>
</div>
<div class="settings-item">
<label>会员状态</label>
<select id="editStatus" onchange="onStatusChange()">
<option value="正常">正常</option>
<option value="休眠">休眠</option>
<option value="冻结">冻结</option>
</select>
</div>
<div class="settings-item">
<label>有效期</label>
<input type="date" id="editExpire" style="height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);outline:none;width:100%;">
</div>
</div>
<div class="settings-row">
<div class="settings-item">
<label>当前积分</label>
<div class="readonly" id="editPoints">8,628</div>
</div>
<div class="settings-item">
<label>累计消费</label>
<div class="readonly" id="editSpend">¥12,860</div>
</div>
</div>
<button class="btn btn-primary" onclick="saveSettings()" style="height:30px;font-size:12px;">保存设置</button>
</div>
<!-- 会员充值 -->
<div class="recharge-card">
<div class="recharge-card__title">会员充值</div>
<div class="recharge-amount">
<span class="currency">¥</span>
<input type="number" id="rechargeAmount" placeholder="0.00" min="0.01" step="0.01" oninput="updateRechargePreview()">
</div>
<div class="recharge-preview" id="rechargePreview">
充值后余额 <span id="previewAfter">¥256.50</span>
</div>
<div class="recharge-form-row">
<div class="recharge-form-item">
<label>支付方式</label>
<select id="rechargeMethod">
<option>微信支付</option><option>现金支付</option><option>银行转账</option><option>支付宝支付</option><option>后台挂账</option>
</select>
</div>
<div class="recharge-form-item">
<label>备注</label>
<input type="text" id="rechargeRemark" placeholder="选填">
</div>
</div>
<button class="btn btn-primary" onclick="confirmRecharge()" style="height:30px;font-size:12px;width:100%;">确认充值</button>
</div>
</div>
<!-- 下方:账单明细列表 -->
<div class="bill-section">
<div class="bill-section__header">
<div class="bill-section__title">账单明细</div>
<span id="billCount" style="font-size:var(--font-sm);color:var(--text-placeholder);"></span>
</div>
<div class="filter-bar">
<div class="filter-item">
<label>交易类型</label>
<select class="filter-select" id="billTypeFilter" onchange="renderBills()">
<option value="">全部</option>
<option value="消费">消费</option>
<option value="充值">充值</option>
<option value="提现">提现</option>
</select>
</div>
<div class="filter-item">
<label>支付方式</label>
<select class="filter-select" id="billPayFilter" onchange="renderBills()">
<option value="">全部</option>
<option value="微信支付">微信支付</option>
<option value="余额支付">余额支付</option>
<option value="现金支付">现金支付</option>
<option value="支付宝支付">支付宝支付</option>
</select>
</div>
<div class="filter-item">
<label>交易状态</label>
<select class="filter-select" id="billStatusFilter" onchange="renderBills()">
<option value="">全部</option>
<option value="交易成功">交易成功</option>
<option value="退款">退款</option>
</select>
</div>
<button class="btn btn-default" onclick="resetBillFilter()" style="height:30px;font-size:12px;">重置</button>
</div>
<div class="table-wrap" id="billTable"></div>
<div class="pagination" id="billPagination"></div>
</div>
</div>
<!-- 充值确认弹窗 -->
<div class="modal-overlay" id="rechargeConfirmModal">
<div class="modal">
<div class="modal-header">
<div class="modal-title">确认充值</div>
<button class="modal-close" onclick="closeRechargeModal()">&times;</button>
</div>
<div class="modal-body" id="rechargeConfirmBody"></div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeRechargeModal()">取消</button>
<button class="btn btn-primary" onclick="submitRecharge()">确认</button>
</div>
</div>
</div>
<!-- Toast -->
<div class="toast" id="toast"></div>
<script>
(function(){var sub=document.querySelector('.sidebar-item.active');if(sub){var sg=sub.closest('.sidebar-subgroup');if(sg)sg.classList.add('open');}})();
/* ========== 账户数据 ========== */
var accountDataMap = {
'张伟': {code:'EMP001',balance:256.50,level:'钻石会员',status:'正常',expire:'2027-01-14',points:'8,628',spend:'¥12,860',
bills:[
{name:'午餐消费',type:'消费',time:'2026-08-03 11:32:15',payMethod:'余额支付',status:'交易成功',amount:'-28.50',payNo:'PAY20260803113215001',orderNo:'NM20260803113215001',merchant:'二号食堂'},
{name:'早餐消费',type:'消费',time:'2026-08-03 07:45:08',payMethod:'余额支付',status:'交易成功',amount:'-15.00',payNo:'PAY20260803074508002',orderNo:'NM20260803074508002',merchant:'二号食堂'},
{name:'午餐消费',type:'消费',time:'2026-08-01 12:10:33',payMethod:'余额支付',status:'交易成功',amount:'-32.00',payNo:'PAY20260801121033003',orderNo:'NM20260801121033003',merchant:'二号食堂'},
{name:'充值',type:'充值',time:'2026-08-01 09:00:21',payMethod:'微信支付',status:'交易成功',amount:'+200.00',payNo:'PAY20260801090021004',orderNo:'RC20260801090021004',merchant:'—'},
{name:'余额提现',type:'提现',time:'2026-07-25 15:30:00',payMethod:'余额支付',status:'交易成功',amount:'-100.00',payNo:'PAY20260725153000005',orderNo:'WD20260725153000005',merchant:'—'},
{name:'午餐消费',type:'消费',time:'2026-07-31 11:45:12',payMethod:'余额支付',status:'交易成功',amount:'-35.00',payNo:'PAY20260731114512005',orderNo:'NM20260731114512005',merchant:'二号食堂'},
{name:'晚餐消费',type:'消费',time:'2026-07-30 18:20:44',payMethod:'微信支付',status:'交易成功',amount:'-42.00',payNo:'PAY20260730182044006',orderNo:'NM20260730182044006',merchant:'二号食堂'},
{name:'早餐消费',type:'消费',time:'2026-07-30 07:30:18',payMethod:'余额支付',status:'交易成功',amount:'-12.00',payNo:'PAY20260730073018007',orderNo:'NM20260730073018007',merchant:'二号食堂'},
{name:'充值',type:'充值',time:'2026-07-28 10:15:55',payMethod:'现金支付',status:'交易成功',amount:'+100.00',payNo:'PAY20260728101555008',orderNo:'RC20260728101555008',merchant:'—'},
{name:'午餐消费',type:'消费',time:'2026-06-30 12:00:03',payMethod:'余额支付',status:'交易成功',amount:'-28.00',payNo:'PAY20260630120003009',orderNo:'NM20260630120003009',merchant:'二号食堂'},
{name:'晚餐消费',type:'消费',time:'2026-06-29 18:00:27',payMethod:'支付宝支付',status:'退款',amount:'-10.00',payNo:'PAY20260629180027010',orderNo:'NM20260629180027010',merchant:'二号食堂'},
{name:'充值',type:'充值',time:'2026-06-25 14:30:41',payMethod:'微信支付',status:'交易成功',amount:'+50.00',payNo:'PAY20260625143041011',orderNo:'RC20260625143041011',merchant:'—'},
{name:'午餐消费',type:'消费',time:'2026-06-20 12:15:09',payMethod:'余额支付',status:'交易成功',amount:'-22.00',payNo:'PAY20260620121509012',orderNo:'NM20260620121509012',merchant:'二号食堂'},
{name:'早餐消费',type:'消费',time:'2026-06-19 08:00:36',payMethod:'余额支付',status:'交易成功',amount:'-8.00',payNo:'PAY20260619080036013',orderNo:'NM20260619080036013',merchant:'二号食堂'},
{name:'充值',type:'充值',time:'2026-06-10 16:20:15',payMethod:'银行转账',status:'交易成功',amount:'+300.00',payNo:'PAY20260610162015014',orderNo:'RC20260610162015014',merchant:'—'}
]},
'李娜': {code:'EMP002',balance:512.00,level:'金卡会员',status:'正常',expire:'2027-05-07',points:'3,286',spend:'¥4,820',
bills:[
{name:'午餐消费',type:'消费',time:'2026-08-02 12:00:10',payMethod:'余额支付',status:'交易成功',amount:'-22.00',payNo:'PAY20260802120010001',orderNo:'NM20260802120010001',merchant:'二号食堂'},
{name:'晚餐消费',type:'消费',time:'2026-08-01 18:30:22',payMethod:'现金支付',status:'交易成功',amount:'-35.00',payNo:'PAY20260801183022002',orderNo:'NM20260801183022002',merchant:'二号食堂'},
{name:'充值',type:'充值',time:'2026-07-20 09:00:00',payMethod:'微信支付',status:'交易成功',amount:'+200.00',payNo:'PAY20260720090000003',orderNo:'RC20260720090000003',merchant:'—'}
]},
'赵敏': {code:'EMP004',balance:320.00,level:'银卡会员',status:'正常',expire:'2027-08-11',points:'1,420',spend:'¥2,180',
bills:[
{name:'午餐消费',type:'消费',time:'2026-08-02 11:30:00',payMethod:'余额支付',status:'交易成功',amount:'-32.00',payNo:'PAY20260802113000001',orderNo:'NM20260802113000001',merchant:'二号食堂'},
{name:'充值',type:'充值',time:'2026-07-15 09:00:00',payMethod:'现金支付',status:'交易成功',amount:'+100.00',payNo:'PAY20260715090000002',orderNo:'RC20260715090000002',merchant:'—'}
]},
'刘芳': {code:'EMP006',balance:678.00,level:'银卡会员',status:'正常',expire:'2027-10-19',points:'986',spend:'¥1,520',
bills:[
{name:'午餐消费',type:'消费',time:'2026-08-01 12:00:00',payMethod:'余额支付',status:'交易成功',amount:'-18.00',payNo:'PAY20260801120000001',orderNo:'NM20260801120000001',merchant:'二号食堂'}
]},
'孙磊': {code:'EMP010',balance:156.00,level:'金卡会员',status:'即将到期',expire:'2026-12-07',points:'5,120',spend:'¥7,860',
bills:[
{name:'早餐消费',type:'消费',time:'2026-07-15 08:00:00',payMethod:'余额支付',status:'交易成功',amount:'-10.00',payNo:'PAY20260715080000001',orderNo:'NM20260715080000001',merchant:'二号食堂'}
]},
'周明': {code:'EMP008',balance:3200.00,level:'钻石会员',status:'休眠',expire:'2027-02-09',points:'12,486',spend:'¥16,820',
bills:[
{name:'充值',type:'充值',time:'2026-04-15 10:00:00',payMethod:'银行转账',status:'交易成功',amount:'+2000.00',payNo:'PAY20260415100000001',orderNo:'RC20260415100000001',merchant:'—'},
{name:'午餐消费',type:'消费',time:'2026-03-28 12:15:00',payMethod:'余额支付',status:'交易成功',amount:'-28.00',payNo:'PAY20260328121500002',orderNo:'NM20260328121500002',merchant:'二号食堂'}
]}
};
/* ========== 分页状态 ========== */
var currentPage = 1;
var pageSize = 8;
/* ========== 初始化 ========== */
function getQueryParam(key) {
var m = location.search.match(new RegExp('[?&]' + key + '=([^&]*)'));
return m ? decodeURIComponent(m[1]) : '';
}
(function init() {
var name = getQueryParam('name') || '张伟';
var data = accountDataMap[name] || accountDataMap['张伟'];
window._currentUser = { name: name, data: data };
/* 用户卡片 */
document.getElementById('userAvatar').textContent = name[0];
document.getElementById('userName').textContent = name;
document.getElementById('userCode').textContent = data.code;
document.getElementById('userBalance').textContent = '¥' + data.balance.toFixed(2);
/* 等级/状态 */
var levelColors = {'普通会员':'tag-gray','银卡会员':'tag-blue','金卡会员':'tag-orange','钻石会员':'tag-red'};
var levelEl = document.getElementById('userLevel');
levelEl.textContent = data.level;
levelEl.className = 'tag ' + (levelColors[data.level] || 'tag-gray');
var statusColors = {'正常':'tag-green','休眠':'tag-gray','冻结':'tag-red','即将到期':'tag-orange'};
var statusEl = document.getElementById('userStatus');
statusEl.textContent = data.status;
statusEl.className = 'tag ' + (statusColors[data.status] || 'tag-green');
/* 设置表单 */
document.getElementById('editLevel').value = data.level;
document.getElementById('editStatus').value = data.status;
document.getElementById('editPoints').textContent = data.points;
document.getElementById('editSpend').textContent = data.spend;
if (data.expire) document.getElementById('editExpire').value = data.expire;
renderBills();
})();
/* ========== 账单渲染 ========== */
function getFilteredBills() {
var bills = window._currentUser.data.bills.slice();
var typeFilter = document.getElementById('billTypeFilter').value;
var payFilter = document.getElementById('billPayFilter').value;
var statusFilter = document.getElementById('billStatusFilter').value;
if (typeFilter) bills = bills.filter(function(b){return b.type===typeFilter;});
if (payFilter) bills = bills.filter(function(b){return b.payMethod===payFilter;});
if (statusFilter) bills = bills.filter(function(b){return b.status===statusFilter;});
return bills;
}
function renderBills() {
var bills = getFilteredBills();
var total = bills.length;
var totalPages = Math.ceil(total / pageSize) || 1;
if (currentPage > totalPages) currentPage = totalPages;
var start = (currentPage - 1) * pageSize;
var pageBills = bills.slice(start, start + pageSize);
document.getElementById('billCount').textContent = '共 ' + total + ' 条';
var thead = '<table><thead><tr><th>交易名称</th><th>交易类型</th><th>交易时间</th><th>支付方式</th><th>交易状态</th><th>金额</th><th>支付流水号</th><th>交易单号</th><th>商户名称</th></tr></thead><tbody>';
var rows = '';
pageBills.forEach(function(b){
var typeTagClass = b.type==='消费'?'tag-blue':(b.type==='充值'?'tag-green':(b.type==='提现'?'tag-red':'tag-gray'));
var statusTagClass = b.status==='交易成功'?'tag-green':'tag-red';
var amountStyle = b.amount.indexOf('+')===0?'color:#52c41a;font-weight:500;':'';
rows += '<tr>'
+ '<td>'+b.name+'</td>'
+ '<td><span class="tag '+typeTagClass+'">'+b.type+'</span></td>'
+ '<td>'+b.time+'</td>'
+ '<td>'+b.payMethod+'</td>'
+ '<td><span class="tag '+statusTagClass+'">'+b.status+'</span></td>'
+ '<td style="'+amountStyle+'">'+b.amount+'</td>'
+ '<td style="font-family:monospace;font-size:11px;">'+b.payNo+'</td>'
+ '<td style="font-family:monospace;font-size:11px;">'+b.orderNo+'</td>'
+ '<td>'+b.merchant+'</td>'
+ '</tr>';
});
if (!total) {
rows = '<tr><td colspan="9" style="text-align:center;padding:60px 24px;color:var(--text-placeholder);font-size:var(--font-base);">暂无符合条件的账单记录</td></tr>';
}
document.getElementById('billTable').innerHTML = thead + rows + '</tbody></table>';
/* 分页 */
var pagHtml = '<div class="pagination__info">显示 '+(total?start+1:0)+'-'+Math.min(start+pageSize,total)+' 条,共 '+total+' 条</div>';
pagHtml += '<div class="pagination__btns"><button class="page-btn" '+(currentPage<=1?'disabled':'')+' onclick="goPage('+(currentPage-1)+')"></button>';
for (var i=1;i<=totalPages;i++) {
pagHtml += '<button class="page-btn'+(i===currentPage?' active':'')+'" onclick="goPage('+i+')">'+i+'</button>';
}
pagHtml += '<button class="page-btn" '+(currentPage>=totalPages?'disabled':'')+' onclick="goPage('+(currentPage+1)+')"></button></div>';
document.getElementById('billPagination').innerHTML = pagHtml;
}
function goPage(page) {
currentPage = page;
renderBills();
}
function resetBillFilter() {
document.getElementById('billTypeFilter').value = '';
document.getElementById('billPayFilter').value = '';
document.getElementById('billStatusFilter').value = '';
currentPage = 1;
renderBills();
}
/* ========== 账户设置 ========== */
function onLevelChange() {
/* 实时更新标签 */
}
function onStatusChange() {
/* 实时更新标签 */
}
function saveSettings() {
var newLevel = document.getElementById('editLevel').value;
var newStatus = document.getElementById('editStatus').value;
var newExpire = document.getElementById('editExpire').value;
var data = window._currentUser.data;
data.level = newLevel;
data.status = newStatus;
data.expire = newExpire;
var levelColors = {'普通会员':'tag-gray','银卡会员':'tag-blue','金卡会员':'tag-orange','钻石会员':'tag-red'};
var le = document.getElementById('userLevel'); le.textContent = newLevel; le.className = 'tag ' + (levelColors[newLevel]||'tag-gray');
var statusColors = {'正常':'tag-green','休眠':'tag-gray','冻结':'tag-red','即将到期':'tag-orange'};
var se = document.getElementById('userStatus'); se.textContent = newStatus; se.className = 'tag ' + (statusColors[newStatus]||'tag-green');
showToast('账户设置保存成功');
}
/* ========== 手动充值 ========== */
function updateRechargePreview() {
var amount = parseFloat(document.getElementById('rechargeAmount').value) || 0;
var preview = document.getElementById('rechargePreview');
if (amount > 0) {
preview.style.display = 'block';
document.getElementById('previewAfter').textContent = '¥' + (window._currentUser.data.balance + amount).toFixed(2);
} else {
preview.style.display = 'none';
}
}
function confirmRecharge() {
var amount = parseFloat(document.getElementById('rechargeAmount').value);
if (!amount || amount <= 0) { showToast('请输入有效的充值金额'); return; }
var method = document.getElementById('rechargeMethod').value;
var remark = document.getElementById('rechargeRemark').value || '无';
var after = (window._currentUser.data.balance + amount).toFixed(2);
var body = '<div style="display:flex;flex-direction:column;gap:10px;">'
+ '<div style="display:flex;justify-content:space-between;"><span>充值金额</span><span style="font-weight:600;color:var(--primary);">¥' + amount.toFixed(2) + '</span></div>'
+ '<div style="display:flex;justify-content:space-between;"><span>支付方式</span><span>' + method + '</span></div>'
+ '<div style="display:flex;justify-content:space-between;"><span>充值前余额</span><span>¥' + window._currentUser.data.balance.toFixed(2) + '</span></div>'
+ '<div style="display:flex;justify-content:space-between;"><span>充值后余额</span><span style="font-weight:600;color:var(--primary);">¥' + after + '</span></div>'
+ '<div style="display:flex;justify-content:space-between;"><span>备注</span><span>' + remark + '</span></div></div>';
document.getElementById('rechargeConfirmBody').innerHTML = body;
document.getElementById('rechargeConfirmModal').classList.add('show');
}
function closeRechargeModal() {
document.getElementById('rechargeConfirmModal').classList.remove('show');
}
function submitRecharge() {
var amount = parseFloat(document.getElementById('rechargeAmount').value);
var method = document.getElementById('rechargeMethod').value;
var remark = document.getElementById('rechargeRemark').value || '';
var data = window._currentUser.data;
/* 更新余额 */
data.balance = data.balance + amount;
document.getElementById('userBalance').textContent = '¥' + data.balance.toFixed(2);
/* 新增账单 */
var now = new Date();
var pad = function(n){return String(n).padStart(2,'0');};
var timeStr = now.getFullYear()+'-'+pad(now.getMonth()+1)+'-'+pad(now.getDate())+' '+pad(now.getHours())+':'+pad(now.getMinutes())+':'+pad(now.getSeconds());
var seq = String(data.bills.length + 1).padStart(5,'0');
data.bills.unshift({
name:'充值', type:'充值', time:timeStr, payMethod:method,
status:'交易成功', amount:'+'+amount.toFixed(2),
payNo:'PAY'+now.getFullYear()+pad(now.getMonth()+1)+pad(now.getDate())+pad(now.getHours())+pad(now.getMinutes())+pad(now.getSeconds())+seq,
orderNo:'RC'+now.getFullYear()+pad(now.getMonth()+1)+pad(now.getDate())+pad(now.getHours())+pad(now.getMinutes())+pad(now.getSeconds())+seq,
merchant:'后台充值' + (remark ? ''+remark+'' : '')
});
closeRechargeModal();
document.getElementById('rechargeAmount').value = '';
document.getElementById('rechargePreview').style.display = 'none';
currentPage = 1;
renderBills();
showToast('充值成功!已到账 ¥' + amount.toFixed(2));
}
/* ========== Toast ========== */
function showToast(msg) {
var el = document.getElementById('toast');
el.textContent = msg;
el.classList.add('show');
setTimeout(function(){el.classList.remove('show');},2000);
}
document.getElementById('rechargeConfirmModal').addEventListener('click',function(e){if(e.target===this)closeRechargeModal();});
</script>
</body>
</html>
+35 -8
View File
@@ -334,6 +334,7 @@ tbody td.td-actions{overflow:visible;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -352,7 +353,16 @@ tbody td.td-actions{overflow:visible;}
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -507,15 +517,15 @@ tbody td.td-actions{overflow:visible;}
<div class="table-card__content">
<table>
<thead>
<tr><th>会员卡号</th><th>姓名</th><th>用户编号</th><th>会员等级</th><th>当前积分</th><th>累计消费(元)</th><th>本月消费</th><th>开通日期</th><th>有效期至</th><th>最近活跃</th><th>会员状态</th><th>操作</th></tr>
<tr><th>会员卡号</th><th>姓名</th><th>用户编号</th><th>会员等级</th><th>当前积分</th><th>累计消费(元)</th><th>本月消费</th><th>开通日期</th><th>有效期至</th><th>最近活跃</th><th>账号余额</th><th>会员状态</th><th>操作</th></tr>
</thead>
<tbody>
<tr><td>MB-202401-001</td><td>张伟</td><td>EMP001</td><td><span class="tag tag-red">钻石</span></td><td>8,628</td><td>12,860</td><td>¥1,286</td><td>2024-01-15</td><td>2027-01-14</td><td>2026-04-28 11:32</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>MB-202405-008</td><td>李娜</td><td>EMP002</td><td><span class="tag tag-orange">金卡</span></td><td>3,286</td><td>4,820</td><td>¥682</td><td>2024-05-08</td><td>2027-05-07</td><td>2026-04-28 12:08</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>MB-202508-015</td><td>赵敏</td><td>EMP004</td><td><span class="tag tag-blue">银卡</span></td><td>1,420</td><td>2,180</td><td>¥486</td><td>2025-08-12</td><td>2027-08-11</td><td>2026-04-28 11:30</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>MB-202510-026</td><td>刘芳</td><td>EMP006</td><td><span class="tag tag-blue">银卡</span></td><td>986</td><td>1,520</td><td>¥320</td><td>2025-10-20</td><td>2027-10-19</td><td>2026-04-28 11:48</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>MB-202312-003</td><td>孙磊</td><td>EMP010</td><td><span class="tag tag-orange">金卡</span></td><td>5,120</td><td>7,860</td><td>¥0</td><td>2023-12-08</td><td>2026-12-07</td><td>2026-04-15 12:20</td><td><span class="tag tag-orange">即将到期</span></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>MB-202402-005</td><td>周明</td><td>EMP008</td><td><span class="tag tag-red">钻石</span></td><td>12,486</td><td>16,820</td><td>¥0</td><td>2024-02-10</td><td>2027-02-09</td><td>2026-03-28 12:15</td><td><span class="tag tag-gray">休眠</span></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>MB-202401-001</td><td>张伟</td><td>EMP001</td><td><span class="tag tag-red">钻石</span></td><td>8,628</td><td>12,860</td><td>¥1,286</td><td>2024-01-15</td><td>2027-01-14</td><td>2026-04-28 11:32</td><td>¥256.50</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
<tr><td>MB-202405-008</td><td>李娜</td><td>EMP002</td><td><span class="tag tag-orange">金卡</span></td><td>3,286</td><td>4,820</td><td>¥682</td><td>2024-05-08</td><td>2027-05-07</td><td>2026-04-28 12:08</td><td>¥512.00</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
<tr><td>MB-202508-015</td><td>赵敏</td><td>EMP004</td><td><span class="tag tag-blue">银卡</span></td><td>1,420</td><td>2,180</td><td>¥486</td><td>2025-08-12</td><td>2027-08-11</td><td>2026-04-28 11:30</td><td>¥320.00</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
<tr><td>MB-202510-026</td><td>刘芳</td><td>EMP006</td><td><span class="tag tag-blue">银卡</span></td><td>986</td><td>1,520</td><td>¥320</td><td>2025-10-20</td><td>2027-10-19</td><td>2026-04-28 11:48</td><td>¥678.00</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
<tr><td>MB-202312-003</td><td>孙磊</td><td>EMP010</td><td><span class="tag tag-orange">金卡</span></td><td>5,120</td><td>7,860</td><td>¥0</td><td>2023-12-08</td><td>2026-12-07</td><td>2026-04-15 12:20</td><td>¥156.00</td><td><span class="tag tag-orange">即将到期</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
<tr><td>MB-202402-005</td><td>周明</td><td>EMP008</td><td><span class="tag tag-red">钻石</span></td><td>12,486</td><td>16,820</td><td>¥0</td><td>2024-02-10</td><td>2027-02-09</td><td>2026-03-28 12:15</td><td>¥3,200.00</td><td><span class="tag tag-gray">休眠</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
</tbody>
</table>
</div>
@@ -1007,6 +1017,23 @@ document.querySelectorAll('.tab-panel')[0].querySelectorAll('tbody tr').forEach(
if (deleteLink) deleteLink.addEventListener('click', () => { closeAllMoreTips(); if(confirm('确定要删除用户「'+name+'」吗?此操作不可恢复。')){ row.remove(); } });
});
/* ========== 会员账户事件绑定(Tab3 ========== */
document.querySelectorAll('.tab-panel')[2].querySelectorAll('tbody tr').forEach(row => {
var tds = row.querySelectorAll('td');
var name = tds[1] ? tds[1].textContent : '';
// 详情
var detailLink = row.querySelector('[data-action="detail"]');
if (detailLink) detailLink.addEventListener('click', function() { showUserProfile(name); });
// 账户管理
var accountLink = row.querySelector('[data-action="account"]');
if (accountLink) accountLink.addEventListener('click', function() {
window.location.href = 'din-user-account.html?name=' + encodeURIComponent(name);
});
// 人脸采集
var faceLink = row.querySelector('[data-action="face"]');
if (faceLink) faceLink.addEventListener('click', function() { showFaceModal(name); });
});
/* ========== 人脸采集弹窗 ========== */
// 模拟:张伟、李娜有人脸数据,其他人无
const faceDataMap = { '张伟': true, '李娜': true };
+7
View File
@@ -1,5 +1,12 @@
# 营养用餐管理端 CHANGELOG
## [2026-08-03]
### 后台营养管理
- **`din-dish-own.html`**(自有餐品库 · 新增页面):营养用餐侧边栏新增「自有餐品库」菜单项,位于「餐品总库」与「算法标准」之间。页面内容暂复制自 `din-dish-all.html`(餐品总库),后续将做差异化。同步更新全部 26 个营养用餐分组页面的侧边栏导航及 `index.html` SITE_MAP。
- **`din-content-center.html`**(内容中心 · 新增页面):营养用餐侧边栏「终端管理」升级为二级可展开菜单组,下设「设备管理」(原 `din-terminal.html`)和「内容中心」(新增)两个三级菜单项。内容中心用于管理投放到终端设备上的物料信息(图片/文字/HTML/视频),支持按设备类型或指定设备配置关联范围,提供物料发布/下架/预览等操作。同步更新全部 27 个营养用餐分组页面的侧边栏导航及 `index.html` SITE_MAP。
## [2026-07-24]
### 后台营养管理
@@ -41,33 +41,31 @@ CQ 能源作为中国最大的油气田之一,员工分布广泛、工作环
### 2.0 用户群体概览
营养管理平台共有 **7 类**核心用户群体,其中管理端 4 类、小程序端 3 类。各群体的定位与操作范围如下:
营养管理平台共有 **5 类**核心用户群体,其中管理端 4 类、小程序端 1 类。各群体的定位与操作范围如下:
**管理端用户(4 类)**
| 序号 | 用户群体 | 主要定位 | 操作范围 |
|------|----------|----------|----------|
| 1 | 营养师/配餐管理员 | 营养标准制定者与餐品/食谱管理者,关注营养科学性与算法迭代 | 营养用餐(食堂/菜品/食材/食谱/算法/模型/数据/创新)+ 健康生产(食谱排期/营养质检)+ 档案(员工/食堂/单位报表)+ 溯源查询 |
| 2 | 食堂运营管理员 | 食堂生产运营执行者,关注生产流程标准化与出餐效率 | 营养用餐(点餐/排队取餐/取餐管理/终端)+ 健康生产(净菜下单/配菜组合/烹饪加工/出餐管理/余量处理/取餐柜/加工统计/日常质检) |
| 1 | 营养师 | 营养标准制定者与餐品/食谱管理者,关注营养科学性与算法迭代 | 营养用餐(食堂/菜品/食材/食谱/算法/模型/数据/创新)+ 健康生产(食谱排期/营养质检)+ 档案(员工/食堂/单位报表) |
| 2 | 食堂管理员 | 食堂生产运营执行者,关注生产流程标准化与出餐效率 | 营养用餐(点餐/排队取餐/取餐管理/终端)+ 健康生产(净菜下单/配菜组合/烹饪加工/出餐管理/余量处理/取餐柜/加工统计/日常质检)+ 溯源管理 |
| 3 | 供应链管理员 | 食材供应链全程管理者,关注源头品质与加工配送合规 | 绿色种采(土地/种植养殖监控/收成入库/采供/供商)+ 卫生供应(毛菜入库/卫生检测/仓储/净菜加工/净菜入库/净菜包装/净菜内供/净菜外售)+ 溯源管理 |
| 4 | 系统管理员 | 平台基础设施与数据治理者,关注系统稳定与数据质量 | 营养用餐(用户/字典/大模型/小模型)+ 异常事件 + 边缘服务 + 档案(算法模型) |
| 4 | 系统管理员 | 平台基础设施与数据治理者,关注系统稳定与数据质量 | 营养用餐(用户/字典/大模型/小模型)+ 异常事件 + 边缘服务 + 档案(算法模型)+ 溯源管理 |
**小程序端用户(3 类)**
**小程序端用户(1 类)**
| 序号 | 用户群体 | 主要定位 | 操作范围 |
|------|----------|----------|----------|
| 5 | 普通员工 | 一线就餐用户,关注日常饮食便捷性营养摄入可见 | 吃模块(订餐/营养明细/就餐记录/拍照补录/溯源查询)+ 我的模块(订单/健康档案/账户/余额/会员/消息) |
| 6 | VIP 定制用户 | 个性化营养需求员工,关注专属方案与营养师指导 | 在普通员工基础上,重点使用 AI 营养咨询与定制食谱推荐 |
| 7 | 美食创作者 | UGC 内容创作者,关注展示厨艺与社区收益 | 做模块(创作食谱/排行/分享赚钱/热门/我的创作) |
| 5 | 普通用户 | 一线就餐用户,关注日常饮食便捷性营养摄入可见与个性化营养需求 | 吃模块(订餐/营养明细/就餐记录/拍照补录/溯源查询)+ AI 营养模块(营养咨询/智能问答)+ 做模块(创作食谱/排行/分享赚钱/热门/我的创作)+ 我的模块(订单/健康档案/账户/余额/会员/消息) |
> 上述 7 类用户群体在第五章「模块功能概要说明」中作为受众群体的唯一引用来源。每类群体下设的具体岗位(如食堂运营管理员下的厨师、配菜员等)在 2.1/2.2 详细画像中说明。
> 上述 5 类用户群体在第五章「模块功能概要说明」中作为受众群体的唯一引用来源。每类群体下设的具体岗位(如食堂管理员下的厨师、配菜员等)在 2.1/2.2 详细画像中说明。
### 2.1 管理端用户画像
#### 营养师/配餐管理员
#### 营养师
- **涵盖岗位**:营养师、配餐管理员、营养质检员、单位报表查看者(单位管理者/工会)
- **职责**:制定营养标准、规划食谱、监控营养数据、管理 VIP 定制方案
- **涵盖岗位**:营养师、营养质检员
- **职责**:制定营养标准、规划食谱、监控营养数据
- **核心诉求**:科学配餐工具、营养数据可视化、AI 辅助决策
- **使用功能**
@@ -86,10 +84,10 @@ CQ 能源作为中国最大的油气田之一,员工分布广泛、工作环
| 档案 | 食堂报表数据 | 查看食堂维度营养汇总 |
| 档案 | 单位报表数据 | 查看单位维度营养汇总 |
#### 食堂运营管理员
#### 食堂管理员
- **涵盖岗位**:食堂管理员、配菜员、厨师、出餐管理员、取餐核销员、食安员、食堂申领员
- **职责**:管理生产流程、监控食品安全、处理订单、管理终端设备
- **涵盖岗位**:食堂管理员、配菜员、厨师、出餐管理员、取餐核销员、食安员、食堂申领员、单位报表查看者(单位管理者/工会)
- **职责**:管理生产流程、监控食品安全、处理订单、管理终端设备、食材溯源查询
- **核心诉求**:生产流程标准化、实时监控、异常预警、成本控制
- **使用功能**
@@ -108,6 +106,9 @@ CQ 能源作为中国最大的油气田之一,员工分布广泛、工作环
| 健康生产 | 加工统计 | 查看生产数据、成本利润分析 |
| 健康生产 | 日常质检 | 视频监控、留样管理、健康证管理 |
| 营养用餐 | 效益数据 | 食材损耗率、毛利率分析 |
| 溯源管理 | 食材溯源 / 食材链路 | 多维度食材溯源查询与时间线 |
| 溯源管理 | 餐品溯源 / 餐品链路 | 多维度餐品溯源查询与时间线 |
| 档案 | 单位报表数据 | 查看单位维度营养汇总 |
#### 供应链管理员
@@ -138,7 +139,7 @@ CQ 能源作为中国最大的油气田之一,员工分布广泛、工作环
#### 系统管理员
- **涵盖岗位**:系统管理员、IT 运维管理员、数据分析师、财务对账员
- **职责**:用户权限管理、字典配置、模型管理、异常事件处理、边缘节点运维
- **职责**:用户权限管理、字典配置、模型管理、异常事件处理、边缘节点运维、食材溯源查询
- **核心诉求**:系统稳定运行、数据质量保障、灵活配置
- **使用功能**
@@ -154,26 +155,16 @@ CQ 能源作为中国最大的油气田之一,员工分布广泛、工作环
| 边缘服务 | 数据上报管理 | 终端数据上报记录与统计 |
| 边缘服务 | 数据下发管理 / 详情 | 云端到边缘节点的下发任务与轨迹 |
| 边缘服务 | 异常告警中心 | 全平台异常告警汇总与处置 |
| 溯源管理 | 食材溯源 / 食材链路 | 多维度食材溯源查询与时间线 |
| 溯源管理 | 餐品溯源 / 餐品链路 | 多维度餐品溯源查询与时间线 |
### 2.2 小程序端用户画像
#### 普通员工(基础用户
#### 普通用户
- **特征**:能源一线/机关员工,关注日常饮食便捷性
- **核心诉求**:快速点餐、了解营养摄入、查看食材来源
- **使用场景**:每日点餐、查看就餐记录、营养明细、扫码溯源
#### VIP 定制用户
- **特征**:有特殊营养需求(减脂、增肌、控糖、术后恢复等)
- **核心诉求**:个性化营养方案、专属食谱推荐、营养师一对一指导
- **使用场景**:AI 营养咨询、定制食谱、营养目标追踪
#### 美食创作者
- **特征**:热爱烹饪、乐于分享的员工群体
- **核心诉求**:展示厨艺、获得社区认可、赚取收益
- **使用场景**:发布食谱、参与排行、分享赚钱
- **特征**:能源一线/机关员工,关注日常饮食便捷性、营养摄入可见与个性化营养需求
- **核心诉求**:快速点餐、了解营养摄入、查看食材来源、AI 营养咨询、社区互动
- **使用场景**:每日点餐、查看就餐记录、营养明细、扫码溯源、AI 营养咨询、食谱创作与分享
---
@@ -427,11 +418,11 @@ flowchart LR
### 5.1 管理端 — 溯源管理模块(trace-*,4 页)
**模块定位**:贯穿全链路的质量保障节点,供食管理员、营养师与食堂管理员从食材或餐品维度任意节点回溯,是问题定位与员工咨询答复的依据来源。上游输入为卫生供应、健康生产、绿色种采各环节沉淀的溯源数据,下游输出为异常事件处置与档案报表。
**模块定位**:贯穿全链路的质量保障节点,供食管理员、系统管理员从食材或餐品维度任意节点回溯,是问题定位与员工咨询答复的依据来源。上游输入为卫生供应、健康生产、绿色种采各环节沉淀的溯源数据,下游输出为异常事件处置与档案报表。
| 页面 | 功能说明 |
|------|----------|
| 食材溯源(trace-query | 食管理员/营养师在食材抽检或异常反馈时,按溯源码、食材名称、来源类型、入库时间、产地/供应商、种养殖批次、检测结论等多维度查询食材批次,快速定位问题源头与影响范围 |
| 食材溯源(trace-query | 食管理员/系统管理员在食材抽检或异常反馈时,按溯源码、食材名称、来源类型、入库时间、产地/供应商、种养殖批次、检测结论等多维度查询食材批次,快速定位问题源头与影响范围 |
| 食材链路(trace-timeline-ingredient | 在食材溯源结果上展开完整链路时间线,展示食材从田间采收入库、卫生检测、净菜加工、净菜包装到烹制出餐的全流转节点,支持反向追溯定位问题环节 |
| 餐品溯源(trace-dish) | 食堂管理员从餐品维度追溯其构成食材的来源、检测与加工信息,用于餐品质量审计、员工咨询答复与 VIP 用户餐品透明度核查 |
| 餐品链路(trace-timeline-dish | 展开餐品维度完整链路,含餐品烹制单号、烹制厨师、构成食材数量等,支持餐品维度的全程透明追溯,与食材链路形成双维度互补 |
+11 -1
View File
@@ -117,6 +117,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -135,7 +136,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -199,6 +199,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -217,7 +218,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+11 -1
View File
@@ -194,6 +194,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-own.html" class="sidebar-item"><span class="mi">📦</span>自有餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -212,7 +213,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🖥️ 终端管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-terminal.html" class="sidebar-item"><span class="mi">📟</span>设备管理</a>
<a href="din-content-center.html" class="sidebar-item"><span class="mi">📦</span>内容中心</a>
</div>
</div>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>