feat: 结算小票功能完善——分区分组、双二维码、支付类型、克重展示

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhangyan
2026-08-04 09:59:04 +08:00
co-authored by Claude Opus 4.6
parent e83b0f0697
commit d7ca18766e
2 changed files with 181 additions and 77 deletions
@@ -312,7 +312,15 @@
.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-table td:nth-child(2) { color: #666; }
.receipt-zone-header td {
font-size: 11px;
font-weight: 600;
color: #333;
padding: 6px 0 2px;
text-align: left;
border-bottom: 1px dotted #ccc;
}
/* 金额汇总 */
.receipt-amount {
@@ -328,7 +336,7 @@
.receipt-amount__paid {
font-size: 12px;
font-weight: 600;
color: #0a7c64;
color: #1a1a2e;
}
/* 营养摄入摘要 */
@@ -371,37 +379,50 @@
justify-content: space-between;
align-items: center;
padding: 6px 10px;
background: linear-gradient(90deg, #f0f7ff, #e6f4ff);
background: #f5f5f5;
border-radius: 4px;
border: 1px solid #cce4ff;
border: 1px solid #ddd;
}
.receipt-score__label {
font-size: 10px;
color: #4a90d9;
color: #333;
font-weight: 600;
}
.receipt-score__value {
font-size: 16px;
font-weight: 700;
color: #2570c0;
color: #1a1a2e;
}
/* 二维码 */
.receipt-qr {
/* 二维码并排 */
.receipt-qr--dual {
display: flex;
gap: 12px;
justify-content: center;
padding: 6px 0;
}
.receipt-qr--dual .receipt-qr {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 6px 0;
}
.receipt-qr__img {
width: 84px;
height: 84px;
.receipt-qr--dual .receipt-qr__img {
width: 72px;
height: 72px;
}
.receipt-qr__text {
.receipt-qr--dual .receipt-qr__text {
font-size: 9px;
color: #666;
text-align: center;
font-weight: 600;
}
.receipt-qr--dual .receipt-qr__sub {
font-size: 8px;
color: #999;
text-align: center;
line-height: 1.3;
}
/* 底部提示 */
@@ -558,14 +579,15 @@
line: 'A 区 1 号餐线',
cashier: '张三',
time: '2026-08-03 12:15:30',
payTime: '2026-08-03 12:15:35',
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 }
{ name: '宫保鸡丁', zone: '档口秤', weight: '200g', price: 18.00, qty: 1, subtotal: 18.00 },
{ name: '番茄炒蛋', zone: '营养秤', weight: '150g', price: 8.00, qty: 1, subtotal: 8.00 },
{ name: '米饭', zone: '营养秤', weight: '250g', price: 2.00, qty: 1, subtotal: 2.00 },
{ name: '紫菜蛋花汤', zone: '档口秤', weight: '300g', price: 5.00, qty: 1, subtotal: 5.00 }
],
total: 33.00, discount: 3.00, payable: 30.00, paid: 30.00,
payMethod: '人脸支付', balance: 1280.50,
payType: '会员支付', payMethod: '人脸支付', balance: 1280.50,
nutrition: { calories: 685, protein: 28, fat: 18, carb: 95, score: 82 },
printed: true
},
@@ -576,13 +598,14 @@
line: 'A 区 1 号餐线',
cashier: '张三',
time: '2026-08-03 07:42:18',
payTime: '2026-08-03 07:42:23',
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 }
{ name: '豆浆', zone: '营养秤', weight: '300g', price: 3.00, qty: 1, subtotal: 3.00 },
{ name: '油条', zone: '档口秤', weight: '120g', price: 2.50, qty: 2, subtotal: 5.00 },
{ name: '茶叶蛋', zone: '营养秤', weight: '60g', price: 4.00, qty: 1, subtotal: 4.00 }
],
total: 12.00, discount: 0, payable: 12.00, paid: 12.00,
payMethod: '餐卡支付', balance: 1292.50,
payType: '会员支付', payMethod: '人脸支付', balance: 1292.50,
nutrition: { calories: 425, protein: 15, fat: 12, carb: 60, score: 75 },
printed: true
},
@@ -593,15 +616,16 @@
line: 'A 区 2 号餐线',
cashier: '李四',
time: '2026-08-03 12:32:45',
payTime: '2026-08-03 12:32:50',
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 }
{ name: '红烧肉', zone: '档口秤', weight: '180g', price: 22.00, qty: 1, subtotal: 22.00 },
{ name: '清炒时蔬', zone: '营养秤', weight: '150g', price: 6.00, qty: 1, subtotal: 6.00 },
{ name: '鱼香肉丝', zone: '档口秤', weight: '160g', price: 12.00, qty: 1, subtotal: 12.00 },
{ name: '米饭', zone: '营养秤', weight: '250g', price: 2.00, qty: 1, subtotal: 2.00 },
{ name: '冬瓜排骨汤', zone: '档口秤', weight: '300g', price: 3.00, qty: 1, subtotal: 3.00 }
],
total: 45.00, discount: 5.00, payable: 40.00, paid: 40.00,
payMethod: '人脸支付', balance: 1240.50,
payType: '会员支付', payMethod: '人脸支付', balance: 1240.50,
nutrition: { calories: 720, protein: 35, fat: 28, carb: 88, score: 88 },
printed: false
},
@@ -612,13 +636,14 @@
line: 'A 区 1 号餐线',
cashier: '王五',
time: '2026-08-03 18:15:20',
payTime: '2026-08-03 18:15:25',
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 }
{ name: '清蒸鲈鱼', zone: '档口秤', weight: '200g', price: 25.00, qty: 1, subtotal: 25.00 },
{ name: '蒜蓉西兰花', zone: '营养秤', weight: '150g', price: 6.00, qty: 1, subtotal: 6.00 },
{ name: '紫米饭', zone: '营养秤', weight: '200g', price: 3.00, qty: 1, subtotal: 3.00 }
],
total: 28.00, discount: 0, payable: 28.00, paid: 28.00,
payMethod: '扫码支付', balance: 1212.50,
payType: '扫码支付', payMethod: '微信支付', balance: 1212.50,
nutrition: { calories: 510, protein: 32, fat: 14, carb: 65, score: 90 },
printed: false
},
@@ -629,14 +654,15 @@
line: 'A 区 3 号餐线',
cashier: '张三',
time: '2026-08-03 12:48:10',
payTime: '2026-08-03 12:48:15',
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 }
{ name: '番茄炒蛋', zone: '营养秤', weight: '150g', price: 8.00, qty: 1, subtotal: 8.00 },
{ name: '麻婆豆腐', zone: '档口秤', weight: '180g', price: 10.00, qty: 1, subtotal: 10.00 },
{ name: '米饭', zone: '营养秤', weight: '250g', price: 2.00, qty: 1, subtotal: 2.00 },
{ name: '番茄蛋汤', zone: '档口秤', weight: '280g', price: 5.00, qty: 1, subtotal: 5.00 }
],
total: 25.00, discount: 0, payable: 25.00, paid: 25.00,
payMethod: '人脸支付', balance: 1187.50,
payType: '会员支付', payMethod: '人脸支付', balance: 1187.50,
nutrition: { calories: 480, protein: 22, fat: 15, carb: 70, score: 78 },
printed: false
},
@@ -647,12 +673,13 @@
line: 'A 区 2 号餐线',
cashier: '李四',
time: '2026-08-03 08:15:30',
payTime: '2026-08-03 08:15:33',
items: [
{ name: '皮蛋瘦肉粥', price: 5.00, qty: 1, subtotal: 5.00 },
{ name: '鲜肉包子', price: 3.00, qty: 1, subtotal: 3.00 }
{ name: '皮蛋瘦肉粥', zone: '档口秤', weight: '350g', price: 5.00, qty: 1, subtotal: 5.00 },
{ name: '鲜肉包子', zone: '营养秤', weight: '150g', price: 3.00, qty: 1, subtotal: 3.00 }
],
total: 8.00, discount: 0, payable: 8.00, paid: 8.00,
payMethod: '餐卡支付', balance: 1195.50,
payType: '扫码支付', payMethod: '支付宝支付', balance: 1195.50,
nutrition: { calories: 320, protein: 12, fat: 8, carb: 48, score: 70 },
printed: false
}
@@ -717,17 +744,30 @@
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 zoneOrder = ['营养秤', '档口秤'];
var zones = {};
order.items.forEach(function(it) {
if (!zones[it.zone]) zones[it.zone] = [];
zones[it.zone].push(it);
});
var itemsHtml = '';
zoneOrder.forEach(function(zoneName) {
var zoneItems = zones[zoneName];
if (!zoneItems || zoneItems.length === 0) return;
itemsHtml += '<tr class="receipt-zone-header"><td colspan="4">【' + zoneName + '】</td></tr>';
zoneItems.forEach(function(it) {
itemsHtml += '<tr>'
+ '<td>' + it.name + '<span style="color:#999;font-size:10px;">' + it.weight + '</span></td>'
+ '<td>' + it.price.toFixed(2) + '</td>'
+ '<td>' + it.qty + '</td>'
+ '<td>' + it.subtotal.toFixed(2) + '</td>'
+ '</tr>';
});
});
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>'
? '<div class="receipt-row"><span class="receipt-row__label">优惠</span><span class="receipt-row__value">-' + order.discount.toFixed(2) + '</span></div>'
: '';
var html = ''
@@ -745,11 +785,12 @@
+ '<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 class="receipt-row"><span class="receipt-row__label">付款时间</span><span class="receipt-row__value">' + order.payTime + '</span></div>'
+ '</div>'
+ '<div class="receipt-divider"></div>'
/* 消费明细表 */
+ '<table class="receipt-table">'
+ '<thead><tr><th>餐品</th><th>单价</th><th>份数</th><th>小计</th></tr></thead>'
+ '<thead><tr><th>餐品</th><th>单价(100g)</th><th>份数</th><th>小计</th></tr></thead>'
+ '<tbody>' + itemsHtml + '</tbody>'
+ '</table>'
+ '<div class="receipt-divider"></div>'
@@ -763,6 +804,7 @@
+ '<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.payType + '</span></div>'
+ '<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>'
@@ -781,9 +823,17 @@
+ '</div>'
+ '<div class="receipt-divider"></div>'
/* 二维码 */
+ '<div class="receipt-qr">'
+ getQRCodeSVG()
+ '<div class="receipt-qr__text">扫码查看电子小票与营养详情</div>'
+ '<div class="receipt-qr--dual">'
+ '<div class="receipt-qr">'
+ getQRCodeEnterpriseSVG()
+ '<div class="receipt-qr__text">扫码加入企业微信群</div>'
+ '<div class="receipt-qr__sub">获取营养知识与优惠活动</div>'
+ '</div>'
+ '<div class="receipt-qr">'
+ getQRCodeMiniProgramSVG()
+ '<div class="receipt-qr__text">扫码进入小程序</div>'
+ '<div class="receipt-qr__sub">查看详细营养分析报告</div>'
+ '</div>'
+ '</div>'
+ '<div class="receipt-divider receipt-divider--double"></div>'
/* 底部提示 */
@@ -798,30 +848,7 @@
}
/* 简化版二维码 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'
];
function generateQRCodeSVG(matrix) {
var size = 21;
var unit = 4;
var dim = size * unit;
@@ -836,6 +863,62 @@
return '<svg class="receipt-qr__img" viewBox="0 0 ' + dim + ' ' + dim + '" xmlns="http://www.w3.org/2000/svg">' + rects + '</svg>';
}
/* 企业微信群二维码 */
function getQRCodeEnterpriseSVG() {
var matrix = [
'111111101001010011100',
'100000101100110000100',
'101110100110101110100',
'101110101110101110110',
'101110100100001110100',
'100000100011100000110',
'111111101010111111100',
'000000001011000000000',
'101000110110010001010',
'010111001101110011010',
'101100100011100111000',
'011010011010101011010',
'100011111001101111010',
'000000001100010010100',
'111111101000011000110',
'100000101110001001000',
'101110101011110111010',
'101110100110001100010',
'101110100101111010010',
'100000101100100100010',
'111111101010011110100'
];
return generateQRCodeSVG(matrix);
}
/* 小程序二维码 */
function getQRCodeMiniProgramSVG() {
var matrix = [
'111111100111101110100',
'100000101001100100100',
'101110101110001110110',
'101110100101001110110',
'101110100011101110000',
'100000100111100000100',
'111111101010111111100',
'000000000111000000000',
'101000110010110110010',
'010011011100000110110',
'101110001010001010010',
'011100101010111100100',
'101011010000111001010',
'000000001111010010010',
'111111101101101101010',
'100000101001000001110',
'101110100001010100100',
'101110101111111110010',
'101110100110101000110',
'100000101000001100100',
'111111101110000110110'
];
return generateQRCodeSVG(matrix);
}
/* 切换订单 */
function selectOrder(orderId) {
currentOrderId = orderId;
@@ -0,0 +1,21 @@
# 结算设备 CHANGELOG
## 2026-08-04 | 打印小票功能完善
### 新增
- `02-print-receipt.html` 新增打印小票预览页面,左右分栏布局(订单列表 + 小票预览)
- **订单信息**:订单号、餐线、收银员、就餐时间、付款时间
- **支付信息**:支付类型(会员支付/扫码支付/现金支付)+ 支付方式(人脸支付/微信支付/支付宝支付/现金支付),含餐卡余额
- **消费明细**:按分区(营养秤/档口秤)分组展示,餐品名称后带克重
- **单价标注**:表头标注单价(100g)
- **营养摘要**:热量、蛋白质、脂肪、碳水四维 + 营养得分
- **双二维码**:左侧扫码加入企业微信群(获取营养知识与优惠活动),右侧扫码进入小程序(查看详细营养分析报告)
- **黑白热敏纸风格**:全小票仅使用黑、白、灰三色,模拟热敏纸打印效果
- 支持按餐次筛选、打印状态筛选、订单选择预览
### 变更
- Mock 数据从 6 个订单增加到完整字段(zone、payTime、payType、weight
- 原单二维码改为双二维码并排布局
### 导航同步
- `checkout-device/index.html` — 已有小票预览入口