feat: 餐线营养秤取餐页重构 + 收银设备副屏U02三栏表格化

- 餐线营养秤 03-meal-pickup: 报警层移至屏幕边缘 + 推荐热量居中 + 取消"取餐中"标签
- 餐线营养秤 03-meal-pickup-dual: 大屏中部删长条 + 餐品区改视频铺满
- 餐线营养秤 02-dish-display: 常规模式放大菜品图 + 视频模式改左右布局
- 收银设备 user-02-checkout-sync: 副屏改三栏表格化布局 + 列表垂直滚动 + 营养素改NRV%
- 收银设备: 恢复 user-03-paying 支付引导页 + user-03-success 重命名为 user-04-success
- 同步更新 index.html 导航 + 两个模块 CHANGELOG

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
fengpu
2026-08-07 13:38:41 +08:00
co-authored by Claude Opus 4.7
parent 56d5aa29ba
commit cfb30a45ee
9 changed files with 1131 additions and 683 deletions
@@ -148,18 +148,6 @@
flex-direction: column;
padding: 50px 80px 30px;
}
/* 顶部副标题 */
.normal-heading {
text-align: center;
margin-bottom: 24px;
}
.normal-heading__text {
font-size: 26px;
font-weight: 600;
color: #FF9800;
letter-spacing: 4px;
text-shadow: 0 0 20px rgba(255,152,0,0.2);
}
/* 中部左右分栏内容 */
.normal-content {
flex: 1;
@@ -176,8 +164,8 @@
}
/* 左侧大图 */
.normal-img-box {
width: 480px;
height: 360px;
width: 560px;
height: 420px;
border-radius: 20px;
overflow: hidden;
flex-shrink: 0;
@@ -295,84 +283,30 @@
display: none;
}
/* ================ 视频模式布局(上信息条 + 下大视频 ================ */
/* ================ 视频模式布局(左右分栏:左视频 + 右信息卡 ================ */
.video-mode {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
display: none;
flex-direction: column;
padding: 24px 40px 28px;
gap: 20px;
flex-direction: row;
align-items: center;
padding: 50px 80px 30px;
gap: 24px;
}
.main-area.mode-video .video-mode {
display: flex;
}
/* 顶部信息条(菜名 + 价格突出 + 标签 */
.video-topbar {
display: flex;
align-items: center;
gap: 24px;
padding: 14px 24px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
flex-shrink: 0;
}
.video-topbar__name {
font-size: 32px;
font-weight: 700;
color: #fff;
flex-shrink: 0;
}
/* 视频模式价格突出(青底胶囊,靠右) */
.video-topbar__price {
display: flex;
align-items: baseline;
gap: 4px;
padding: 8px 18px;
background: linear-gradient(135deg, rgba(10,196,168,0.18), rgba(10,196,168,0.08));
border: 1px solid rgba(10,196,168,0.4);
border-radius: 10px;
flex-shrink: 0;
margin-left: auto;
}
.video-topbar__price-symbol {
font-size: 18px;
font-weight: 600;
color: #0AC4A8;
}
.video-topbar__price-num {
font-size: 36px;
font-weight: 800;
color: #0AC4A8;
line-height: 1;
text-shadow: 0 0 12px rgba(10,196,168,0.4);
}
.video-topbar__price-unit {
font-size: 13px;
color: rgba(10,196,168,0.7);
margin-left: 2px;
}
/* 视频模式标签行 */
.video-topbar__tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
flex: 1;
min-width: 0;
}
/* 视频播放区(尽量放大) */
/* 视频播放区(左侧主体,尽量放大 */
.video-player {
flex: 1;
width: 100%;
border-radius: 14px;
height: 420px;
border-radius: 20px;
overflow: hidden;
position: relative;
background: #000;
box-shadow: 0 16px 40px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.06);
min-height: 0;
min-width: 0;
}
.video-player .video-bg {
position: absolute;
@@ -449,7 +383,7 @@
<div class="device-frame">
<!-- 顶部窗口栏 -->
<div class="top-bar">
<a class="back-btn" id="backBtn" href="javascript:void(0)">&#x25C0; 返回</a> <span>Phone-192.168.1.165:5555</span>
<a class="back-btn" id="backBtn" href="javascript:void(0)">&#x25C0; 返回</a>
</div>
<!-- 演示模式切换按钮(仅原型演示用,不影响实际业务) -->
@@ -468,11 +402,6 @@
<div class="main-area">
<!-- ============ 常规模式:左右分栏(左大图 + 右信息卡) ============ -->
<div class="normal-mode">
<!-- 顶部副标题 -->
<div class="normal-heading">
<span class="normal-heading__text">先识别 后取餐</span>
</div>
<!-- 中部内容(点击进入开餐界面) -->
<div class="normal-content" id="dishInfo">
<!-- 左侧大图 -->
@@ -503,22 +432,9 @@
<div class="normal-hint">点击菜品信息 &#x2192; 进入取餐界面</div>
</div>
<!-- ============ 视频模式:上信息条 + 下大视频 ============ -->
<!-- ============ 视频模式:左右分栏(左视频 + 右信息卡) ============ -->
<div class="video-mode" id="videoMode">
<!-- 顶部信息条 -->
<div class="video-topbar">
<span class="video-topbar__name" id="videoDishName">宫保鸡丁</span>
<!-- 标签(紧跟菜名) -->
<div class="video-topbar__tags" id="videoTags"></div>
<!-- 价格突出(右侧) -->
<div class="video-topbar__price">
<span class="video-topbar__price-symbol">&#xFFE5;</span>
<span class="video-topbar__price-num" id="videoDishPrice">5</span>
<span class="video-topbar__price-unit">元/100克</span>
</div>
</div>
<!-- 视频播放器(铺满下方剩余空间) -->
<!-- 左侧视频播放器 -->
<div class="video-player">
<div class="video-bg" id="videoBg"></div>
<div class="video-play-btn" id="videoPlayBtn" title="播放"></div>
@@ -529,6 +445,27 @@
<span class="video-fullscreen">&#x26F6;</span>
</div>
</div>
<!-- 右侧信息卡(与常规模式信息卡内容一致) -->
<div class="normal-info">
<div class="normal-info__name" id="videoDishName">宫保鸡丁</div>
<!-- 价格突出 -->
<div class="normal-info__price">
<span class="normal-info__price-symbol">&#xFFE5;</span>
<span class="normal-info__price-num" id="videoDishPrice">5</span>
<span class="normal-info__price-unit">元/100克</span>
</div>
<!-- 餐品标签 -->
<div class="normal-tags" id="videoTags"></div>
<!-- 分隔线 -->
<div class="normal-divider"></div>
<!-- 热量 -->
<div class="normal-cal">
<span class="normal-cal__label">热量</span>
<span class="normal-cal__num" id="videoDishKcal">158.66</span>
<span class="normal-cal__unit">千卡/100克</span>
</div>
</div>
</div>
</div>
@@ -622,7 +559,7 @@
var dishTags = dishData.tags || [];
/* 常规模式数据填充 */
document.getElementById('dishImg').innerHTML = dishFoodSVG(480, 360, dishData.colors);
document.getElementById('dishImg').innerHTML = dishFoodSVG(560, 420, dishData.colors);
document.getElementById('dishName').textContent = dishData.name;
document.getElementById('dishPrice').textContent = dishData.price;
document.getElementById('dishKcal').textContent = dishData.kcal;
@@ -631,6 +568,7 @@
/* 视频模式数据填充 */
document.getElementById('videoDishName').textContent = dishData.name;
document.getElementById('videoDishPrice').textContent = dishData.price;
document.getElementById('videoDishKcal').textContent = dishData.kcal;
document.getElementById('videoBg').innerHTML = videoBgSVG(dishData.colors);
renderTags('videoTags', dishTags);
@@ -63,6 +63,7 @@
flex-direction: column;
gap: 12px;
min-width: 0;
min-height: 0;
}
/* 设置按钮(浮于 pickup-section 头部) */
.settings-btn {
@@ -106,7 +107,6 @@
font-size: 20px; flex-shrink: 0;
}
.info-card__body { flex: 1; min-width: 0; }
/* 用户信息卡:第一行 姓名 + 编号 同行 */
.info-card__line {
display: flex;
align-items: baseline;
@@ -125,31 +125,7 @@
color: rgba(255,255,255,0.55);
font-weight: 500;
}
/* 第二行:推荐热量 + 范围值 */
.info-card__heat {
display: flex;
align-items: baseline;
gap: 6px;
white-space: nowrap;
}
.info-card__label {
font-size: 11px;
color: rgba(255,255,255,0.45);
font-weight: 400;
}
.info-card__value {
font-size: 16px;
font-weight: 700;
color: #0AC4A8;
line-height: 1.1;
}
.info-card__unit {
font-size: 11px;
font-weight: 400;
color: rgba(10,196,168,0.7);
}
/* ===== 餐品信息卡 ===== */
/* 餐品信息卡 */
.info-card--dish {
cursor: pointer;
transition: background 0.2s;
@@ -188,121 +164,106 @@
letter-spacing: 0;
text-shadow: none;
}
.info-card__dish-heat {
font-size: 11px;
color: rgba(255,255,255,0.5);
line-height: 1.3;
}
.info-card__dish-heat em {
font-style: normal;
color: #0AC4A8;
font-weight: 600;
}
/* ===== 当前餐次标题行(参考单屏 03 nutrition-header-row) ===== */
.meal-header-row {
/* ===== 中列下半部:餐品展示(上方视频铺满 + 下方热量/标签区域) ===== */
.dish-display {
flex: 1;
background: #000;
border-radius: 14px;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
box-shadow: 0 12px 32px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.06);
}
/* 上方视频区:占据剩余空间 */
.dish-video {
flex: 1;
position: relative;
background: #000;
min-height: 0;
}
.dish-video .video-bg {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
}
.video-play-btn {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 64px; height: 64px;
background: rgba(0,0,0,0.55);
border: 2px solid rgba(255,255,255,0.85);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s, background 0.2s;
z-index: 2;
}
.video-play-btn:hover {
background: rgba(0,0,0,0.75);
transform: translate(-50%, -50%) scale(1.05);
}
.video-play-btn::after {
content: '';
width: 0; height: 0;
border-left: 18px solid #fff;
border-top: 12px solid transparent;
border-bottom: 12px solid transparent;
margin-left: 5px;
}
.video-controls {
position: absolute;
bottom: 0; left: 0; right: 0;
padding: 10px 14px;
background: linear-gradient(to top, rgba(0,0,0,0.75), transparent);
display: flex;
align-items: center;
gap: 12px;
font-size: 12px;
color: rgba(255,255,255,0.85);
z-index: 2;
}
.video-progress {
flex: 1;
height: 4px;
background: rgba(255,255,255,0.2);
border-radius: 2px;
position: relative;
}
.video-progress::after {
content: '';
position: absolute;
left: 0; top: 0;
width: 35%;
height: 100%;
background: #FF9800;
border-radius: 2px;
}
.video-time {
font-size: 12px;
letter-spacing: 0.5px;
white-space: nowrap;
}
/* 下方信息区:热量 + 标签 */
.dish-meta {
flex-shrink: 0;
padding: 14px 20px;
background: #1a2b4a;
display: flex;
align-items: center;
justify-content: space-between;
background: #1a2b4a;
border-radius: 10px;
padding: 12px 20px;
flex-shrink: 0;
}
.meal-header-row .n-title {
font-size: 16px;
font-weight: 600;
color: #fff;
}
.meal-header-row .n-title-hint {
font-size: 12px;
font-weight: 400;
color: #FFB236;
margin-left: 4px;
}
.meal-header-row .n-meal {
font-size: 13px;
color: rgba(255,255,255,0.5);
}
.meal-header-row .n-time {
font-size: 12px;
color: rgba(255,255,255,0.35);
margin-left: 12px;
}
/* ===== 中列下半部:餐品介绍(替代 nutrition-core) ===== */
.dish-display {
flex: 1;
background: linear-gradient(180deg, #1a2b4a 0%, #142038 100%);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
min-height: 0;
}
.dish-info {
display: flex;
align-items: center;
justify-content: center;
gap: 32px;
width: 100%;
}
.dish-img-box {
width: 320px; height: 320px;
border-radius: 20px;
overflow: hidden;
flex-shrink: 0;
box-shadow: 0 12px 32px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.06);
}
.dish-img-box svg { width: 100%; height: 100%; }
.dish-info-text {
display: flex;
flex-direction: column;
gap: 16px;
flex: 1;
min-width: 0;
max-width: 420px;
}
.dish-name-row {
font-size: 38px;
font-weight: 700;
color: #fff;
line-height: 1.2;
}
/* 价格胶囊(青色) */
.dish-price-pill {
display: flex;
align-items: baseline;
gap: 6px;
padding: 10px 20px;
background: linear-gradient(135deg, rgba(10,196,168,0.18), rgba(10,196,168,0.06));
border: 1px solid rgba(10,196,168,0.4);
border-radius: 12px;
width: fit-content;
}
.dish-price-pill__symbol {
font-size: 20px;
font-weight: 600;
color: #0AC4A8;
}
.dish-price-pill__num {
font-size: 44px;
font-weight: 800;
color: #0AC4A8;
line-height: 1;
text-shadow: 0 0 16px rgba(10,196,168,0.35);
}
.dish-price-pill__unit {
font-size: 14px;
color: rgba(10,196,168,0.8);
font-weight: 400;
}
/* 餐品标签 chips */
.dish-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
flex: 1;
min-width: 0;
}
.dish-tag {
display: inline-flex;
@@ -326,29 +287,28 @@
background: rgba(91,155,248,0.12);
border-color: rgba(91,155,248,0.25);
}
.dish-divider {
width: 100%;
height: 1px;
background: rgba(255,255,255,0.1);
}
.dish-cal-row {
display: flex;
align-items: baseline;
gap: 8px;
flex-shrink: 0;
padding: 6px 14px;
background: rgba(0,0,0,0.25);
border-radius: 8px;
}
.dish-cal-row__label {
font-size: 13px;
color: rgba(255,255,255,0.5);
color: rgba(255,255,255,0.6);
letter-spacing: 0.5px;
}
.dish-cal-row__num {
font-size: 26px;
font-size: 24px;
font-weight: 700;
color: #fff;
}
.dish-cal-row__unit {
font-size: 12px;
color: rgba(255,255,255,0.5);
color: rgba(255,255,255,0.55);
}
/* ===== 右列:取餐记录(通顶) ===== */
@@ -391,13 +351,42 @@
border-bottom: 1px solid rgba(255,255,255,0.08);
flex-shrink: 0;
}
.pickup-list { flex: 1; overflow-y: auto; padding: 0; }
.pickup-list { flex: 1; overflow: hidden; padding: 0; }
.pickup-row {
display: grid; grid-template-columns: 1fr 1fr 1fr;
padding: 10px 0; font-size: 13px;
color: rgba(255,255,255,0.7);
border-bottom: 1px solid rgba(255,255,255,0.04);
}
/* 当前正在取餐的餐品行:橙色背景胶囊 + 左侧色条 + 文字加粗 */
.pickup-row--active {
position: relative;
padding: 12px 12px 12px 16px;
margin: 4px -4px;
background: linear-gradient(135deg, rgba(255,178,54,0.22) 0%, rgba(255,140,40,0.32) 100%);
border: 1px solid rgba(255,178,54,0.55);
border-radius: 8px;
box-shadow: 0 4px 14px rgba(255,140,40,0.22);
border-bottom: 1px solid rgba(255,178,54,0.55);
}
.pickup-row--active::before {
content: '';
position: absolute;
left: 0; top: 6px; bottom: 6px;
width: 3px;
background: #FFB236;
border-radius: 2px;
box-shadow: 0 0 8px rgba(255,178,54,0.6);
}
.pickup-row--active > span {
color: #fff;
font-weight: 700;
}
.pickup-row--active > span:nth-child(2),
.pickup-row--active > span:nth-child(3) {
color: #FFB236;
text-shadow: 0 1px 4px rgba(255,140,40,0.4);
}
/* 合计区:橙色胶囊(沿用单屏 03 样式) */
.pickup-footer {
display: flex;
@@ -530,7 +519,7 @@
<span class="screen-label">大屏 · 1280×720</span>
<div class="device-frame">
<div class="large-main">
<!-- ===== 中列:用户信息 + 餐品信息 + 餐品介绍 ===== -->
<!-- ===== 中列:用户信息 + 餐品信息 + 视频展示 ===== -->
<div class="center-column">
<!-- 用户信息 + 餐品信息 横排 -->
<div class="info-row">
@@ -555,37 +544,30 @@
</div>
</div>
<!-- 当前餐次标题行 -->
<div class="meal-header-row">
<span class="n-title">&#x1F4CA; &#x672C;&#x9910;&#x6B21;&#x6444;&#x5165;<span class="n-title-hint">&#xFF08;&#x63A8;&#x8350;&#x70ED;&#x91CF; 580~720 &#x5343;&#x5361;&#xFF09;</span></span>
<span>
<span class="n-meal">&#x5F53;&#x524D;&#x9910;&#x6B21;&#xFF1A;&#x65E9;&#x9910;</span>
<span class="n-time">06:00 ~ 10:19</span>
</span>
</div>
<!-- 餐品介绍(替代原 nutrition-core) -->
<!-- 餐品展示:上方视频铺满 + 下方热量/标签区域 -->
<div class="dish-display">
<div class="dish-info">
<div class="dish-img-box" id="dishImg"></div>
<div class="dish-info-text">
<div class="dish-name-row">&#x5BAB;&#x4FDD;&#x9E21;&#x4E01;</div>
<div class="dish-price-pill">
<span class="dish-price-pill__symbol">&#xFFE5;</span>
<span class="dish-price-pill__num">5</span>
<span class="dish-price-pill__unit">&#x5143;/100&#x514B;</span>
</div>
<div class="dish-tags">
<span class="dish-tag">&#x9AD8;&#x86CB;&#x767D;</span>
<span class="dish-tag">&#x4F4E;&#x82A5;&#x9178;</span>
<span class="dish-tag">&#x8425;&#x517B;&#x5747;&#x8861;</span>
</div>
<div class="dish-divider"></div>
<div class="dish-cal-row">
<span class="dish-cal-row__label">&#x70ED;&#x91CF;</span>
<span class="dish-cal-row__num">158.66</span>
<span class="dish-cal-row__unit">&#x5343;&#x5361;/100&#x514B;</span>
</div>
<!-- 上方视频区 -->
<div class="dish-video">
<div class="video-bg" id="videoBg"></div>
<div class="video-play-btn" id="videoPlayBtn" title="播放"></div>
<div class="video-controls">
<span style="cursor:pointer;">&#x23EF;</span>
<div class="video-progress"></div>
<span class="video-time">01:23 / 03:45</span>
<span style="cursor:pointer;">&#x26F6;</span>
</div>
</div>
<!-- 下方信息区:标签 + 热量 -->
<div class="dish-meta">
<div class="dish-tags">
<span class="dish-tag">&#x9AD8;&#x86CB;&#x767D;</span>
<span class="dish-tag">&#x4F4E;&#x82A5;&#x9178;</span>
<span class="dish-tag">&#x8425;&#x517B;&#x5747;&#x8861;</span>
</div>
<div class="dish-cal-row">
<span class="dish-cal-row__label">&#x70ED;&#x91CF;</span>
<span class="dish-cal-row__num">158.66</span>
<span class="dish-cal-row__unit">&#x5343;&#x5361;/100&#x514B;</span>
</div>
</div>
</div>
@@ -605,7 +587,7 @@
<span>&#x5C0F;&#x8BA1;(&#x5143;)</span>
</div>
<div class="pickup-list">
<div class="pickup-row">
<div class="pickup-row pickup-row--active">
<span>&#x5BAB;&#x4FDD;&#x9E21;&#x4E01;</span><span>186</span><span>9.30</span>
</div>
<div class="pickup-row">
@@ -741,21 +723,29 @@
window.location.href = '01-meal-start.html';
});
/* ===== 餐品占位图(SVG) ===== */
function dishFoodSVG(w, h, colors) {
var id = 'dg';
return '<svg xmlns="http://www.w3.org/2000/svg" width="' + w + '" height="' + h + '" viewBox="0 0 ' + w + ' ' + h + '">' +
/* ===== 视频背景 SVG(按菜品色系生成展示画面) ===== */
function videoBgSVG(colors) {
var w = 1280, h = 720;
var id = 'vidGrad';
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + w + ' ' + h + '" preserveAspectRatio="xMidYMid slice" style="width:100%;height:100%;">' +
'<defs><linearGradient id="' + id + '" x1="0" y1="0" x2="1" y2="1">' +
'<stop offset="0%" stop-color="' + colors[0] + '"/>' +
'<stop offset="50%" stop-color="' + colors[1] + '"/>' +
'<stop offset="100%" stop-color="' + colors[2] + '"/></linearGradient></defs>' +
'<rect width="' + w + '" height="' + h + '" fill="url(#' + id + ')"/>' +
'<ellipse cx="' + (w*0.4) + '" cy="' + (h*0.45) + '" rx="' + (w*0.3) + '" ry="' + (h*0.25) + '" fill="rgba(255,255,255,0.08)"/>' +
'<ellipse cx="' + (w*0.6) + '" cy="' + (h*0.55) + '" rx="' + (w*0.25) + '" ry="' + (h*0.2) + '" fill="rgba(0,0,0,0.1)"/>' +
'<ellipse cx="' + (w*0.35) + '" cy="' + (h*0.35) + '" rx="' + (w*0.12) + '" ry="' + (h*0.1) + '" fill="rgba(255,255,255,0.15)"/>' +
'<ellipse cx="' + (w*0.3) + '" cy="' + (h*0.35) + '" rx="280" ry="200" fill="rgba(255,255,255,0.08)"/>' +
'<ellipse cx="' + (w*0.7) + '" cy="' + (h*0.65) + '" rx="340" ry="240" fill="rgba(0,0,0,0.12)"/>' +
'<text x="' + (w/2) + '" y="' + (h*0.5) + '" text-anchor="middle" fill="rgba(255,255,255,0.55)" font-size="40" font-weight="600" font-family="-apple-system,Microsoft YaHei,sans-serif">餐品制作工艺展示</text>' +
'</svg>';
}
document.getElementById('dishImg').innerHTML = dishFoodSVG(320, 320, ['#C87030', '#E8A040', '#D08028']);
document.getElementById('videoBg').innerHTML = videoBgSVG(['#C87030', '#E8A040', '#D08028']);
/* ===== 视频播放按钮(演示用,点击切换播放/暂停图标) ===== */
var videoPlaying = false;
document.getElementById('videoPlayBtn').addEventListener('click', function() {
videoPlaying = !videoPlaying;
this.style.opacity = videoPlaying ? '0' : '1';
});
</script>
</body>
</html>
@@ -30,6 +30,34 @@
display: flex;
flex-direction: column;
}
/* 屏幕边缘报警层(热量超标触发):全屏红色边框跑马灯 + 外发光脉冲 */
.device-frame--alarm::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
border-radius: 24px;
border: 4px solid rgba(255,40,40,0.85);
box-shadow:
inset 0 0 36px rgba(255,40,40,0.55),
0 0 28px rgba(255,40,40,0.7);
animation: screenEdgeAlarm 1s ease-in-out infinite;
z-index: 50;
}
@keyframes screenEdgeAlarm {
0%, 100% {
border-color: rgba(255,40,40,0.55);
box-shadow:
inset 0 0 18px rgba(255,40,40,0.3),
0 0 14px rgba(255,40,40,0.35);
}
50% {
border-color: rgba(255,40,40,1);
box-shadow:
inset 0 0 48px rgba(255,40,40,0.75),
0 0 36px rgba(255,40,40,0.9);
}
}
/* ---- 主内容区:两列 ---- */
.main-area {
@@ -162,52 +190,37 @@
font-weight: 600;
}
/* 营养标题行 */
.nutrition-header-row {
display: flex;
align-items: center;
justify-content: space-between;
background: #1a2b4a;
border-radius: 10px;
padding: 12px 20px;
flex-shrink: 0;
}
.nutrition-header-row .n-title {
font-size: 16px;
font-weight: 600;
color: #fff;
}
/* 标题行内的推荐热量提示:小字号+淡青色,作为辅助信息弱化视觉权重 */
.nutrition-header-row .n-title-hint {
font-size: 12px;
font-weight: 400;
color: #0AC4A8;
margin-left: 4px;
}
.nutrition-header-row .n-meal {
font-size: 13px;
color: rgba(255,255,255,0.5);
}
.nutrition-header-row .n-time {
font-size: 12px;
color: rgba(255,255,255,0.35);
margin-left: 12px;
}
/* 圆柱形营养展示区 */
/* 圆柱形营养展示区:纵向布局,柱组占满主体,底部居中放推荐热量提示 */
.nutrition-core {
flex: 1;
background: linear-gradient(180deg, #1a2b4a 0%, #142038 100%);
border-radius: 14px;
display: flex;
align-items: flex-end;
justify-content: center;
padding: 40px 40px 32px;
gap: 32px;
flex-direction: column;
align-items: stretch;
justify-content: space-between;
padding: 24px 40px 18px;
position: relative;
overflow: hidden;
}
/* 底部居中的推荐热量提示横条(独立于柱组之外,不影响柱子基线对齐) */
.cyl-recommend {
font-size: 13px;
color: #0AC4A8;
text-align: center;
line-height: 1.3;
letter-spacing: 0.5px;
white-space: nowrap;
flex-shrink: 0;
padding-top: 12px;
}
/* 超标时提示色变红加粗,与屏幕边缘报警呼应 */
.nutrition-core--over .cyl-recommend {
color: #ff5a5a;
font-weight: 700;
}
/* 圆柱组 */
.cyl-group {
display: flex;
@@ -215,7 +228,9 @@
justify-content: center;
gap: 32px;
width: 100%;
padding-bottom: 16px;
flex: 1;
align-self: center;
padding-bottom: 8px;
}
/* 单个圆柱 */
@@ -224,6 +239,11 @@
flex-direction: column;
align-items: center;
gap: 8px;
position: relative;
}
/* 超标语义:数值变红(不闪烁,闪烁已改为屏幕边缘报警) */
.cyl-item--over .cyl-value {
color: #ff5a5a;
}
/* 圆柱数值(柱顶) */
@@ -327,6 +347,41 @@
font-size: 13px;
color: rgba(255,255,255,0.7);
border-bottom: 1px solid rgba(255,255,255,0.04);
position: relative;
}
/* 正在取餐的餐品行:差异化高亮 + 呼吸光晕 + 左侧高亮条 */
.pickup-row--active {
background: linear-gradient(90deg,
rgba(255,178,54,0.22) 0%,
rgba(255,178,54,0.08) 60%,
transparent 100%);
border-bottom-color: rgba(255,178,54,0.25);
border-radius: 6px;
animation: pickupPulse 1.8s ease-in-out infinite;
}
.pickup-row--active::before {
content: '';
position: absolute;
left: -16px;
top: 6px;
bottom: 6px;
width: 4px;
border-radius: 2px;
background: #FFB236;
box-shadow: 0 0 8px rgba(255,178,54,0.8);
}
.pickup-row--active > span:first-child {
color: #fff;
font-weight: 700;
}
.pickup-row--active > span:nth-child(2),
.pickup-row--active > span:nth-child(3) {
color: #FFB236;
font-weight: 600;
}
@keyframes pickupPulse {
0%, 100% { box-shadow: inset 0 0 0 0 rgba(255,178,54,0); }
50% { box-shadow: inset 0 0 18px 0 rgba(255,178,54,0.25); }
}
/* 合计区:单行排布,合计/重量/金额一行,金额最大化 */
.pickup-footer {
@@ -401,7 +456,7 @@
</head>
<body>
<div class="device-frame">
<div class="device-frame device-frame--alarm">
<!-- 返回按钮 -->
<div style="position:absolute;top:12px;left:16px;z-index:10;">
@@ -438,22 +493,13 @@
</div>
</div>
<!-- 营养标题行 -->
<div class="nutrition-header-row">
<span class="n-title">&#x1F4CA; &#x672C;&#x9910;&#x6B21;&#x6444;&#x5165;<span class="n-title-hint">&#xFF08;&#x63A8;&#x8350;&#x70ED;&#x91CF; 2000~2500 &#x5343;&#x5361;&#xFF09;</span></span>
<span>
<span class="n-meal">&#x5F53;&#x524D;&#x9910;&#x6B21;&#xFF1A;&#x65E9;&#x9910;</span>
<span class="n-time">06:00 ~ 10:19</span>
</span>
</div>
<!-- 圆柱形营养展示 -->
<div class="nutrition-core">
<div class="nutrition-core nutrition-core--over">
<div class="cyl-group">
<!-- 热量柱(红色,最高 -->
<div class="cyl-item">
<div class="cyl-value">1070<span class="cyl-unit">千卡</span></div>
<!-- 热量柱(超标,数值变红;屏幕边缘触发报警闪烁 -->
<div class="cyl-item cyl-item--over">
<div class="cyl-value">2780<span class="cyl-unit">千卡</span></div>
<svg class="cyl-svg" width="120" height="380" viewBox="0 0 120 380">
<defs>
<linearGradient id="heatGrad" x1="0" y1="0" x2="1" y2="0">
@@ -532,6 +578,8 @@
</div>
</div>
<!-- 推荐热量提示:居中显示在底部,独立于柱组之外,不影响柱子基线对齐 -->
<div class="cyl-recommend">推荐热量 2000~2500 千卡</div>
</div>
</div>
@@ -551,7 +599,7 @@
<span>&#x5C0F;&#x8BA1;(&#x5143;)</span>
</div>
<div class="pickup-list">
<div class="pickup-row">
<div class="pickup-row pickup-row--active">
<span>&#x5BAB;&#x4FDD;&#x9E21;&#x4E01;</span>
<span>186</span>
<span>9.30</span>
@@ -1,5 +1,61 @@
# 餐线营养秤 - 变更记录
## [2026-08-07] fix: 取餐页报警层移至屏幕边缘 + 推荐热量居中 + 取消"取餐中"标签
### 调整
- **热量超标报警层从柱身移到屏幕边缘**(`03-meal-pickup.html`):
- 移除原 `.cyl-item--over` 柱身跑马灯(伪元素斜向扫动光带 + 外发光呼吸 + 数值脉冲三处动画全部删除)
- 改为 `.device-frame--alarm` 触发 `.device-frame::after`:PAD 外壳内侧 4px 红色边框 + 内外双重红色 `box-shadow` 发光,`screenEdgeAlarm` 1s `ease-in-out` 循环(边框颜色与发光强度脉冲)
- 柱身仅保留"超标语义"轻量变化:热量柱数值变红(不闪烁),与屏幕边缘报警呼应
- **推荐热量提示居中显示在底部**:
- `.cyl-recommend` 从原热量柱列内部移出,改放 `.nutrition-core` 容器底部,作为独立横条居中显示
- `.nutrition-core` 改为纵向 flex`flex-direction: column` + `justify-content: space-between`):上方 `.cyl-group` 占满主体高度并 `align-self: center`,下方 `.cyl-recommend` 横条贴底
- 关键:所有柱子使用 `align-items: flex-end` 底对齐,移除原"附在热量柱下方"的位置后柱子们站在同一基线,避免高低不平
- 超标时 `.nutrition-core--over` 让提示色由淡青变红加粗
- **右侧取餐列表取消"取餐中"胶囊标签**:删除 `.pickup-row--active > span:first-child::after` 段,保留其余差异化高亮(左侧 4px 橙色光条 + 橙色渐变背景 + 内部呼吸光晕 + 餐名/数值加粗变橙)
---
## [2026-08-07] refactor: 取餐页(单屏版)删除中部营养标题行 + 热量超标跑马灯 + 取餐行差异化
### 调整
- **删除中部「本餐次摄入」营养标题行**(`03-meal-pickup.html`):移除 `.nutrition-header-row` 整块及相关 CSS(标题、推荐热量副文案、当前餐次、时间段),营养圆柱区直接占据剩余空间
- **推荐热量提示移至热量柱下方**:新增 `.cyl-recommend`(11px 淡青色),固定文案"推荐 2000~2500 千卡",紧跟"热量"标签
- **热量超标红色跑马灯闪烁**:演示值改为 2780 千卡(>2500 上限),`.cyl-item--over` 修饰类触发:
- `::before` 伪元素叠加斜向扫动光带(`cylOverSweep` 1.4s linear infinite+ 红色外发光阴影
- `.cyl-value` 文字变红 + 透明度脉冲(`cylValueBlink` 1s ease-in-out infinite
- 同步把 `.cyl-recommend` 提示色切换为红色加粗,与跑马灯呼应
- **右侧取餐列表差异化"正在取餐"餐品**:新增 `.pickup-row--active` 修饰类,演示首行(宫保鸡丁)标记为正在取餐
- 左侧 4px 亮橙色高亮条(带橙色发光阴影,伪元素定位到行容器外 `-16px`
- 行背景橙色横向渐变 + 内部呼吸光晕(`pickupPulse` 1.8s ease-in-out infinite
- 餐品名加粗变白,行尾追加"取餐中"橙色胶囊小标签;重量/小计变橙色加粗
---
## [2026-08-07] refactor: 双屏取餐页大屏中部删长条 + 餐品区改视频展示
### 调整
- **删除大屏中部「本餐次摄入」长条**(`03-meal-pickup-dual.html`):移除 `.meal-header-row` 及其关联 CSS(标题、推荐热量副文案、当前餐次、时间段),保留顶部「用户信息 + 餐品信息」横排不变
- **餐品展示区改为视频铺满**
- `.dish-display` 重构为纵向布局:上方 `.dish-video` 视频铺满剩余空间(含视频背景、播放按钮、进度条、时间、全屏控件),下方 `.dish-meta` 信息条展示「餐品标签 + 每100g 热量」
- 移除原 `.dish-info` 左右分栏结构(菜名/价格等信息已在顶部餐品信息卡展示,避免重复)
- 移除上一版引入的演示切换按钮(常规/视频),固定为视频展示
- **右侧取餐列表取消滚动条**`.pickup-list``overflow-y: auto` 改为 `overflow: hidden`
- **右侧取餐记录高亮当前餐品**(保留上一版改动):`.pickup-row--active` 修饰类,橙色背景胶囊 + 左侧 3px 色条 + 文字加粗,首行宫保鸡丁标记为正在取餐
---
## [2026-08-07] refactor: 菜品展示页常规模式放大菜品图 + 视频模式改左右布局
### 调整
- **常规模式菜品图放大**`02-dish-display.html`):左侧 `.normal-img-box` 由 480×360 调整为 560×420,同步更新 SVG 生成尺寸
- **视频模式改左右布局**:原「上信息条 + 下大视频」纵向布局调整为「左视频 + 右信息卡」横向布局
- 移除原 `.video-topbar` 顶部信息条及相关 CSS
- 右侧复用常规模式 `.normal-info` 信息卡结构(菜名 / 价格 / 标签 / 分隔线 / 热量),保持两模式右侧视觉一致
- JS 数据填充同步覆盖视频模式热量字段(`#videoDishKcal`
---
## [2026-08-05] feat: 菜品展示页新增视频演示模式
### 新增
@@ -1,5 +1,28 @@
# 结算设备 CHANGELOG
## 2026-08-07 | U02 副屏取餐明细三栏重构
### 变更
- `user-02-checkout-sync.html` 全面重构,优化副屏结算信息密度与视觉聚焦
- **顶部**:删除原「您的取餐明细」标题,改为全宽用户信息条(头像+姓名/工号+部门·餐线+会员等级+实时时间)
- **左侧餐品明细**:
- 改用 `<table>` 表格化布局(`table-layout: fixed`),彻底解决原 grid 卡片对齐混乱问题
- 新增「热量(kcal)」列,与表头/合计行严格对齐
- 列表区域使用 CSS animation 实现**垂直无缝循环滚动**(18s,首尾重复一份数据,hover 暂停)
- 合计行固定在列表底部(`flex-shrink: 0`),列宽与表头完全对齐:合计 / 总重量 1040g / 总热量 1563 kcal / 总金额 ¥46.60
- **右侧三段式纵向布局**(删除原顶部用户信息,改由顶部条承担):
1. 营养摄入卡(压缩版):热量环形 + 三大营养素横条;**三大营养素改为显示 NRV%(营养素参考值占比),不再显示克数**;删除原底部「💡 搭配均衡...」提示文字
2. 订单金额(中间,极醒目):字号提升至 60px,加粗发光阴影,强调应付金额 ¥41.60
3. 支付二维码(底部):内联简化版二维码 SVG + 微信/支付宝标签 + 扫码提示
## 2026-08-07 | 恢复副屏支付引导页(需求回退)
### 变更
- 恢复 `user-03-paying.html`(用户屏 U03「支付引导」页面,需求回滚)
- 重命名 `user-03-success.html``user-04-success.html`,支付成功退回 U04 序号
- `index.html` 用户屏区恢复 4 张卡片:U01 待机 / U02 取餐明细 / U03 支付引导 / U04 支付成功
- 注:本次仅回退「副屏支付引导删除」部分,收银员屏「小票预览」改名(2026-08-07 上一次提交)保持不变
## 2026-08-07 | 副屏支付引导删除 + 收银员屏小票预览改名
### 变更
@@ -217,8 +217,16 @@
<div class="card__desc">同步商品列表 + 营养摘要</div>
</div>
</a>
<a class="card" href="user-03-success.html">
<a class="card" href="user-03-paying.html">
<span class="card__status card__status--done">U03</span>
<div class="card__icon">&#x1F4F1;</div>
<div class="card__body">
<div class="card__title">支付引导</div>
<div class="card__desc">4 种支付方式引导切换</div>
</div>
</a>
<a class="card" href="user-04-success.html">
<span class="card__status card__status--done">U04</span>
<div class="card__icon">&#x2705;</div>
<div class="card__body">
<div class="card__title">支付成功</div>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,210 @@
<!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; }
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;
}
.device-frame {
width: 1280px;
height: 720px;
background: linear-gradient(180deg, #0e1326 0%, #1a2b4a 50%, #0e1326 100%);
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;
}
/* 顶部金额条 */
.header {
padding: 24px 36px 20px;
background: rgba(26, 43, 74, 0.5);
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.header__left { display: flex; align-items: baseline; gap: 12px; }
.header__label {
font-size: 14px;
color: rgba(255, 255, 255, 0.55);
}
.header__amount {
font-size: 42px;
font-weight: 700;
color: #0AC4A8;
font-family: Menlo, Consolas, monospace;
line-height: 1;
}
.header__right {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: rgba(74, 144, 217, 0.15);
border: 1px solid rgba(74, 144, 217, 0.35);
border-radius: 18px;
font-size: 13px;
color: #60a5fa;
font-weight: 600;
}
.header__right__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #52c41a;
box-shadow: 0 0 8px #52c41a;
animation: pulse 1.2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(1.3); }
}
/* 主体 */
.body {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 30px 60px;
overflow: hidden;
}
/* 支付方式面板 */
.pay-panel {
width: 100%;
max-width: 720px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
/* 支付图标动画区 */
.pay-icon-area {
width: 200px;
height: 200px;
border-radius: 50%;
background: radial-gradient(circle at 50% 50%, rgba(74, 144, 217, 0.25), rgba(74, 144, 217, 0.05) 70%);
border: 2px solid rgba(74, 144, 217, 0.35);
display: flex;
align-items: center;
justify-content: center;
position: relative;
box-shadow: 0 0 60px rgba(74, 144, 217, 0.25);
}
/* 外圈呼吸 */
.pay-icon-area::before {
content: "";
position: absolute;
inset: -20px;
border-radius: 50%;
border: 1px solid rgba(74, 144, 217, 0.2);
animation: ripple 2s ease-out infinite;
}
.pay-icon-area::after {
content: "";
position: absolute;
inset: -40px;
border-radius: 50%;
border: 1px solid rgba(74, 144, 217, 0.1);
animation: ripple 2s ease-out infinite;
animation-delay: 0.7s;
}
@keyframes ripple {
0% { opacity: 1; transform: scale(0.8); }
100% { opacity: 0; transform: scale(1.2); }
}
.pay-icon-area__icon {
font-size: 100px;
filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.3));
}
/* 支付状态文字 */
.pay-status { display: flex; flex-direction: column; gap: 10px; }
.pay-status__title {
font-size: 36px;
font-weight: 700;
color: #fff;
letter-spacing: 2px;
}
.pay-status__hint {
font-size: 16px;
color: rgba(255, 255, 255, 0.65);
line-height: 1.6;
}
/* 额度提示(在线支付/餐券) */
.pay-method-tip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 18px;
background: rgba(82, 196, 26, 0.1);
border: 1px solid rgba(82, 196, 26, 0.3);
border-radius: 22px;
font-size: 13px;
color: #4ade80;
}
</style>
</head>
<body>
<div class="device-frame">
<!-- 顶部金额条 -->
<div class="header">
<div class="header__left">
<span class="header__label">应付金额</span>
<span class="header__amount">¥41.60</span>
</div>
<div class="header__right">
<span class="header__right__dot"></span>
<span>等待支付中</span>
</div>
</div>
<!-- 主体 -->
<div class="body">
<!-- 当前支付方式:在线支付(扫码) -->
<div class="pay-panel" id="payPanel">
<div class="pay-icon-area">
<span class="pay-icon-area__icon" id="payIcon">&#x1F4F1;</span>
</div>
<div class="pay-status">
<div class="pay-status__title" id="payTitle">请出示付款码</div>
<div class="pay-status__hint" id="payHint">
打开微信/支付宝,将付款码对准扫码器
</div>
</div>
<div class="pay-method-tip" id="payTip">
&#x1F4F1; 支持微信 / 支付宝 / 银联云闪付
</div>
</div>
</div>
</div>
</body>
</html>