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>
This commit is contained in:
fengpu
2026-08-03 18:39:43 +08:00
co-authored by Claude Opus 4.7
parent e157638168
commit 836b05383d
6 changed files with 1900 additions and 261 deletions
@@ -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>