fix:合并
This commit is contained in:
@@ -479,7 +479,7 @@
|
||||
],
|
||||
total: 35.50, discount: 3.00, payable: 32.50, paid: 32.50,
|
||||
payType: '会员支付', payMethod: '人脸支付', balance: 1280.50,
|
||||
nutrition: { calories: 685, protein: 28, fat: 18, carb: 95, score: 82, nrv: { calories: 34, protein: 47, fat: 30, carb: 32 } },
|
||||
nutrition: { calories: 685, protein: 28, fat: 18, carb: 95, sodium: 800, score: 82, nrv: { calories: 34, protein: 47, fat: 30, carb: 32, sodium: 40 } },
|
||||
printed: true
|
||||
};
|
||||
|
||||
@@ -585,6 +585,7 @@
|
||||
+ '<tr><td>蛋白质</td><td>' + order.nutrition.protein + '<small>g</small></td><td>' + order.nutrition.nrv.protein + '%</td></tr>'
|
||||
+ '<tr><td>脂肪</td><td>' + order.nutrition.fat + '<small>g</small></td><td>' + order.nutrition.nrv.fat + '%</td></tr>'
|
||||
+ '<tr><td>碳水</td><td>' + order.nutrition.carb + '<small>g</small></td><td>' + order.nutrition.nrv.carb + '%</td></tr>'
|
||||
+ '<tr><td>钠</td><td>' + order.nutrition.sodium + '<small>mg</small></td><td>' + order.nutrition.nrv.sodium + '%</td></tr>'
|
||||
+ '</tbody>'
|
||||
+ '</table>'
|
||||
+ '<div class="receipt-divider"></div>'
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
<div class="login-brand">
|
||||
<div class="login-brand__icon">🏷️</div>
|
||||
<div class="login-brand__name">净菜包装贴标秤</div>
|
||||
<div class="login-brand__desc">接收配比完成订单<br>核对实物 · 包装塑封 · 全程可溯</div>
|
||||
<div class="login-brand__desc">接收净配完成订单<br>核对实物 · 包装塑封 · 全程可溯</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧登录面板 -->
|
||||
|
||||
@@ -319,7 +319,7 @@
|
||||
/* 当前 Tab */
|
||||
var currentFilter = 'pending';
|
||||
|
||||
/* 模拟数据:待打印订单(来自配比秤已完成配比的订单)
|
||||
/* 模拟数据:待打印订单(来自净配秤已完成净配的订单)
|
||||
* 字段说明:
|
||||
* cutSpec: 结构化对象 {length,width,height,shape,technique}(任务 4.4)
|
||||
* packSpec: 结构化对象 {baseGram, unit, portion?, packMethod?}(任务 4.4)
|
||||
|
||||
@@ -420,7 +420,7 @@
|
||||
document.getElementById('topUser').textContent = userName;
|
||||
document.getElementById('userAvatar').textContent = userName.charAt(0);
|
||||
|
||||
/* ---- 模拟订单数据(来自配比秤已完成配比的订单) ----
|
||||
/* ---- 模拟订单数据(来自净配秤已完成净配的订单) ----
|
||||
* 字段说明:
|
||||
* cutSpec: 结构化对象 {length,width,height,shape,technique}(任务 4.4)
|
||||
* packSpec: 结构化对象 {baseGram, unit, portion?, packMethod?}(任务 4.4)
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
- `index.html` — 模块导航页(从占位页改为实际功能菜单入口)
|
||||
|
||||
### 功能说明
|
||||
- 接收配比秤已完成配比的订单(含净菜包和餐品净菜包)
|
||||
- 接收净配秤已完成净配的订单(含净菜包和餐品净菜包)
|
||||
- 净菜包:通过摄像头识别+候选选择+秤重确认弹窗完成秤重
|
||||
- 餐品净菜包:每份用量显示"--",后方有"打印"按钮,打印完状态显示"已打印",按钮可重复点击
|
||||
- 贴标清单:餐品净菜包按餐品维度展示(不按食材展开),份数/需/已称按实际显示
|
||||
@@ -97,7 +97,7 @@
|
||||
- `02-labeling.html` — 净菜包列表项 meta 区新增"净菜类型"字段(尺寸·形状·刀工,如 `20mm*1mm*1mm · 细丝 · 平刀`),餐品净菜包显示"—"
|
||||
|
||||
### 新增页面
|
||||
- `03-history.html` — 贴标历史页(4:3横屏,参考组配秤组配历史的显示逻辑:日视图/本周切换、日期条、搜索框、统计行;参考贴标秤任务列表的表单结构:订单号+类型+完成时间+操作人+表格行)
|
||||
- `03-history.html` — 贴标历史页(4:3横屏,参考净配秤净配历史的显示逻辑:日视图/本周切换、日期条、搜索框、统计行;参考贴标秤任务列表的表单结构:订单号+类型+完成时间+操作人+表格行)
|
||||
|
||||
### 导航同步
|
||||
- 更新根 `index.html` SITE_MAP 新增贴标历史页面条目,贴标订单更名为贴标任务
|
||||
|
||||
@@ -54,52 +54,58 @@
|
||||
.label-panel { display: none; flex-direction: column; gap: 16px; }
|
||||
.label-panel.active { display: flex; }
|
||||
|
||||
/* 单个标签 */
|
||||
/* 单个标签 — 紧凑型 */
|
||||
.label-sticker {
|
||||
width: 100%; background: #fff; color: #1a1a2e; border-radius: 8px;
|
||||
overflow: hidden; box-shadow: 0 4px 16px rgba(0,0,0,0.3);
|
||||
display: flex; flex-direction: row;
|
||||
/* 模拟 80mm × 50mm 热敏标签比例 */
|
||||
min-height: 140px;
|
||||
width: 400px; background: #fff; color: #1a1a2e; border-radius: 6px;
|
||||
overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,0.25);
|
||||
display: flex; flex-direction: row; align-items: stretch;
|
||||
min-height: 90px;
|
||||
}
|
||||
/* 标签主信息区 */
|
||||
.label-sticker__body { flex: 1; padding: 12px 14px; display: flex; flex-direction: column; gap: 4px; }
|
||||
.label-sticker__body { flex: 1; padding: 6px 10px; display: flex; flex-direction: column; gap: 2px; justify-content: center; }
|
||||
/* 标签标题栏 */
|
||||
.label-sticker__title-bar {
|
||||
font-size: 13px; font-weight: 700; color: #fff; padding: 3px 10px; border-radius: 4px;
|
||||
display: inline-block; align-self: flex-start; letter-spacing: 2px;
|
||||
font-size: 12px; font-weight: 700; color: #fff; padding: 2px 8px; border-radius: 3px;
|
||||
display: inline-block; align-self: flex-start; letter-spacing: 1px;
|
||||
}
|
||||
.label-sticker__title-bar--blue { background: #4a90d9; }
|
||||
.label-sticker__title-bar--green { background: #52c41a; }
|
||||
.label-sticker__title-bar--orange { background: #fa8c16; }
|
||||
|
||||
/* 信息行 */
|
||||
.label-info { display: flex; flex-wrap: wrap; gap: 2px 16px; font-size: 11px; }
|
||||
.label-info__item { display: flex; align-items: center; gap: 3px; white-space: nowrap; }
|
||||
.label-info { display: flex; flex-wrap: wrap; gap: 0px 10px; font-size: 10px; line-height: 1.4; }
|
||||
.label-info__item { display: flex; align-items: center; gap: 2px; white-space: nowrap; }
|
||||
.label-info__label { color: #888; }
|
||||
.label-info__value { color: #1a1a2e; font-weight: 600; }
|
||||
|
||||
/* 订单标签 — 包装项明细表 */
|
||||
.label-detail-table { width: 100%; border-collapse: collapse; margin-top: 2px; font-size: 10px; }
|
||||
.label-detail-table { width: 100%; border-collapse: collapse; margin-top: 1px; font-size: 9px; }
|
||||
.label-detail-table th {
|
||||
background: #f0f4f8; color: #666; font-weight: 500; padding: 3px 6px;
|
||||
background: #f0f4f8; color: #666; font-weight: 500; padding: 2px 4px;
|
||||
text-align: left; border-bottom: 1px solid #e0e4e8;
|
||||
}
|
||||
.label-detail-table td {
|
||||
padding: 3px 6px; border-bottom: 1px solid #f0f0f0; color: #333;
|
||||
padding: 2px 4px; border-bottom: 1px solid #f0f0f0; color: #333;
|
||||
}
|
||||
.label-detail-table .type-tag { display: inline-block; padding: 1px 5px; border-radius: 3px; font-size: 9px; font-weight: 500; }
|
||||
.label-detail-table .type-tag { display: inline-block; padding: 1px 4px; border-radius: 2px; font-size: 8px; font-weight: 500; }
|
||||
.type-tag--veg { background: #e6f7ff; color: #4a90d9; }
|
||||
.type-tag--meal { background: #fff7e6; color: #fa8c16; }
|
||||
|
||||
/* 总重量行 */
|
||||
.label-total { font-size: 11px; font-weight: 700; color: #1a1a2e; margin-top: 2px; }
|
||||
.label-total { font-size: 10px; font-weight: 700; color: #1a1a2e; margin-top: 1px; }
|
||||
|
||||
/* 保质期三字段 */
|
||||
.label-shelf { display: flex; gap: 12px; margin-top: 2px; }
|
||||
.label-shelf__item { display: flex; flex-direction: column; align-items: center; padding: 2px 8px; background: #f5f5f5; border-radius: 4px; }
|
||||
.label-shelf__label { font-size: 9px; color: #999; }
|
||||
.label-shelf__value { font-size: 11px; font-weight: 700; color: #333; }
|
||||
.label-shelf { display: flex; gap: 6px; margin-top: 1px; }
|
||||
.label-shelf__item { display: flex; flex-direction: column; align-items: center; padding: 1px 6px; background: #f5f5f5; border-radius: 3px; }
|
||||
.label-shelf__label { font-size: 8px; color: #999; }
|
||||
.label-shelf__value { font-size: 10px; font-weight: 700; color: #333; }
|
||||
|
||||
/* 二维码区 */
|
||||
.label-sticker__qrcode {
|
||||
width: 56px; height: 56px; flex-shrink: 0;
|
||||
align-self: center; margin-right: 6px;
|
||||
}
|
||||
.label-sticker__qrcode svg { display: block; width: 100%; height: 100%; }
|
||||
|
||||
/* 打印按钮 */
|
||||
.action-bar { display: flex; gap: 10px; justify-content: center; flex-shrink: 0; padding: 0 20px 16px; }
|
||||
@@ -144,7 +150,7 @@
|
||||
<span class="tab-dot tab-dot--blue"></span>订单标签
|
||||
</button>
|
||||
<button class="tab-btn tab-btn--green" data-tab="veg" onclick="switchTab('veg')">
|
||||
<span class="tab-dot tab-dot--green"></span>净菜标签
|
||||
<span class="tab-dot tab-dot--green"></span>净菜包标签
|
||||
</button>
|
||||
<button class="tab-btn tab-btn--orange" data-tab="meal" onclick="switchTab('meal')">
|
||||
<span class="tab-dot tab-dot--orange"></span>餐品净菜包标签
|
||||
@@ -330,7 +336,31 @@
|
||||
panel.innerHTML = html;
|
||||
}
|
||||
|
||||
/* ---- 渲染净菜标签 ---- */
|
||||
/* 生成二维码占位 SVG(9×9 网格 + 三角落定位图案) */
|
||||
function qrSvg(code) {
|
||||
var size = 56, cells = 9, cw = size / cells;
|
||||
var hash = 0, i;
|
||||
for (i = 0; i < code.length; i++) { hash = ((hash << 5) - hash) + code.charCodeAt(i); hash |= 0; }
|
||||
var rects = '';
|
||||
var fp = [[0,0],[cells-3,0],[0,cells-3]]; /* 三个定位图案 */
|
||||
for (i = 0; i < cells; i++) {
|
||||
for (var j = 0; j < cells; j++) {
|
||||
var isFinder = false;
|
||||
for (var f = 0; f < 3; f++) {
|
||||
if (i >= fp[f][0] && i < fp[f][0]+3 && j >= fp[f][1] && j < fp[f][1]+3) {
|
||||
var fi = i - fp[f][0], fj = j - fp[f][1];
|
||||
if (fi === 0 || fi === 2 || fj === 0 || fj === 2 || (fi === 1 && fj === 1)) isFinder = true;
|
||||
}
|
||||
}
|
||||
if (isFinder || ((hash >> ((i * cells + j) % 31)) & 1)) {
|
||||
rects += '<rect x="' + (j * cw + 1) + '" y="' + (i * cw + 1) + '" width="' + (cw - 1) + '" height="' + (cw - 1) + '" fill="#1a1a2e"/>';
|
||||
}
|
||||
}
|
||||
}
|
||||
return '<div class="label-sticker__qrcode"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + size + ' ' + size + '" width="56" height="56"><rect width="' + size + '" height="' + size + '" fill="#fff"/>' + rects + '</svg></div>';
|
||||
}
|
||||
|
||||
/* ---- 渲染净菜包标签 ---- */
|
||||
function renderVegLabels() {
|
||||
var panel = document.getElementById('panelVeg');
|
||||
var html = '';
|
||||
@@ -343,15 +373,16 @@
|
||||
html += ''
|
||||
+ '<div class="label-sticker">'
|
||||
+ '<div class="label-sticker__body">'
|
||||
+ '<span class="label-sticker__title-bar label-sticker__title-bar--green">净菜标签</span>'
|
||||
+ '<div class="label-info" style="margin-top:4px">'
|
||||
+ '<div class="label-info">'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">订单号:</span><span class="label-info__value">' + mockData.orderNo + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="label-info">'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">净菜名称:</span><span class="label-info__value">' + v.name + '</span></div>'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">净菜类型:</span><span class="label-info__value">' + formatCutSpec(cutObj) + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="label-info">'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">包装规格:</span><span class="label-info__value">' + formatSpec(specObj) + '</span></div>'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">包装日期:</span><span class="label-info__value">' + mockData.packDate + '</span></div>'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">订单号:</span><span class="label-info__value">' + mockData.orderNo + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="label-shelf">'
|
||||
+ '<div class="label-shelf__item"><span class="label-shelf__label">保质期</span><span class="label-shelf__value">' + v.shelfLife + '</span></div>'
|
||||
@@ -359,6 +390,7 @@
|
||||
+ '<div class="label-shelf__item"><span class="label-shelf__label">保质期至</span><span class="label-shelf__value">' + v.expiryDate + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ qrSvg(code)
|
||||
+ '</div>';
|
||||
}
|
||||
});
|
||||
@@ -378,14 +410,15 @@
|
||||
html += ''
|
||||
+ '<div class="label-sticker">'
|
||||
+ '<div class="label-sticker__body">'
|
||||
+ '<span class="label-sticker__title-bar label-sticker__title-bar--orange">餐品净菜包标签</span>'
|
||||
+ '<div class="label-info" style="margin-top:4px">'
|
||||
+ '<div class="label-info">'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">订单号:</span><span class="label-info__value">' + mockData.orderNo + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="label-info">'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">餐品名称:</span><span class="label-info__value">' + m.name + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="label-info">'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">包装规格:</span><span class="label-info__value">' + formatSpec(specObj) + '</span></div>'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">包装日期:</span><span class="label-info__value">' + mockData.packDate + '</span></div>'
|
||||
+ '<div class="label-info__item"><span class="label-info__label">订单号:</span><span class="label-info__value">' + mockData.orderNo + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '<div class="label-shelf">'
|
||||
+ '<div class="label-shelf__item"><span class="label-shelf__label">保质期</span><span class="label-shelf__value">' + m.shelfLife + '</span></div>'
|
||||
@@ -393,6 +426,7 @@
|
||||
+ '<div class="label-shelf__item"><span class="label-shelf__label">保质期至</span><span class="label-shelf__value">' + m.expiryDate + '</span></div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ qrSvg(code)
|
||||
+ '</div>';
|
||||
}
|
||||
});
|
||||
@@ -428,7 +462,7 @@
|
||||
if (currentTab === 'order') labels.push('订单标签');
|
||||
else if (currentTab === 'veg') {
|
||||
mockData.vegPacks.forEach(function(v) {
|
||||
for (var p = 0; p < v.packSize; p++) { labels.push('净菜标签 — ' + v.name); }
|
||||
for (var p = 0; p < v.packSize; p++) { labels.push('净菜包标签 — ' + v.name); }
|
||||
});
|
||||
} else if (currentTab === 'meal') {
|
||||
mockData.mealPacks.forEach(function(m) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 员工登录 | 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 员工登录 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -95,7 +95,7 @@
|
||||
<body>
|
||||
<div class="login-page">
|
||||
<div class="login-card">
|
||||
<div class="login-card__title">净菜加工秤</div>
|
||||
<div class="login-card__title">厨配秤</div>
|
||||
<div class="login-card__subtitle">员工登录</div>
|
||||
<div class="login-tabs">
|
||||
<div class="login-tab active" data-tab="face" onclick="switchTab('face')">人脸识别</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 净菜加工 | 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 厨配 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -285,7 +285,7 @@
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-bar">
|
||||
<a class="back-btn" id="backBtn" href="javascript:void(0)">◀ 返回</a>
|
||||
<span class="page-title">净菜加工</span>
|
||||
<span class="page-title">厨配</span>
|
||||
<div class="user-info">
|
||||
<div class="avatar" id="userAvatar">赵</div>
|
||||
<span id="userName">赵六</span>
|
||||
@@ -335,7 +335,7 @@
|
||||
|
||||
<!-- 右列:净菜列表 -->
|
||||
<div class="col-right">
|
||||
<!-- 净菜加工卡片列表 -->
|
||||
<!-- 厨配卡片列表 -->
|
||||
<button class="fetch-task-btn" onclick="handleFetchTask()">📥 获取任务</button>
|
||||
<div class="food-cards" id="foodCards"></div>
|
||||
|
||||
@@ -347,12 +347,12 @@
|
||||
<!-- 重量确认弹窗 -->
|
||||
<div class="modal-overlay" id="weightModal">
|
||||
<div class="modal">
|
||||
<div class="modal__title">本次加工确认</div>
|
||||
<div class="modal__title">本次厨配确认</div>
|
||||
<div class="modal__food">
|
||||
<span class="modal__food-icon" id="modalIcon"></span>
|
||||
<span id="modalFoodName">--</span>
|
||||
</div>
|
||||
<div class="modal__label">本次加工重量</div>
|
||||
<div class="modal__label">本次厨配重量</div>
|
||||
<div class="modal__weight-row">
|
||||
<span class="modal__weight-value" id="modalWeight">0.0</span>
|
||||
<button class="modal__weight-refresh" onclick="refreshWeight()" title="重新获取秤上重量">🔄</button>
|
||||
@@ -417,7 +417,7 @@
|
||||
document.getElementById('userName').textContent = userName;
|
||||
document.getElementById('userAvatar').textContent = userName.charAt(0);
|
||||
|
||||
// 初始化净菜列表(带累计已加工重量)
|
||||
// 初始化净菜列表(带累计已厨配重量)
|
||||
acceptedFoods = mockFoods.map(function(f) {
|
||||
foodIdCounter++;
|
||||
return { id: foodIdCounter, name: f.name, size: f.size, shape: f.shape, blade: f.blade, target: f.target, processed: f.processed, arrivalTime: f.arrivalTime, icon: f.icon };
|
||||
@@ -448,7 +448,7 @@
|
||||
+ '<span class="food-card__spec">' + f.size + ' · ' + f.shape + ' · ' + f.blade + '</span>'
|
||||
+ '<div class="food-card__progress">'
|
||||
+ '<div style="display:flex;justify-content:space-between;">'
|
||||
+ '<span class="food-card__progress-label">累计加工(斤)</span>'
|
||||
+ '<span class="food-card__progress-label">累计厨配(斤)</span>'
|
||||
+ '<span class="food-card__progress-text">' + f.processed.toFixed(1) + ' / ' + f.target.toFixed(1) + '</span>'
|
||||
+ '</div>'
|
||||
+ '<div class="food-card__progress-wrap">'
|
||||
@@ -458,7 +458,7 @@
|
||||
+ '</div>';
|
||||
});
|
||||
if (html === '') {
|
||||
html = '<div style="grid-column:1/-1;text-align:center;padding:40px;color:rgba(255,255,255,0.2);font-size:13px;">暂无加工净菜,点击上方「获取任务」添加</div>';
|
||||
html = '<div style="grid-column:1/-1;text-align:center;padding:40px;color:rgba(255,255,255,0.2);font-size:13px;">暂无厨配净菜,点击上方「获取任务」添加</div>';
|
||||
}
|
||||
container.innerHTML = html;
|
||||
}
|
||||
@@ -490,7 +490,7 @@
|
||||
}
|
||||
|
||||
function doFetchTask() {
|
||||
showToast('正在获取加工任务...', 800);
|
||||
showToast('正在获取厨配任务...', 800);
|
||||
setTimeout(function() {
|
||||
var existingNames = acceptedFoods.filter(function(f) { return f.processed < f.target; }).map(function(f) { return f.name; });
|
||||
var available = foodDatabase.filter(function(f) { return existingNames.indexOf(f.name) === -1; });
|
||||
@@ -504,7 +504,7 @@
|
||||
foodIdCounter++;
|
||||
acceptedFoods.push({ id: foodIdCounter, name: food.name, size: food.size, shape: food.shape, blade: food.blade, target: target, processed: processed, arrivalTime: food.arrivalTime, icon: food.icon });
|
||||
renderFoodCards();
|
||||
showToast('已获取:' + food.name + '(应加工 ' + target.toFixed(1) + '斤)', 1500);
|
||||
showToast('已获取:' + food.name + '(应厨配 ' + target.toFixed(1) + '斤)', 1500);
|
||||
}, 800);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 功能菜单 | 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 功能菜单 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -68,7 +68,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="top-bar">
|
||||
<span class="top-bar__title">净菜加工秤</span>
|
||||
<span class="top-bar__title">厨配秤</span>
|
||||
<div class="top-bar__user">
|
||||
<div class="top-bar__avatar" id="menuAvatar">赵</div>
|
||||
<span id="menuUserName">赵六</span>
|
||||
@@ -83,8 +83,8 @@
|
||||
<a class="menu-btn" id="btnAccept" href="javascript:void(0)" onclick="handleMenu('01-accept.html')">
|
||||
<div class="menu-btn__icon menu-btn__icon--blue">✅</div>
|
||||
<div class="menu-btn__info">
|
||||
<div class="menu-btn__title">净菜加工</div>
|
||||
<div class="menu-btn__desc">净菜上秤识别、加工确认</div>
|
||||
<div class="menu-btn__title">厨配</div>
|
||||
<div class="menu-btn__desc">净菜上秤识别、厨配确认</div>
|
||||
</div>
|
||||
<div class="menu-btn__arrow">›</div>
|
||||
</a>
|
||||
@@ -107,8 +107,8 @@
|
||||
<a class="menu-btn" id="btnHistory" href="javascript:void(0)" onclick="handleMenu('02-history.html')">
|
||||
<div class="menu-btn__icon menu-btn__icon--gray">📝</div>
|
||||
<div class="menu-btn__info">
|
||||
<div class="menu-btn__title">加工记录</div>
|
||||
<div class="menu-btn__desc">历史加工验收记录查看与筛选</div>
|
||||
<div class="menu-btn__title">厨配记录</div>
|
||||
<div class="menu-btn__desc">历史厨配验收记录查看与筛选</div>
|
||||
</div>
|
||||
<div class="menu-btn__arrow">›</div>
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 加工记录 | 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 厨配记录 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -233,7 +233,7 @@
|
||||
|
||||
<div class="top-bar">
|
||||
<a class="back-btn" id="backBtn" href="javascript:void(0)">◀ 返回</a>
|
||||
<span class="page-title">加工记录</span>
|
||||
<span class="page-title">厨配记录</span>
|
||||
<span class="top-bar__user" id="topUser">--</span>
|
||||
<button class="logout-btn" onclick="handleLogout()">退出</button>
|
||||
</div>
|
||||
@@ -263,7 +263,7 @@
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-box">
|
||||
<span class="search-box__icon">🔍</span>
|
||||
<input class="search-box__input" id="searchInput" type="text" placeholder="搜索加工号 / 净菜" oninput="onSearchInput()">
|
||||
<input class="search-box__input" id="searchInput" type="text" placeholder="搜索厨配号 / 净菜" oninput="onSearchInput()">
|
||||
<button class="search-box__clear" id="searchClear" onclick="clearSearch()">×</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -272,11 +272,11 @@
|
||||
<div class="stats-row">
|
||||
<div class="stat-chip">
|
||||
<div class="stat-chip__icon stat-chip__icon--blue">📋</div>
|
||||
<div><div class="stat-chip__value" id="statTotal">6</div><div class="stat-chip__label" id="statTotalLabel">今日加工总数</div></div>
|
||||
<div><div class="stat-chip__value" id="statTotal">6</div><div class="stat-chip__label" id="statTotalLabel">今日厨配总数</div></div>
|
||||
</div>
|
||||
<div class="stat-chip">
|
||||
<div class="stat-chip__icon stat-chip__icon--green">⚖️</div>
|
||||
<div><div class="stat-chip__value" id="statWeight">0</div><div class="stat-chip__label">加工总重量(斤)</div></div>
|
||||
<div><div class="stat-chip__value" id="statWeight">0</div><div class="stat-chip__label">厨配总重量(斤)</div></div>
|
||||
</div>
|
||||
<div class="stat-chip">
|
||||
<div class="stat-chip__icon stat-chip__icon--orange">🥬</div>
|
||||
@@ -289,14 +289,14 @@
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead><tr id="tableHead">
|
||||
<th>加工号</th><th>净菜</th><th>净菜类型</th>
|
||||
<th>加工重量</th><th>完成时间</th>
|
||||
<th>厨配号</th><th>净菜</th><th>净菜类型</th>
|
||||
<th>厨配重量</th><th>完成时间</th>
|
||||
</tr></thead>
|
||||
<tbody id="tableBody"></tbody>
|
||||
</table>
|
||||
<div class="empty-state" id="emptyState">
|
||||
<div class="empty-state__icon">📭</div>
|
||||
<div>暂无加工记录</div>
|
||||
<div>暂无厨配记录</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-footer" id="tableFooter">共 0 条记录</div>
|
||||
@@ -324,7 +324,7 @@
|
||||
var TODAY = '2026-06-17';
|
||||
var WEEKDAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
|
||||
// 加工记录数据(净菜加工语义)
|
||||
// 厨配记录数据(厨配语义)
|
||||
var RECORDS = [
|
||||
{ batch:'JC-20260617-001', orderId:'PO-20260617-001', time:'2026-06-17 09:35', dish:'土豆', spec:'35mm*1mm*1mm · 长条 · 平刀', weight:5.2, duration:'5分钟' },
|
||||
{ batch:'JC-20260617-002', orderId:'PO-20260617-001', time:'2026-06-17 09:52', dish:'黄瓜', spec:'30mm*30mm*3mm · 圆片 · 平刀', weight:4.2, duration:'4分钟' },
|
||||
@@ -448,10 +448,10 @@
|
||||
var monday = getMonday();
|
||||
var mondayStr = dateStr(monday);
|
||||
records = RECORDS.filter(function(r) { return r.time.substring(0, 10) >= mondayStr; });
|
||||
totalLabel = '本周加工总数';
|
||||
totalLabel = '本周厨配总数';
|
||||
} else {
|
||||
records = RECORDS.filter(function(r) { return r.time.substring(0, 10) === selectedDate; });
|
||||
totalLabel = (selectedDate === TODAY) ? '今日加工总数' : (selectedDate.substring(5).replace('-', '/') + ' 加工总数');
|
||||
totalLabel = (selectedDate === TODAY) ? '今日厨配总数' : (selectedDate.substring(5).replace('-', '/') + ' 厨配总数');
|
||||
}
|
||||
|
||||
var total = records.length;
|
||||
@@ -514,8 +514,8 @@
|
||||
var thead = document.getElementById('tableHead');
|
||||
|
||||
var dateCol = isWeeklyMode ? '<th>日期</th>' : '';
|
||||
thead.innerHTML = dateCol + '<th>加工号</th><th>净菜</th><th>净菜类型</th>'
|
||||
+ '<th>加工重量</th><th>完成时间</th>';
|
||||
thead.innerHTML = dateCol + '<th>厨配号</th><th>净菜</th><th>净菜类型</th>'
|
||||
+ '<th>厨配重量</th><th>完成时间</th>';
|
||||
|
||||
if (records.length === 0) {
|
||||
tbody.innerHTML = '';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 设备设置 | 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 设备设置 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif; background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden; }
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<div class="settings-menu">
|
||||
<div class="menu-item active" onclick="switchSection('cutting', this)"><span class="menu-item__icon">🔪</span><span>加工参数</span></div>
|
||||
<div class="menu-item active" onclick="switchSection('cutting', this)"><span class="menu-item__icon">🔪</span><span>厨配参数</span></div>
|
||||
<div class="menu-item" onclick="switchSection('ai', this)"><span class="menu-item__icon">🤖</span><span>AI识别</span></div>
|
||||
<div class="menu-item" onclick="switchSection('scale', this)"><span class="menu-item__icon">⚖️</span><span>秤参数</span></div>
|
||||
<div class="menu-item" onclick="switchSection('device', this)"><span class="menu-item__icon">💻</span><span>设备信息</span></div>
|
||||
@@ -67,7 +67,7 @@
|
||||
</div>
|
||||
<div class="settings-content">
|
||||
<div class="settings-section" id="section-cutting">
|
||||
<div class="section-title">加工参数配置</div>
|
||||
<div class="section-title">厨配参数配置</div>
|
||||
<div class="setting-item__hint" style="background:rgba(74,144,217,0.08);border:1px solid rgba(74,144,217,0.2);border-radius:8px;padding:10px 12px;margin-bottom:12px;font-size:11px;color:rgba(255,255,255,0.6);line-height:1.5;">
|
||||
ℹ️ 以下默认值从食材档案 <code style="color:#4a90d9">vegTypes.cutSpec</code> 自动读取,此处仅作设备级覆盖(切配类型字典 <code style="color:#4a90d9">spec_cut_shape</code>)。
|
||||
</div>
|
||||
@@ -120,7 +120,7 @@
|
||||
<div class="settings-section" id="section-device" style="display:none">
|
||||
<div class="section-title">设备信息</div>
|
||||
<div class="device-info">
|
||||
<div class="info-item"><div class="info-item__label">设备名称</div><div class="info-item__value">净菜加工秤-01</div></div>
|
||||
<div class="info-item"><div class="info-item__label">设备名称</div><div class="info-item__value">厨配秤-01</div></div>
|
||||
<div class="info-item"><div class="info-item__label">设备编号</div><div class="info-item__value">JCJG-2026-001</div></div>
|
||||
<div class="info-item"><div class="info-item__label">固件版本</div><div class="info-item__value">v2.3.1</div></div>
|
||||
<div class="info-item"><div class="info-item__label">AI模型版本</div><div class="info-item__value">cut-ai-v3.1</div></div>
|
||||
@@ -135,7 +135,7 @@
|
||||
<div class="device-info">
|
||||
<div class="info-item"><div class="info-item__label">系统名称</div><div class="info-item__value">蚁熊智能餐养平台</div></div>
|
||||
<div class="info-item"><div class="info-item__label">系统版本</div><div class="info-item__value">v5.8.0</div></div>
|
||||
<div class="info-item"><div class="info-item__label">设备类型</div><div class="info-item__value">净菜加工秤</div></div>
|
||||
<div class="info-item"><div class="info-item__label">设备类型</div><div class="info-item__value">厨配秤</div></div>
|
||||
<div class="info-item"><div class="info-item__label">制造商</div><div class="info-item__value">蚁熊智能科技有限公司</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 净菜采样 | 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 净菜采样 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 采样历史 | 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 采样历史 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 净菜加工秤 CHANGELOG
|
||||
# 厨配秤 CHANGELOG
|
||||
|
||||
## [2026-06-17] refactor: 任务截止时间格式改为年月日时间
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
### 变更
|
||||
- 删除旧页面:`01-cutting-tasks.html`(任务列表)、`02-weighing.html`(称重录入)、`03-cutting.html`(切配操作)
|
||||
- 新建 `01-accept.html`(净菜验收主操作页):模仿毛菜验收秤流程,包含任务选择→上秤识别→验收确认两步操作
|
||||
- 重命名 `04-history.html` → `02-history.html`:「切配记录」→「验收记录」,规格标签由切法改为加工规格
|
||||
- 重命名 `05-settings.html` → `03-settings.html`:「切配参数」→「加工参数」
|
||||
- 重命名 `04-history.html` → `02-history.html`:「切配记录」→「验收记录」,规格标签由切法改为厨配规格
|
||||
- 重命名 `05-settings.html` → `03-settings.html`:「切配参数」→「厨配参数」
|
||||
- 更新 `00-welcome.html`:功能菜单由4个缩减为3个(净菜验收、验收记录、设置)
|
||||
- 更新 `index.html`:导航卡片同步更新为新页面结构
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
## [1.0.0] - 2026-06-13
|
||||
|
||||
### 新增
|
||||
- 首次创建净菜加工秤模块(6个页面)
|
||||
- 首次创建厨配秤模块(6个页面)
|
||||
- `00-welcome.html` — 欢迎页:人脸识别+账号密码登录+功能菜单
|
||||
- `01-cutting-tasks.html` — 今日切配任务列表:任务卡片含切法标签、状态筛选、进度展示
|
||||
- `02-weighing.html` — 称重录入:AI识别品类、实时称重、确认后跳转切配页面
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1280, initial-scale=1.0">
|
||||
<title>净菜加工秤 - 蚁熊智能餐养平台</title>
|
||||
<title>厨配秤 - 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif; background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden; }
|
||||
@@ -24,7 +24,7 @@
|
||||
<body>
|
||||
<div class="top-bar">
|
||||
<a class="back-link" href="../index.html">◀ 返回</a>
|
||||
<span class="top-bar__title">净菜加工秤</span>
|
||||
<span class="top-bar__title">厨配秤</span>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<a class="card" href="00-welcome.html">
|
||||
@@ -38,8 +38,8 @@
|
||||
<a class="card" href="01-accept.html">
|
||||
<span class="card__icon">✅</span>
|
||||
<div class="card__info">
|
||||
<div class="card__title">净菜加工</div>
|
||||
<div class="card__desc">净菜上秤识别、加工确认</div>
|
||||
<div class="card__title">厨配</div>
|
||||
<div class="card__desc">净菜上秤识别、厨配确认</div>
|
||||
</div>
|
||||
<span class="card__arrow">›</span>
|
||||
</a>
|
||||
@@ -62,8 +62,8 @@
|
||||
<a class="card" href="02-history.html">
|
||||
<span class="card__icon">📝</span>
|
||||
<div class="card__info">
|
||||
<div class="card__title">加工记录</div>
|
||||
<div class="card__desc">历史加工记录查看与筛选</div>
|
||||
<div class="card__title">厨配记录</div>
|
||||
<div class="card__desc">历史厨配记录查看与筛选</div>
|
||||
</div>
|
||||
<span class="card__arrow">›</span>
|
||||
</a>
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
<a class="card" href="clean-veg-ratio-scale/index.html">
|
||||
<span class="card__status card__status--done">已设计</span>
|
||||
<div class="card__icon">⚖️</div>
|
||||
<div class="card__name">净菜加工秤</div>
|
||||
<div class="card__name">厨配秤</div>
|
||||
<div class="card__desc">净菜加工:任务驱动,含尺寸/形状/刀工要求</div>
|
||||
</a>
|
||||
<a class="card" href="clean-veg-inventory-cabinet/index.html">
|
||||
@@ -190,14 +190,14 @@
|
||||
<a class="card" href="ratio-scale/index.html">
|
||||
<span class="card__status card__status--done">已设计</span>
|
||||
<div class="card__icon">📦</div>
|
||||
<div class="card__name">净菜组配秤</div>
|
||||
<div class="card__desc">餐品净菜包独立组配,主辅材+调料双区称重</div>
|
||||
<div class="card__name">净配秤</div>
|
||||
<div class="card__desc">餐品净菜包独立净配,主辅材+调料双区称重</div>
|
||||
</a>
|
||||
<a class="card" href="clean-veg-packaging-labeler/index.html">
|
||||
<span class="card__status card__status--done">已设计</span>
|
||||
<div class="card__icon">🏷️</div>
|
||||
<div class="card__name">净菜包装贴标秤</div>
|
||||
<div class="card__desc">接收配比完成订单,核对实物、包装塑封</div>
|
||||
<div class="card__desc">接收净配完成订单,核对实物、包装塑封</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=720, initial-scale=1.0">
|
||||
<title>净菜组配秤 - 登录 | 蚁熊智能餐养平台</title>
|
||||
<title>净配秤 - 登录 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -143,7 +143,7 @@
|
||||
|
||||
<div class="device-frame">
|
||||
<div class="top-bar">
|
||||
<span class="top-bar__title">净菜组配秤 - 员工登录</span>
|
||||
<span class="top-bar__title">净配秤 - 员工登录</span>
|
||||
</div>
|
||||
|
||||
<div class="login-main">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=720, initial-scale=1.0">
|
||||
<title>净菜组配秤 - 功能菜单 | 蚁熊智能餐养平台</title>
|
||||
<title>净配秤 - 功能菜单 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -121,7 +121,7 @@
|
||||
|
||||
<div class="device-frame">
|
||||
<div class="top-bar">
|
||||
<span class="top-bar__title">净菜组配秤 - 功能菜单</span>
|
||||
<span class="top-bar__title">净配秤 - 功能菜单</span>
|
||||
<span class="top-bar__user" id="topUser">--</span>
|
||||
<button class="logout-btn" onclick="handleLogout()">退出登录</button>
|
||||
</div>
|
||||
@@ -136,8 +136,8 @@
|
||||
<a class="menu-btn" id="btnTasks" onclick="handleMenu('01-packing-tasks.html')">
|
||||
<div class="menu-btn__icon menu-btn__icon--blue">📋</div>
|
||||
<div class="menu-btn__info">
|
||||
<div class="menu-btn__title">组配任务</div>
|
||||
<div class="menu-btn__desc">查看系统分配的组配任务清单</div>
|
||||
<div class="menu-btn__title">净配任务</div>
|
||||
<div class="menu-btn__desc">查看系统分配的净配任务清单</div>
|
||||
</div>
|
||||
<span class="menu-btn__badge" id="taskBadge">0</span>
|
||||
<div class="menu-btn__arrow">›</div>
|
||||
@@ -145,8 +145,8 @@
|
||||
<a class="menu-btn" onclick="handleMenu('03-verify-history.html')">
|
||||
<div class="menu-btn__icon menu-btn__icon--green">📝</div>
|
||||
<div class="menu-btn__info">
|
||||
<div class="menu-btn__title">组配记录</div>
|
||||
<div class="menu-btn__desc">历史组配记录查看与筛选</div>
|
||||
<div class="menu-btn__title">净配记录</div>
|
||||
<div class="menu-btn__desc">历史净配记录查看与筛选</div>
|
||||
</div>
|
||||
<div class="menu-btn__arrow">›</div>
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=720, initial-scale=1.0">
|
||||
<title>净菜组配秤 - 组配任务 | 蚁熊智能餐养平台</title>
|
||||
<title>净配秤 - 净配任务 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -141,14 +141,14 @@
|
||||
<div class="device-frame">
|
||||
<div class="top-bar">
|
||||
<a class="back-btn" id="backBtn" href="javascript:void(0)">◀ 返回</a>
|
||||
<span class="top-bar__title">组配任务</span>
|
||||
<span class="top-bar__title">净配任务</span>
|
||||
<span class="top-bar__user" id="topUser">--</span>
|
||||
</div>
|
||||
|
||||
<div class="main-content">
|
||||
<!-- Tab 切换 -->
|
||||
<div class="tabs">
|
||||
<div class="tab active" data-filter="pending" onclick="switchTab('pending', this)">待组配 <span class="tab__count" id="pendingCount">0</span></div>
|
||||
<div class="tab active" data-filter="pending" onclick="switchTab('pending', this)">待净配 <span class="tab__count" id="pendingCount">0</span></div>
|
||||
<div class="tab" data-filter="done" onclick="switchTab('done', this)">已完成 <span class="tab__count" id="doneCount">0</span></div>
|
||||
</div>
|
||||
|
||||
@@ -345,10 +345,10 @@
|
||||
return sea.weighed >= sea.weightPerPack * pack.packSize;
|
||||
});
|
||||
if (ingDone && seaDone) return { text: '已完成', cls: 'status-badge--done' };
|
||||
return { text: '待组配', cls: 'status-badge--pending' };
|
||||
return { text: '待净配', cls: 'status-badge--pending' };
|
||||
}
|
||||
|
||||
/* 判断餐品净菜包是否有待组配项(主辅材或调料) */
|
||||
/* 判断餐品净菜包是否有待净配项(主辅材或调料) */
|
||||
function hasPendingPack(pack) {
|
||||
var ingPending = pack.ingredients.some(function(ing) {
|
||||
return ing.weighed < ing.weightPerPack * pack.packSize;
|
||||
@@ -385,7 +385,7 @@
|
||||
'<span class="task-card__order-info"><span class="task-card__order-info__label">订单号:</span><span class="task-card__order-info__value">' + order.orderNo + '</span></span>' +
|
||||
'<span class="order-type-tag ' + orderTypeCls + '">' + orderTypeLabel + '</span>' +
|
||||
'<span class="task-card__arrival">⏰ ' + (orderAllDone ? '任务完成时间' : '任务截止时间') + ':<span class="task-card__arrival__time">' + order.expectedArrival + '</span></span>' +
|
||||
'<span class="task-card__status"><span class="status-badge ' + (orderAllDone ? 'status-badge--done' : 'status-badge--pending') + '">' + (orderAllDone ? '已完成' : '待组配') + '</span></span>';
|
||||
'<span class="task-card__status"><span class="status-badge ' + (orderAllDone ? 'status-badge--done' : 'status-badge--pending') + '">' + (orderAllDone ? '已完成' : '待净配') + '</span></span>';
|
||||
taskCard.appendChild(header);
|
||||
|
||||
/* 表格头(6列:名称/包装规格/应称重量/已称重量/状态/操作) */
|
||||
@@ -430,8 +430,8 @@
|
||||
'<div style="text-align:center"><span class="pack-spec-tag">' + specText + '</span></div>' +
|
||||
'<div class="parent-weight ' + weightClass + '">' + totalNeed + ' 克</div>' +
|
||||
'<div class="parent-weight ' + weightClass + '">' + totalWeighed + ' 克</div>' +
|
||||
'<div style="text-align:center"><span class="status-tag ' + (packDone ? 'status-tag--done' : 'status-tag--wait') + '">' + (packDone ? '已完成' : '待组配') + '</span></div>' +
|
||||
'<div style="text-align:center">' + (packDone ? '' : '<button class="task-card__pack-btn" onclick="handlePackWeigh(\'' + order.taskNo + '\',\'' + order.orderNo + '\',\'' + encodeURIComponent(pack.packName) + '\')">组配</button>') + '</div>';
|
||||
'<div style="text-align:center"><span class="status-tag ' + (packDone ? 'status-tag--done' : 'status-tag--wait') + '">' + (packDone ? '已完成' : '待净配') + '</span></div>' +
|
||||
'<div style="text-align:center">' + (packDone ? '' : '<button class="task-card__pack-btn" onclick="handlePackWeigh(\'' + order.taskNo + '\',\'' + order.orderNo + '\',\'' + encodeURIComponent(pack.packName) + '\')">净配</button>') + '</div>';
|
||||
tableBody.appendChild(parentRow);
|
||||
});
|
||||
|
||||
@@ -461,7 +461,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* 进入餐品净菜包组配详情 */
|
||||
/* 进入餐品净菜包净配详情 */
|
||||
function handlePackWeigh(taskNo, orderNo, packName) {
|
||||
var target = '02-weighing-packing.html?task=' + taskNo + '&order=' + encodeURIComponent(orderNo) + '&pack=' + packName;
|
||||
if (userName) target += '&user=' + encodeURIComponent(userName);
|
||||
@@ -481,7 +481,7 @@
|
||||
|
||||
/* 初始化 */
|
||||
renderTaskList();
|
||||
/* 初始过滤:待组配 Tab 默认不显示已完成订单 */
|
||||
/* 初始过滤:待净配 Tab 默认不显示已完成订单 */
|
||||
switchTab('pending', document.querySelector('.tab[data-filter="pending"]'));
|
||||
|
||||
/* 返回按钮 */
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=720, initial-scale=1.0">
|
||||
<title>净菜组配秤 - 组配任务 | 蚁熊智能餐养平台</title>
|
||||
<title>净配秤 - 净配任务 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -305,7 +305,7 @@
|
||||
.section-divider__icon { font-size: 14px; }
|
||||
.section-divider__line { flex: 1; height: 1px; background: rgba(255,255,255,0.08); }
|
||||
|
||||
/* 调料组配区展开/折叠开关 */
|
||||
/* 调料净配区展开/折叠开关 */
|
||||
.seasoning-toggle {
|
||||
display: flex; align-items: center; gap: 6px; margin-left: auto;
|
||||
cursor: pointer; user-select: none; flex-shrink: 0;
|
||||
@@ -328,7 +328,7 @@
|
||||
background: #fff; transform: translateX(20px); color: #4a90d9;
|
||||
}
|
||||
|
||||
/* ===== 调料组配网格 ===== */
|
||||
/* ===== 调料净配网格 ===== */
|
||||
.seasoning-section { flex-shrink: 0; display: flex; flex-direction: column; gap: 6px; }
|
||||
.seasoning-grid {
|
||||
display: grid;
|
||||
@@ -368,7 +368,7 @@
|
||||
}
|
||||
.seasoning-cell--info .sea-slot__status--wait { background: rgba(250,173,21,0.15); color: #faad14; }
|
||||
.seasoning-cell--info .sea-slot__status--done { background: rgba(82,196,26,0.15); color: #52c41a; }
|
||||
/* 信息格待组配也加脉冲 */
|
||||
/* 信息格待净配也加脉冲 */
|
||||
.seasoning-cell--info.seasoning-cell--pending {
|
||||
border: 2px solid #4a90d9; background: rgba(74,144,217,0.15);
|
||||
box-shadow: 0 0 0 2px rgba(74,144,217,0.3), 0 0 12px rgba(74,144,217,0.4);
|
||||
@@ -378,13 +378,13 @@
|
||||
.seasoning-cell--slot { cursor: pointer; }
|
||||
.seasoning-cell--slot:hover { border-color: rgba(74,144,217,0.3); background: rgba(74,144,217,0.06); }
|
||||
.seasoning-cell--occupied { border-color: rgba(255,255,255,0.12); }
|
||||
/* 待组配:蓝色脉冲动态效果 */
|
||||
/* 待净配:蓝色脉冲动态效果 */
|
||||
.seasoning-cell--pending {
|
||||
border: 2px solid #4a90d9; background: rgba(74,144,217,0.15);
|
||||
box-shadow: 0 0 0 2px rgba(74,144,217,0.3), 0 0 12px rgba(74,144,217,0.4);
|
||||
animation: seaPulse 1.4s ease-in-out infinite;
|
||||
}
|
||||
/* 已组配:绿色,取消高亮 */
|
||||
/* 已净配:绿色,取消高亮 */
|
||||
.seasoning-cell--done {
|
||||
border: 1px solid rgba(82,196,26,0.4); background: rgba(82,196,26,0.08);
|
||||
animation: none;
|
||||
@@ -424,7 +424,7 @@
|
||||
<!-- 顶部导航栏 -->
|
||||
<div class="top-bar">
|
||||
<a class="back-btn" id="backBtn" href="javascript:void(0)">◀ 返回</a>
|
||||
<span class="page-title">组配任务</span>
|
||||
<span class="page-title">净配任务</span>
|
||||
<span class="top-bar__spacer"></span>
|
||||
<div class="user-info">
|
||||
<div class="avatar" id="userAvatar">--</div>
|
||||
@@ -500,15 +500,15 @@
|
||||
<div class="task-card__item-value task-card__item-value--purple" id="taskPackName">--</div>
|
||||
</div>
|
||||
<div class="task-card__item">
|
||||
<div class="task-card__item-label">餐品组配状态</div>
|
||||
<div class="task-card__item-label">餐品净配状态</div>
|
||||
<div class="task-card__item-value" id="taskStatus">--</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主辅材组配区 -->
|
||||
<!-- 主辅材净配区 -->
|
||||
<div class="section-divider">
|
||||
<span class="section-divider__icon">🥜</span>主辅材组配
|
||||
<span class="section-divider__icon">🥜</span>主辅材净配
|
||||
<span class="section-divider__line"></span>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
@@ -517,10 +517,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 调料组配区 -->
|
||||
<!-- 调料净配区 -->
|
||||
<div class="seasoning-section">
|
||||
<div class="section-divider">
|
||||
<span class="section-divider__icon">🧂</span>调料组配
|
||||
<span class="section-divider__icon">🧂</span>调料净配
|
||||
<span class="section-divider__line"></span>
|
||||
<div class="seasoning-toggle seasoning-toggle--on" id="seasoningToggle" onclick="toggleSeasoning()">
|
||||
<span>是否需要</span>
|
||||
@@ -840,7 +840,7 @@
|
||||
});
|
||||
var allDone = ingAllDone && seaAllDone;
|
||||
var statusEl = document.getElementById('taskStatus');
|
||||
statusEl.textContent = allDone ? '已组配' : '待组配';
|
||||
statusEl.textContent = allDone ? '已净配' : '待净配';
|
||||
statusEl.className = 'task-card__item-value' + (allDone ? ' task-card__item-value--green' : ' task-card__item-value--yellow');
|
||||
|
||||
renderItemlist();
|
||||
@@ -877,8 +877,8 @@
|
||||
else if (ing.weighed > 0) actualClass = 'item-card__data-value--progress';
|
||||
|
||||
var statusTag = isDone
|
||||
? '<span class="status-tag status-tag--done">已组配</span>'
|
||||
: '<span class="status-tag status-tag--wait">待组配</span>';
|
||||
? '<span class="status-tag status-tag--done">已净配</span>'
|
||||
: '<span class="status-tag status-tag--wait">待净配</span>';
|
||||
|
||||
row.innerHTML =
|
||||
'<div class="item-card__header">' +
|
||||
@@ -895,7 +895,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* ---- 调料组配区展开/折叠 ---- */
|
||||
/* ---- 调料净配区展开/折叠 ---- */
|
||||
function toggleSeasoning() {
|
||||
var toggle = document.getElementById('seasoningToggle');
|
||||
var grid = document.getElementById('seasoningGrid');
|
||||
@@ -905,26 +905,26 @@
|
||||
thumb.textContent = isOn ? '是' : '否';
|
||||
}
|
||||
|
||||
/* ---- 渲染调料组配网格(3行×5列=15格,左上4格合并为信息格固定显示油,11格为调料位) ---- */
|
||||
/* ---- 渲染调料净配网格(3行×5列=15格,左上4格合并为信息格固定显示油,11格为调料位) ---- */
|
||||
function renderSeasoningGrid() {
|
||||
var grid = document.getElementById('seasoningGrid');
|
||||
grid.innerHTML = '';
|
||||
|
||||
/* 信息格(合并格):固定显示"油"的完整字段(油始终参与组配) */
|
||||
/* 信息格(合并格):固定显示"油"的完整字段(油始终参与净配) */
|
||||
var oilSea = allSeasonings.find(function(s) { return s.cellIdx === 0; });
|
||||
var infoCell = document.createElement('div');
|
||||
infoCell.className = 'seasoning-cell seasoning-cell--info';
|
||||
if (oilSea) {
|
||||
var oilNeed = Math.round(oilSea.weightPerPack * 100) / 100;
|
||||
var oilDone = oilSea.weighed >= oilNeed;
|
||||
/* 待组配→脉冲高亮,已组配→绿色 */
|
||||
/* 待净配→脉冲高亮,已净配→绿色 */
|
||||
if (oilDone) {
|
||||
infoCell.classList.add('seasoning-cell--done');
|
||||
} else {
|
||||
infoCell.classList.add('seasoning-cell--pending');
|
||||
}
|
||||
var oilStatusCls = oilDone ? 'sea-slot__status--done' : 'sea-slot__status--wait';
|
||||
var oilStatusText = oilDone ? '已组配' : '待组配';
|
||||
var oilStatusText = oilDone ? '已净配' : '待净配';
|
||||
var oilActualCls = oilDone ? 'sea-field__value--done' : '';
|
||||
infoCell.innerHTML =
|
||||
'<span class="sea-slot__status ' + oilStatusCls + '">' + oilStatusText + '</span>' +
|
||||
@@ -957,22 +957,22 @@
|
||||
var neededSea = neededSeaMap[pos];
|
||||
|
||||
if (neededSea) {
|
||||
/* 状态1/2:该格调料在本餐品组配清单中 — 待组配或已组配 */
|
||||
/* 状态1/2:该格调料在本餐品净配清单中 — 待净配或已净配 */
|
||||
var seaNeed2 = Math.round(neededSea.weightPerPack * 100) / 100;
|
||||
var seaDone2 = neededSea.weighed >= seaNeed2;
|
||||
|
||||
cell.classList.add('seasoning-cell--occupied');
|
||||
if (seaDone2) {
|
||||
/* 已组配:绿色,取消高亮 */
|
||||
/* 已净配:绿色,取消高亮 */
|
||||
cell.classList.add('seasoning-cell--done');
|
||||
} else {
|
||||
/* 待组配:脉冲高亮 */
|
||||
/* 待净配:脉冲高亮 */
|
||||
cell.classList.add('seasoning-cell--pending');
|
||||
}
|
||||
|
||||
var actualCls2 = seaDone2 ? 'sea-slot__field-value--done' : '';
|
||||
var statusCls = seaDone2 ? 'sea-slot__status--done' : 'sea-slot__status--wait';
|
||||
var statusText = seaDone2 ? '已组配' : '待组配';
|
||||
var statusText = seaDone2 ? '已净配' : '待净配';
|
||||
|
||||
cell.innerHTML =
|
||||
'<span class="sea-slot__pos">' + pos + '</span>' +
|
||||
@@ -984,7 +984,7 @@
|
||||
|
||||
cell.onclick = (function(idx) { return function() { confirmSeasoningDirect(idx); }; })(findSeasoningIdxByPos(pos));
|
||||
} else if (cfg && cfg.name) {
|
||||
/* 状态3:该格放置了调料但本餐品不需要组配 — 字段一致,数据用--占位 */
|
||||
/* 状态3:该格放置了调料但本餐品不需要净配 — 字段一致,数据用--占位 */
|
||||
cell.classList.add('seasoning-cell--occupied');
|
||||
var cfgIcon = SEASONING_ICONS[cfg.name] || '🧂';
|
||||
cell.innerHTML =
|
||||
@@ -1008,13 +1008,13 @@
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ---- 调料组配:直接读秤确认(无弹窗) ---- */
|
||||
/* ---- 调料净配:直接读秤确认(无弹窗) ---- */
|
||||
function confirmSeasoningDirect(idx) {
|
||||
if (idx < 0 || idx >= allSeasonings.length) return;
|
||||
var sea = allSeasonings[idx];
|
||||
var seaNeed = Math.round(sea.weightPerPack * 100) / 100;
|
||||
if (sea.weighed >= seaNeed) {
|
||||
showToast(sea.name + ' 已组配', 1500, 'success');
|
||||
showToast(sea.name + ' 已净配', 1500, 'success');
|
||||
return;
|
||||
}
|
||||
/* 设为当前调料 */
|
||||
@@ -1117,7 +1117,7 @@
|
||||
event.target.value = '';
|
||||
}
|
||||
|
||||
/* ---- 主辅材组配:直接读秤确认(无弹窗) ---- */
|
||||
/* ---- 主辅材净配:直接读秤确认(无弹窗) ---- */
|
||||
function confirmCandidateDirect(result) {
|
||||
/* 直接读取当前秤重赋值 */
|
||||
var scaleWeight = parseFloat(document.getElementById('scaleWeightValue').textContent) || 0;
|
||||
@@ -1125,7 +1125,7 @@
|
||||
ing.weighed = scaleWeight;
|
||||
document.getElementById('scaleWeightValue').textContent = ing.weighed.toFixed(2);
|
||||
document.getElementById('statusDot').className = 'status-dot status-dot--done';
|
||||
document.getElementById('statusText').textContent = '组配确认: ' + result.name + ' — 置信度 ' + result.confidence + '%';
|
||||
document.getElementById('statusText').textContent = '净配确认: ' + result.name + ' — 置信度 ' + result.confidence + '%';
|
||||
showToast('称重完成: ' + result.name + ' ' + scaleWeight.toFixed(2) + '克', 2000, 'success');
|
||||
renderItemlist();
|
||||
updateOverallStatus();
|
||||
@@ -1151,9 +1151,9 @@
|
||||
document.getElementById('cameraOverlay').classList.add('show');
|
||||
} else {
|
||||
document.getElementById('scaleWeightValue').textContent = '0.00';
|
||||
/* 主辅材全完成,提示进行调料组配 */
|
||||
/* 主辅材全完成,提示进行调料净配 */
|
||||
if (currentSeaIdx >= 0) {
|
||||
showToast('主辅材已组配,请点击高亮调料格进行调料组配', 3000, 'success');
|
||||
showToast('主辅材已净配,请点击高亮调料格进行调料净配', 3000, 'success');
|
||||
} else {
|
||||
showToast('所有食材已称重完成', 2500, 'success');
|
||||
}
|
||||
@@ -1171,11 +1171,11 @@
|
||||
});
|
||||
var allDoneNow = ingDone && seaDone;
|
||||
var taskStatusEl = document.getElementById('taskStatus');
|
||||
taskStatusEl.textContent = allDoneNow ? '已组配' : '待组配';
|
||||
taskStatusEl.textContent = allDoneNow ? '已净配' : '待净配';
|
||||
taskStatusEl.className = 'task-card__item-value' + (allDoneNow ? ' task-card__item-value--green' : ' task-card__item-value--yellow');
|
||||
}
|
||||
|
||||
/* ---- 校验组配是否全部完成 ---- */
|
||||
/* ---- 校验净配是否全部完成 ---- */
|
||||
function checkPackAllDone() {
|
||||
var ingDone = allIngredients.every(function(ing) {
|
||||
return ing.weighed >= ing.weightPerPack;
|
||||
@@ -1186,7 +1186,7 @@
|
||||
return ingDone && seaDone;
|
||||
}
|
||||
|
||||
/* ---- 完成并退出:返回组配任务列表 ---- */
|
||||
/* ---- 完成并退出:返回净配任务列表 ---- */
|
||||
function handlePackDoneExit() {
|
||||
if (!checkPackAllDone()) {
|
||||
var msg = [];
|
||||
@@ -1197,13 +1197,13 @@
|
||||
showToast(msg.join('和') + '未完成称重', 2500, 'error');
|
||||
return;
|
||||
}
|
||||
showToast('组配完成,正在返回任务列表...', 1500, 'success');
|
||||
showToast('净配完成,正在返回任务列表...', 1500, 'success');
|
||||
setTimeout(function() {
|
||||
window.location.href = '01-packing-tasks.html' + (userName ? '?user=' + encodeURIComponent(userName) : '');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/* ---- 完成并继续:跳转同订单下一个待组配餐品包 ---- */
|
||||
/* ---- 完成并继续:跳转同订单下一个待净配餐品包 ---- */
|
||||
function handlePackDoneContinue() {
|
||||
if (!checkPackAllDone()) {
|
||||
var msg = [];
|
||||
@@ -1214,29 +1214,29 @@
|
||||
showToast(msg.join('和') + '未完成称重', 2500, 'error');
|
||||
return;
|
||||
}
|
||||
/* 在同订单中查找下一个待组配餐品包 */
|
||||
/* 在同订单中查找下一个待净配餐品包 */
|
||||
var task = taskData[taskNo];
|
||||
var nextPack = null;
|
||||
if (task) {
|
||||
for (var i = 0; i < task.mealPacks.length; i++) {
|
||||
var pk = task.mealPacks[i];
|
||||
if (pk.packName === packName) continue;
|
||||
/* 判断该包是否有待组配项 */
|
||||
/* 判断该包是否有待净配项 */
|
||||
var ingPending = pk.ingredients.some(function(ing) { return ing.weighed < ing.weightPerPack; });
|
||||
var seaPending = pk.seasonings && pk.seasonings.some(function(sea) { return sea.weighed < sea.weightPerPack; });
|
||||
if (ingPending || seaPending) { nextPack = pk; break; }
|
||||
}
|
||||
}
|
||||
if (nextPack) {
|
||||
showToast('组配完成,继续下一餐品包: ' + nextPack.packName, 1500, 'success');
|
||||
showToast('净配完成,继续下一餐品包: ' + nextPack.packName, 1500, 'success');
|
||||
setTimeout(function() {
|
||||
var target = '02-weighing-packing.html?task=' + taskNo + '&order=' + encodeURIComponent(task ? task.orderNo : '') + '&pack=' + encodeURIComponent(nextPack.packName);
|
||||
if (userName) target += '&user=' + encodeURIComponent(userName);
|
||||
window.location.href = target;
|
||||
}, 1000);
|
||||
} else {
|
||||
/* 同订单无更多待组配包,返回任务列表 */
|
||||
showToast('本订单所有餐品包已组配完成,正在返回任务列表...', 1500, 'success');
|
||||
/* 同订单无更多待净配包,返回任务列表 */
|
||||
showToast('本订单所有餐品包已净配完成,正在返回任务列表...', 1500, 'success');
|
||||
setTimeout(function() {
|
||||
window.location.href = '01-packing-tasks.html' + (userName ? '?user=' + encodeURIComponent(userName) : '');
|
||||
}, 1000);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=720, initial-scale=1.0">
|
||||
<title>净菜组配秤 - 组配记录 | 蚁熊智能餐养平台</title>
|
||||
<title>净配秤 - 净配记录 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -293,7 +293,7 @@
|
||||
<div class="device-frame">
|
||||
<div class="top-bar">
|
||||
<a class="back-btn" id="backBtn" href="javascript:void(0)">◀ 返回</a>
|
||||
<span class="page-title">组配记录</span>
|
||||
<span class="page-title">净配记录</span>
|
||||
<span class="top-bar__user" id="topUser">--</span>
|
||||
<button class="logout-btn" onclick="handleLogout()">退出</button>
|
||||
</div>
|
||||
@@ -332,11 +332,11 @@
|
||||
<div class="stats-row">
|
||||
<div class="stat-chip">
|
||||
<div class="stat-chip__icon stat-chip__icon--blue">📋</div>
|
||||
<div><div class="stat-chip__value" id="statTotal">5</div><div class="stat-chip__label" id="statTotalLabel">今日组配订单数</div></div>
|
||||
<div><div class="stat-chip__value" id="statTotal">5</div><div class="stat-chip__label" id="statTotalLabel">今日净配订单数</div></div>
|
||||
</div>
|
||||
<div class="stat-chip">
|
||||
<div class="stat-chip__icon stat-chip__icon--green">⚖️</div>
|
||||
<div><div class="stat-chip__value" id="statWeight">0</div><div class="stat-chip__label">组配总重量(g)</div></div>
|
||||
<div><div class="stat-chip__value" id="statWeight">0</div><div class="stat-chip__label">净配总重量(g)</div></div>
|
||||
</div>
|
||||
<div class="stat-chip">
|
||||
<div class="stat-chip__icon stat-chip__icon--orange">🥬</div>
|
||||
@@ -351,7 +351,7 @@
|
||||
</div>
|
||||
<div class="empty-state" id="emptyState">
|
||||
<div class="empty-state__icon">📭</div>
|
||||
<div>暂无组配记录</div>
|
||||
<div>暂无净配记录</div>
|
||||
</div>
|
||||
<div class="table-footer" id="tableFooter">共 0 条记录</div>
|
||||
</div>
|
||||
@@ -448,7 +448,7 @@
|
||||
var TODAY = '2026-06-16';
|
||||
var WEEKDAYS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
|
||||
// 组配记录数据(packSpec 结构化为对象,与 01 任务页保持一致)
|
||||
// 净配记录数据(packSpec 结构化为对象,与 01 任务页保持一致)
|
||||
var RECORDS = [
|
||||
{
|
||||
orderNo: 'PO-20260616-001', orderType: 'internal', completeTime: '2026-06-16 10:20', taskNo: 'T-001',
|
||||
@@ -629,10 +629,10 @@
|
||||
var monday = getMonday();
|
||||
var mondayStr = dateStr(monday);
|
||||
records = RECORDS.filter(function(r) { return r.completeTime.substring(0, 10) >= mondayStr; });
|
||||
totalLabel = '本周组配订单数';
|
||||
totalLabel = '本周净配订单数';
|
||||
} else {
|
||||
records = RECORDS.filter(function(r) { return r.completeTime.substring(0, 10) === selectedDate; });
|
||||
totalLabel = (selectedDate === TODAY) ? '今日组配订单数' : (selectedDate.substring(5).replace('-', '/') + ' 组配订单数');
|
||||
totalLabel = (selectedDate === TODAY) ? '今日净配订单数' : (selectedDate.substring(5).replace('-', '/') + ' 净配订单数');
|
||||
}
|
||||
|
||||
var total = records.length;
|
||||
@@ -713,10 +713,10 @@
|
||||
return sea.weighed >= sea.weightPerPack * pack.packSize;
|
||||
});
|
||||
if (ingDone && seaDone) return { text: '已完成', cls: 'status-badge--done' };
|
||||
return { text: '待组配', cls: 'status-badge--pending' };
|
||||
return { text: '待净配', cls: 'status-badge--pending' };
|
||||
}
|
||||
|
||||
/* 判断餐品净菜包是否有待组配项(主辅材或调料) */
|
||||
/* 判断餐品净菜包是否有待净配项(主辅材或调料) */
|
||||
function hasPendingPack(pack) {
|
||||
var ingPending = pack.ingredients.some(function(ing) {
|
||||
return ing.weighed < ing.weightPerPack * pack.packSize;
|
||||
@@ -760,7 +760,7 @@
|
||||
'<span class="task-card__order-info"><span class="task-card__order-info__label">订单号:</span><span class="task-card__order-info__value">' + order.orderNo + '</span></span>' +
|
||||
'<span class="order-type-tag ' + orderTypeCls + '">' + orderTypeLabel + '</span>' +
|
||||
'<span class="task-card__arrival">⏰ 任务完成时间:<span class="task-card__arrival__time">' + order.completeTime + '</span></span>' +
|
||||
'<span class="task-card__status"><span class="status-badge ' + (orderAllDone ? 'status-badge--done' : 'status-badge--pending') + '">' + (orderAllDone ? '已完成' : '待组配') + '</span></span>';
|
||||
'<span class="task-card__status"><span class="status-badge ' + (orderAllDone ? 'status-badge--done' : 'status-badge--pending') + '">' + (orderAllDone ? '已完成' : '待净配') + '</span></span>';
|
||||
taskCard.appendChild(header);
|
||||
|
||||
/* 表格头(5列:餐品净菜包名称 / 包装规格 / 应称重量 / 已称重量 / 状态) */
|
||||
@@ -805,7 +805,7 @@
|
||||
'<div style="text-align:center"><span class="pack-spec-tag">' + specText + '</span></div>' +
|
||||
'<div class="parent-weight ' + weightClass + '">' + totalNeed + ' 克</div>' +
|
||||
'<div class="parent-weight ' + weightClass + '">' + totalWeighed + ' 克</div>' +
|
||||
'<div style="text-align:center"><span class="status-tag ' + (packDone ? 'status-tag--done' : 'status-tag--wait') + '">' + (packDone ? '已完成' : '待组配') + '</span></div>';
|
||||
'<div style="text-align:center"><span class="status-tag ' + (packDone ? 'status-tag--done' : 'status-tag--wait') + '">' + (packDone ? '已完成' : '待净配') + '</span></div>';
|
||||
tableBody.appendChild(parentRow);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=720, initial-scale=1.0">
|
||||
<title>净菜组配秤 - 调料位置设置 | 蚁熊智能餐养平台</title>
|
||||
<title>净配秤 - 调料位置设置 | 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
|
||||
@@ -1,69 +1,84 @@
|
||||
# 净菜组配秤 CHANGELOG
|
||||
# 净配秤 CHANGELOG
|
||||
|
||||
## [2.2.4] - 2026-08-07
|
||||
|
||||
### 新增「今日调料需求」一键放置 + 手工配置需求感知
|
||||
- `04-seasoning-settings.html` 新增净菜订单调料需求面板
|
||||
- 顶部栏新增「今日调料需求」按钮,显示待放置调料数量徽标
|
||||
- 展开面板展示当日净菜订单汇总的调料清单(名称 + 所需量)
|
||||
- 「一键放置全部」按钮按顺序逐格分配调料到空格子(位置1→11)
|
||||
- 放置时标签高亮动画 + 已放置标签绿色划掉标记
|
||||
- **手工配置增强**:点击格子选调料时,搜索结果按需求分组
|
||||
- 需求调料置顶 + 黄色高亮 +「📋 今日需求」角标
|
||||
- 非需求调料以分隔线区分,标注「其它调料」
|
||||
- 点击需求项自动回填建议重量
|
||||
- 确认后自动标记已放置,刷新需求面板
|
||||
- 8 种 Mock 调料需求数据(盐/酱油/白糖/料酒/味精/蚝油/胡椒粉/豆瓣酱)
|
||||
|
||||
## [2.2.3] - 2026-07-06
|
||||
|
||||
### 移除份数列 + 调料设置页重构
|
||||
- `01-packing-tasks.html` — 移除"份数"列,grid 7列→6列;移除按 packSize 拆分行逻辑
|
||||
- `02-weighing-packing.html` — 移除"组配份数"字段和行拆分;移除主辅材和调料的"每份"/"每份用量"字段
|
||||
- `02-weighing-packing.html` — 移除"净配份数"字段和行拆分;移除主辅材和调料的"每份"/"每份用量"字段
|
||||
- `03-verify-history.html` — 移除"总份数"统计标签和行拆分,grid 6列→5列
|
||||
- `04-seasoning-settings.html` — 弹窗从下拉框改为搜索框(含搜索结果列表/空状态);油格改为2×2金色信息格,与组配页视觉风格对齐;所有调料弹窗统一体验;移除位置编号;去掉"放置重量"标签
|
||||
- `04-seasoning-settings.html` — 弹窗从下拉框改为搜索框(含搜索结果列表/空状态);油格改为2×2金色信息格,与净配页视觉风格对齐;所有调料弹窗统一体验;移除位置编号;去掉"放置重量"标签
|
||||
|
||||
## [2.2.2] - 2026-06-26
|
||||
|
||||
### 组配任务页按单份拆分 + 组配作业页移除弹窗
|
||||
- `01-packing-tasks.html` — 行展示按单份拆分("第N份/M份"),组配按钮携带份数参数;grid 列宽调整(`1fr 0.55fr 0.35fr 0.55fr 0.55fr 46px 66px`)
|
||||
### 净配任务页按单份拆分 + 净配作业页移除弹窗
|
||||
- `01-packing-tasks.html` — 行展示按单份拆分("第N份/M份"),净配按钮携带份数参数;grid 列宽调整(`1fr 0.55fr 0.35fr 0.55fr 0.55fr 46px 66px`)
|
||||
- `01-packing-tasks.html` — 食材名称简化:去丝/段/片/块/液后缀(土豆丝→土豆、黄瓜片→黄瓜、番茄块→番茄、鸡蛋液→鸡蛋等)
|
||||
- `02-weighing-packing.html` — 移除组配确认弹窗(弹窗 CSS + HTML + JS 全部删除);底部按钮区改为 flex 布局
|
||||
- `02-weighing-packing.html` — 移除净配确认弹窗(弹窗 CSS + HTML + JS 全部删除);底部按钮区改为 flex 布局
|
||||
- `02-weighing-packing.html` — 新增 `next-btn--secondary` 样式(绿色边框按钮)
|
||||
- `03-verify-history.html` — 食材名称同步简化
|
||||
- `doc/PRD.md` — 食材名称同步简化
|
||||
|
||||
## [2.2.1] - 2026-06-26
|
||||
|
||||
### 组配记录页字段与显示逻辑对齐
|
||||
- `03-verify-history.html` — 表头与组配任务页(01)统一:打包份数→份数、所需总重→应称重量、已称→已称重量
|
||||
### 净配记录页字段与显示逻辑对齐
|
||||
- `03-verify-history.html` — 表头与净配任务页(01)统一:打包份数→份数、所需总重→应称重量、已称→已称重量
|
||||
- 行展示改为按单份拆分("第N份/M份"),与01页一致
|
||||
- 状态文案统一:已组配→已完成,新增 `hasPendingPack` 判断函数
|
||||
- 状态文案统一:已净配→已完成,新增 `hasPendingPack` 判断函数
|
||||
- grid 列宽对齐01页,新增 `.parent-weight--done` 绿色完成样式
|
||||
- 数据层补充 `taskNo` 字段
|
||||
|
||||
## [2.2.0] - 2026-06-25
|
||||
|
||||
### 组配作业页新增净菜类型
|
||||
- `02-weighing-packing.html` — 食材行数据行新增"净菜类型"项(尺寸·形状·刀工,如 `20mm*1mm*1mm · 细丝 · 平刀`),与净菜加工秤加工规格格式一致
|
||||
### 净配作业页新增净菜类型
|
||||
- `02-weighing-packing.html` — 食材行数据行新增"净菜类型"项(尺寸·形状·刀工,如 `20mm*1mm*1mm · 细丝 · 平刀`),与厨配秤加工规格格式一致
|
||||
- 净菜类食材(丝/片/段)显示实际切法规格;菜品类食材(番茄炒蛋/凉拌黄瓜等)显示"—"
|
||||
- 数据层 ingredients 增加 `cutSpec` 字段,扁平化时传递
|
||||
|
||||
## [2.1.0] - 2026-06-25
|
||||
|
||||
### 组配记录页对齐组配任务页
|
||||
### 净配记录页对齐净配任务页
|
||||
- **表头6列**:餐品净菜包名称 / 包装规格 / 打包份数 / 所需总重 / 已称 / 状态(去掉"净菜/餐品"列和操作列)
|
||||
- **数据结构一致**:只含餐品净菜包,每个包含 ingredients + seasonings,与01页镜像
|
||||
- **所需总重含调料**:主辅材需求合计 + 调料需求合计
|
||||
- **状态计算一致**:主辅材+调料全部完成才算"已组配",文案统一为待组配/已组配
|
||||
- **统计行含调料重量**:组配总重量包含调料已称重量
|
||||
- **状态计算一致**:主辅材+调料全部完成才算"已净配",文案统一为待净配/已净配
|
||||
- **统计行含调料重量**:净配总重量包含调料已称重量
|
||||
|
||||
## [2.0.0] - 2026-06-25
|
||||
|
||||
### 屏幕比例改造
|
||||
- **全页面纵向 3:4**:所有 7 个页面从横向 1280×720(16:9)改为纵向 720×960(3:4),viewport 同步更新
|
||||
- **02 组配详情页布局重构**:从横向两列(左识别+右清单)改为纵向上下(上区识别+候选左右排列,下区任务+清单+调料+按钮);body 统一为 PAD 外壳模式
|
||||
- **03 组配记录页**:统一为 PAD 外壳模式(深色画布+居中 device-frame)
|
||||
- **02 净配详情页布局重构**:从横向两列(左识别+右清单)改为纵向上下(上区识别+候选左右排列,下区任务+清单+调料+按钮);body 统一为 PAD 外壳模式
|
||||
- **03 净配记录页**:统一为 PAD 外壳模式(深色画布+居中 device-frame)
|
||||
|
||||
### 重构
|
||||
- **全局命名**:硬件名称由「净菜配比秤」改为「净菜组配秤」,所有「配比」文案统一改为「组配」(组配任务/组配记录/去组配/组配完成/组配确认/待组配)
|
||||
- **任务列表页(01)**:移除「净菜包」订单,仅展示「餐品净菜包」订单;移除订单级「去组配」按钮,改为每个餐品净菜包主行独立「组配」按钮;表头精简(去掉「净菜/餐品」列,新增「操作」列)
|
||||
- **组配详情页(02)**:任务信息卡新增「餐品名称」字段;移除清单中「餐品净菜包」名称元信息行;清单拆为「主辅材组配」+「调料组配」两区
|
||||
- **调料组配区(02)**:3行×5列=15格网格,左上角第一列+第二列的第1、2行4格合并为信息格(固定显示「油」的完整字段:名称/每份用量/份数/应称/已称),其余11格为调料位置格,每个调料格字段与主辅材一致(名称/每份用量/份数/应称/已称)
|
||||
- **全局命名**:硬件名称由「净菜配比秤」改为「净配秤」,所有「配比」文案统一改为「净配」(净配任务/净配记录/去净配/净配完成/净配确认/待净配)
|
||||
- **任务列表页(01)**:移除「净菜包」订单,仅展示「餐品净菜包」订单;移除订单级「去净配」按钮,改为每个餐品净菜包主行独立「净配」按钮;表头精简(去掉「净菜/餐品」列,新增「操作」列)
|
||||
- **净配详情页(02)**:任务信息卡新增「餐品名称」字段;移除清单中「餐品净菜包」名称元信息行;清单拆为「主辅材净配」+「调料净配」两区
|
||||
- **调料净配区(02)**:3行×5列=15格网格,左上角第一列+第二列的第1、2行4格合并为信息格(固定显示「油」的完整字段:名称/每份用量/份数/应称/已称),其余11格为调料位置格,每个调料格字段与主辅材一致(名称/每份用量/份数/应称/已称)
|
||||
- **调料多高亮状态(02)**:active(当前要称,蓝色脉冲)、next(下一个要称,淡蓝虚线)、done(已完成,绿色),称重确认后自动从 active 切换为 done
|
||||
- **调料组配交互(02)**:不走 AI 识别,点击高亮调料格 → 弹出组配确认弹窗 → 读取秤值 → 确认写入,信息格(油)也可点击称重
|
||||
- **状态判断(02)**:主辅材+调料全部完成才算该餐品净菜包组配完成
|
||||
- **调料净配交互(02)**:不走 AI 识别,点击高亮调料格 → 弹出净配确认弹窗 → 读取秤值 → 确认写入,信息格(油)也可点击称重
|
||||
- **状态判断(02)**:主辅材+调料全部完成才算该餐品净菜包净配完成
|
||||
|
||||
### 新增
|
||||
- **调料位置设置页(04-seasoning-settings.html)**:3×5 网格可视化配置调料秤 11 个位置各自放置的调料,点击格子 → 下拉选择调料名称(17种常用调料候选),支持清空/重置/保存(localStorage 持久化)
|
||||
- **功能菜单(00-welcome.html)**:新增「调料位置设置」入口
|
||||
- **导航页(index.html)**:新增「调料位置设置」卡片入口
|
||||
- **canteen-device/index.html**:卡片名称改为「净菜组配秤」,描述更新为「餐品净菜包独立组配,主辅材+调料双区称重」
|
||||
- **canteen-device/index.html**:卡片名称改为「净配秤」,描述更新为「餐品净菜包独立净配,主辅材+调料双区称重」
|
||||
|
||||
---
|
||||
|
||||
@@ -91,18 +106,18 @@
|
||||
|
||||
### 重构
|
||||
- **数据结构扁平化**:任务由"一个订单一个餐品净菜包"改为"一个订单多个净菜包/餐品净菜包"(mealPacks 数组),支持混合打包模式
|
||||
- **任务列表页(01)**:表格从6列扩展为8列,新增「净菜/餐品」和「包装规格」列;去配比不再携带 packName 参数;订单状态由所有包完成才算完成
|
||||
- **任务列表页(01)**:表格从6列扩展为8列,新增「净菜/餐品」和「包装规格」列;去净配不再携带 packName 参数;订单状态由所有包完成才算完成
|
||||
- **称重打包页(02)**:食材清单从表格布局改为卡片布局,每张卡片展示名称、状态、餐品净菜包归属、包装规格、用量/份数/需/已称
|
||||
- **配比确认弹窗**:AI 识别后不再自动写入重量,改为弹窗展示食材图标、名称、当前秤重,支持刷新读取,需人工点选候选食材并确认
|
||||
- **净配确认弹窗**:AI 识别后不再自动写入重量,改为弹窗展示食材图标、名称、当前秤重,支持刷新读取,需人工点选候选食材并确认
|
||||
- **候选列表排序**:AI 识别候选按置信度降序排列(不再随机)
|
||||
|
||||
### 新增
|
||||
- 02 页顶部栏新增「退出登录」按钮
|
||||
- 02 页任务信息卡新增「订单状态」字段(待配比/已完成)
|
||||
- 02 页候选识别区点选食材后弹出配比确认弹窗
|
||||
- 02 页任务信息卡新增「订单状态」字段(待净配/已完成)
|
||||
- 02 页候选识别区点选食材后弹出净配确认弹窗
|
||||
|
||||
### 删除
|
||||
- 02 页移除包装信息弹窗(包装方式/保质期/存放温度/终端设备),统一改为配比确认弹窗
|
||||
- 02 页移除包装信息弹窗(包装方式/保质期/存放温度/终端设备),统一改为净配确认弹窗
|
||||
- 02 页顶部栏移除 packTag 展示
|
||||
|
||||
---
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 净菜配比秤 — 产品需求文档
|
||||
# 净配秤 — 产品需求文档
|
||||
|
||||
> 原型目录:`other-module/canteen-device/ratio-scale/`
|
||||
> 创建时间:2026-06-13 | 最后更新:2026-06-16
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
## 一、模块概述
|
||||
|
||||
净菜配比秤是食堂净菜打包流程中的核心硬件终端,承担打包任务管理、净菜识别称重、餐品组合打包三大职能。设备配备触摸屏、称重传感器、食材识别摄像头和人脸识别摄像头,支持「净菜包打包」和「餐品组合打包」两种业务模式。
|
||||
净配秤是食堂净菜打包流程中的核心硬件终端,承担打包任务管理、净菜识别称重、餐品组合打包三大职能。设备配备触摸屏、称重传感器、食材识别摄像头和人脸识别摄像头,支持「净菜包打包」和「餐品组合打包」两种业务模式。
|
||||
|
||||
### 业务流程
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
| 来源页 | 触发操作 | 目标页 | 携带参数 |
|
||||
|--------|----------|--------|----------|
|
||||
| 根导航(index.html) | 进入净菜配比秤 | 00 登录页 | — |
|
||||
| 根导航(index.html) | 进入净配秤 | 00 登录页 | — |
|
||||
| 00 登录页 | 登录成功 | 00 功能菜单页 | `?user=姓名&uid=工号` |
|
||||
| 00 功能菜单页 | 点击「今日任务」 | 01 任务列表页 | `?user=...&uid=...` |
|
||||
| 00 功能菜单页 | 点击「称重打包」 | 02 称重打包页 | `?user=...&uid=...` |
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=720, initial-scale=1.0">
|
||||
<title>净菜组配秤 - 蚁熊智能餐养平台</title>
|
||||
<title>净配秤 - 蚁熊智能餐养平台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -41,7 +41,7 @@
|
||||
<div class="device-frame">
|
||||
<div class="top-bar">
|
||||
<a class="back-link" href="../index.html">◀ 返回</a>
|
||||
<span class="top-bar__title">净菜组配秤</span>
|
||||
<span class="top-bar__title">净配秤</span>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<a class="card" href="00-login.html">
|
||||
@@ -56,23 +56,23 @@
|
||||
<span class="card__icon">📋</span>
|
||||
<div class="card__info">
|
||||
<div class="card__title">功能菜单</div>
|
||||
<div class="card__desc">组配任务入口</div>
|
||||
<div class="card__desc">净配任务入口</div>
|
||||
</div>
|
||||
<span class="card__arrow">›</span>
|
||||
</a>
|
||||
<a class="card" href="01-packing-tasks.html">
|
||||
<span class="card__icon">📦</span>
|
||||
<div class="card__info">
|
||||
<div class="card__title">组配任务</div>
|
||||
<div class="card__desc">餐品净菜包独立组配,主辅材+调料双区</div>
|
||||
<div class="card__title">净配任务</div>
|
||||
<div class="card__desc">餐品净菜包独立净配,主辅材+调料双区</div>
|
||||
</div>
|
||||
<span class="card__arrow">›</span>
|
||||
</a>
|
||||
<a class="card" href="03-verify-history.html">
|
||||
<span class="card__icon">📝</span>
|
||||
<div class="card__info">
|
||||
<div class="card__title">组配记录</div>
|
||||
<div class="card__desc">历史组配记录查看与筛选</div>
|
||||
<div class="card__title">净配记录</div>
|
||||
<div class="card__desc">历史净配记录查看与筛选</div>
|
||||
</div>
|
||||
<span class="card__arrow">›</span>
|
||||
</a>
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 6. 净菜组配 -->
|
||||
<!-- 6. 净配 -->
|
||||
<div class="tl-node">
|
||||
<div class="tl-left">
|
||||
<div class="tl-dot" style="border-color:#eb2f96;background:#eb2f961a">🔪</div>
|
||||
@@ -159,8 +159,8 @@
|
||||
</div>
|
||||
<div class="tl-right">
|
||||
<div class="tl-card">
|
||||
<div style="display:flex;align-items:center;gap:5px;margin-bottom:3px"><span class="tl-stage" style="color:#eb2f96">🔪 净菜组配</span><span class="tl-ok">✅ 正常</span></div>
|
||||
<div class="tl-title">组配任务分支 · 分配至多道菜品</div>
|
||||
<div style="display:flex;align-items:center;gap:5px;margin-bottom:3px"><span class="tl-stage" style="color:#eb2f96">🔪 净配</span><span class="tl-ok">✅ 正常</span></div>
|
||||
<div class="tl-title">净配任务分支 · 分配至多道菜品</div>
|
||||
<div class="tl-time">⏱ 2026-04-22 10:20 ~ 10:40</div>
|
||||
<div class="tl-detail">分支A:CMB-001 宫保鸡丁 5.0kg · 分支B:CMB-006 辣子鸡 3.5kg</div>
|
||||
<div class="tl-code">CMB-20260422-001</div>
|
||||
|
||||
@@ -98,6 +98,10 @@ tr:hover td{background:#FAFAFA;}
|
||||
.form-row-inline{display:flex;gap:16px;align-items:flex-start;}
|
||||
.form-row-inline .form-row{flex:1;}
|
||||
.form-row-inline .form-row.status-row{flex:0 0 140px;}
|
||||
/* 用户范围复选 */
|
||||
.checkbox-group{display:flex;flex-wrap:wrap;gap:8px 20px;padding:8px 12px;background:#FAFAFA;border:1px solid var(--border);border-radius:var(--radius-sm);}
|
||||
.checkbox-item{display:inline-flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;user-select:none;}
|
||||
.checkbox-item input[type="checkbox"]{width:14px;height:14px;cursor:pointer;accent-color:var(--primary);}
|
||||
/* 分区标题 */
|
||||
.form-section-title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);margin:20px 0 12px;padding-bottom:8px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;}
|
||||
.form-section-title:first-child{margin-top:0;}
|
||||
@@ -318,6 +322,17 @@ tr:hover td{background:#FAFAFA;}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label class="form-label"><em>*</em> 用户范围</label>
|
||||
<div class="checkbox-group" id="lineUserScope">
|
||||
<label class="checkbox-item"><input type="checkbox" value="staff"> 员工</label>
|
||||
<label class="checkbox-item"><input type="checkbox" value="member"> 会员用户</label>
|
||||
<label class="checkbox-item"><input type="checkbox" value="normal"> 普通用户</label>
|
||||
<label class="checkbox-item"><input type="checkbox" value="worker"> 工作人员</label>
|
||||
<label class="checkbox-item"><input type="checkbox" value="external"> 外部人员</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 收费模式 -->
|
||||
<div class="form-section-title">
|
||||
<span>收费模式</span>
|
||||
@@ -380,6 +395,8 @@ var MODE_OPTIONS=[
|
||||
{value:'byWeight',label:'称重计费'},
|
||||
{value:'free',label:'免费'}
|
||||
];
|
||||
/* 用户类型选项(用户范围复选) */
|
||||
var ALL_USER_TYPES=['staff','member','normal','worker','external'];
|
||||
/* 生成收费模式矩阵:7天×4餐次,同一模式 */
|
||||
function genCharging(mode, prices){
|
||||
return WEEKDAYS.map(function(){
|
||||
@@ -393,6 +410,7 @@ function genCharging(mode, prices){
|
||||
var lines=[
|
||||
{
|
||||
id:'L1', name:'A餐线(自选餐)', settleType:'auto', status:'on',
|
||||
userScope:['staff','member','normal','worker','external'],
|
||||
charging: genCharging('byMeal', [8,15,15,6]),
|
||||
devices:[
|
||||
{type:'营养秤', code:'CL-A01', name:'餐线营养秤1号', days:326, online:true},
|
||||
@@ -405,6 +423,7 @@ var lines=[
|
||||
},
|
||||
{
|
||||
id:'L2', name:'B餐线(营养点餐)', settleType:'auto', status:'on',
|
||||
userScope:['staff','member'],
|
||||
charging: genCharging('byWeight'),
|
||||
devices:[
|
||||
{type:'营养秤', code:'CL-B01', name:'餐线营养秤4号', days:400, online:true},
|
||||
@@ -415,6 +434,7 @@ var lines=[
|
||||
},
|
||||
{
|
||||
id:'L3', name:'C餐线(清真)', settleType:'manual', status:'off',
|
||||
userScope:['external','worker'],
|
||||
charging: genCharging('free'),
|
||||
devices:[
|
||||
{type:'营养秤', code:'CL-C01', name:'餐线营养秤6号', days:90, online:true},
|
||||
@@ -479,12 +499,14 @@ function openLineModal(id){
|
||||
settle.value=l.settleType;
|
||||
tempStatus=l.status;
|
||||
tempCharging=JSON.parse(JSON.stringify(l.charging));
|
||||
setUserScope(l.userScope||ALL_USER_TYPES);
|
||||
}else{
|
||||
title.textContent='新增餐线';
|
||||
nameInput.value='';
|
||||
settle.value='auto';
|
||||
tempStatus='on';
|
||||
tempCharging=genCharging('free');
|
||||
setUserScope(ALL_USER_TYPES);
|
||||
}
|
||||
renderStatusSwitch();
|
||||
renderChargeTable();
|
||||
@@ -504,6 +526,20 @@ function toggleLineStatusSwitch(){
|
||||
tempStatus=tempStatus==='on'?'off':'on';
|
||||
renderStatusSwitch();
|
||||
}
|
||||
/* 用户范围复选:回填勾选状态 */
|
||||
function setUserScope(arr){
|
||||
document.querySelectorAll('#lineUserScope input[type="checkbox"]').forEach(function(cb){
|
||||
cb.checked=(arr||[]).indexOf(cb.value)>-1;
|
||||
});
|
||||
}
|
||||
/* 用户范围复选:读取当前勾选值 */
|
||||
function getUserScope(){
|
||||
var arr=[];
|
||||
document.querySelectorAll('#lineUserScope input[type="checkbox"]:checked').forEach(function(cb){
|
||||
arr.push(cb.value);
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
/* ========== 收费模式矩阵 ========== */
|
||||
function renderChargeTable(){
|
||||
@@ -577,7 +613,9 @@ function applyBatch(){
|
||||
function confirmLine(){
|
||||
var name=document.getElementById('lineName').value.trim();
|
||||
var settle=document.getElementById('lineSettle').value;
|
||||
var userScope=getUserScope();
|
||||
if(!name){ alert('请输入餐线名称'); return; }
|
||||
if(userScope.length===0){ alert('请选择用户范围'); return; }
|
||||
for(var w=0;w<7;w++){
|
||||
for(var m=0;m<4;m++){
|
||||
var c=tempCharging[w][m];
|
||||
@@ -588,12 +626,14 @@ function confirmLine(){
|
||||
var l=lines.find(function(x){return x.id===editingLineId;});
|
||||
if(l){
|
||||
l.name=name; l.settleType=settle; l.status=tempStatus;
|
||||
l.userScope=userScope;
|
||||
l.charging=JSON.parse(JSON.stringify(tempCharging));
|
||||
}
|
||||
}else{
|
||||
lines.push({
|
||||
id:'L'+Date.now(),
|
||||
name:name, settleType:settle, status:tempStatus,
|
||||
userScope:userScope,
|
||||
charging:JSON.parse(JSON.stringify(tempCharging)),
|
||||
devices:[]
|
||||
});
|
||||
|
||||
@@ -982,7 +982,7 @@ function getVal(id){ var el = document.getElementById(id); return el ? el.value.
|
||||
function getNum(id){ var v = getVal(id); if(v==='') return null; var n = parseFloat(v); return isNaN(n) ? null : n; }
|
||||
|
||||
/* ========== 跨页 specs 共享(阶段三任务 3.0) ========== */
|
||||
/* 把 DISHES 数组序列化到 localStorage,供下游订单/组配/包装等页面读取 */
|
||||
/* 把 DISHES 数组序列化到 localStorage,供下游订单/净配/包装等页面读取 */
|
||||
function persistDishSpecs(){
|
||||
try {
|
||||
var simplified = DISHES.map(function(d){
|
||||
|
||||
@@ -483,7 +483,7 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<div class="form-grid">
|
||||
<div class="form-item"><label>设备编号<span class="required">*</span></label><input type="text" placeholder="请输入设备编号"></div>
|
||||
<div class="form-item"><label>设备名称<span class="required">*</span></label><input type="text" placeholder="请输入设备名称"></div>
|
||||
<div class="form-item"><label>设备类型<span class="required">*</span></label><select><option>请选择</option><option>智能取餐柜</option><option>点餐屏</option><option>配比秤</option><option>餐线秤台</option><option>摄像头</option><option>智能电表</option><option>智能水表</option><option>智能气表</option><option>体脂秤</option></select></div>
|
||||
<div class="form-item"><label>设备类型<span class="required">*</span></label><select><option>请选择</option><option>智能取餐柜</option><option>点餐屏</option><option>净配秤</option><option>餐线秤台</option><option>摄像头</option><option>智能电表</option><option>智能水表</option><option>智能气表</option><option>体脂秤</option></select></div>
|
||||
<div class="form-item"><label>所属餐线</label><select><option value="">请选择</option><option>1号餐线</option><option>2号餐线</option><option>3号餐线</option><option>4号餐线</option></select></div>
|
||||
<div class="form-item"><label>厂家</label><input type="text" placeholder="请输入厂家"></div>
|
||||
<div class="form-item"><label>型号</label><input type="text" placeholder="请输入型号"></div>
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 营养用餐管理端 CHANGELOG
|
||||
|
||||
## [2026-08-25] feat: 餐线设置新增用户范围必填复选 + 食谱订单餐品明细表份数调整
|
||||
|
||||
- `din-canteen-line.html` 餐线管理:
|
||||
- 餐线设置/新增餐线弹窗「基础信息」下新增「用户范围」必填复选(用户类型多选:员工/会员用户/普通用户/工作人员/外部人员)
|
||||
- 新增 checkbox-group/checkbox-item 样式,支持编辑回填与新增默认全选,保存时校验至少勾选一项
|
||||
- `prod-clean-order.html` 净菜订单:
|
||||
- 食谱快速创建订单弹窗「餐品明细」表:移除「包装规格」列(克数+单位下拉),改为「份数」列(固定单位「份」,输入框编辑份数)
|
||||
- 提示语追加最大包装克重说明(默认4kg/份,超限按最大包装反算份数),两段说明之间空一行
|
||||
- 份数存储 recipeSpecMap 改为 recipeServingMap,提交订单固定单位「份」并回退就餐人数作为份数
|
||||
|
||||
## [2026-08-24] feat: 任务中心页重构(统计卡片/分段Tab/动态筛选) + 仓库页新增净菜包柜
|
||||
|
||||
- `sup-task-center.html` 任务中心重构:
|
||||
@@ -486,7 +496,7 @@ logistics: {
|
||||
- **4.2b 展示层兼容**:内联 `formatGram` 和 `formatGramForCanteen`,展示层自动还原"斤"显示(如 2000g → "4 斤")。修复 04-history 中 `formatGramForCanteen` 的语法错误(`g === null === ''` → `g === null || g === ''`)。
|
||||
- **4.2c yieldRate 字段**:03-batch-washing 的 `aiResults` 6 项 + `foodDB` 12 项新增 `yieldRate`(85~95),UI 列展示暂未添加(允许后续从食材档案带出)。
|
||||
|
||||
### 设备端 · 净菜加工秤(任务 4.3)
|
||||
### 设备端 · 厨配秤(任务 4.3)
|
||||
|
||||
- **`clean-veg-ratio-scale/03-settings.html`**
|
||||
- 删除"重量单位" select(与 4.2 存储统一为克冲突),替换为说明 hint(`formatGramForCanteen` 自动还原"斤"显示)。
|
||||
@@ -502,7 +512,7 @@ logistics: {
|
||||
- **渲染逻辑**:直接展示的字符串改为 `formatSpec(specObj)` / `formatCutSpec(cutObj)`;为兼容多形态,加入 `typeof === 'object'` 判断并回退到 `parseLegacyPackSpec` / `parseLegacyCutSpec`。
|
||||
- **决策**:用户确认"真空包装"是包装方式(packMethod)而非包装规格(packSpec),不影响改造逻辑。
|
||||
|
||||
### 设备端 · 净菜组配秤(任务 4.5,3 个文件)
|
||||
### 设备端 · 净配秤(任务 4.5,3 个文件)
|
||||
|
||||
- **`ratio-scale/01-packing-tasks.html`**、**`02-weighing-packing.html`**、**`03-verify-history.html`**
|
||||
- **工具函数内联**:3 文件各自内联 5 个工具函数。
|
||||
@@ -606,7 +616,7 @@ logistics: {
|
||||
- **`sup-clean-internal.html`**(净菜内供):数据模型重构为主子表结构。表格主表展示申领单号/申领场所/菜品数/总份数/总数量/申领单状态/申领时间/预定配送时间/配送人,展开显示各菜品明细(净菜类型/菜品食材/包装规格/份数/数量/行状态)。新建申领单弹窗重构为动态明细行构建器,支持多菜品添加与汇总预览。详情抽屉展示申领单汇总+明细列表。JS 数据模型统一改为主子表。
|
||||
- **`farm-purchase.html`**(采购管理):采购明细子表新增「验收数量」列,所有示例数据行补充验收数量字段。确认收货弹窗从简单确认框重构为验收明细弹窗——展示食材名称/规格/采购数量/验收数量/备注表格,逐项填写验收数量后方可提交(含全覆盖校验)。已完成的采购单自动预填验收数量。详情抽屉表格同步新增验收数量列。
|
||||
- **`prod-proc-cabinet.html`**(净菜进柜):表头新增「订单菜品」列(区分于原「存放净菜」列语义),数据行同步适配(如土豆行拆分为订单菜品=土豆丝 / 存放净菜=土豆(土豆丝))。空闲行补齐新增列占位。进柜弹窗新增「选择订单菜品」联动回显区域。
|
||||
- **`prod-proc-combo.html`**(净菜组配):表头新增「关联订单」列,数据行关联订单号 CO-001~CO-004,同一订单的多条组配任务以 `order-last` 样式标记末行。任务来源字段从下拉选择改为 tag 展示(餐线/点餐/录入)。
|
||||
- **`prod-proc-combo.html`**(净配):表头新增「关联订单」列,数据行关联订单号 CO-001~CO-004,同一订单的多条净配任务以 `order-last` 样式标记末行。任务来源字段从下拉选择改为 tag 展示(餐线/点餐/录入)。
|
||||
- **`prod-proc-cook.html`**(按单烹制):表头新增「关联订单」列,数据行关联订单号并展示该订单下的烹制进度汇总(✓ 已烹齐 / 烹制中 n/m / 待烹制 n/m)。新增 `updateOrderBadge()` / `showCookOrderDetail()` JS 函数支持订单进度动态更新。
|
||||
- **`sup-in-clean.html`**(净菜加工):表头移除「质检结果」列,所有数据行同步移除质检结果字段。新建/编辑弹窗移除质检结果下拉选择。
|
||||
|
||||
@@ -928,12 +938,12 @@ logistics: {
|
||||
|
||||
### 增强 — 健康生产模块交互重构与食堂切换
|
||||
|
||||
- **`prod-proc-combo.html`** 净菜组配
|
||||
- **`prod-proc-combo.html`** 净配
|
||||
- 筛选栏新增「当前食堂」「任务来源」「任务截止日期」筛选,截止日期默认当天
|
||||
- 表头重构:去掉需求规格列,新增餐品规格·份数/任务截止时间/任务来源/完成时间列
|
||||
- 食材配方列移至任务截止时间后,「查看配方」改为「食材明细」
|
||||
- 操作列重构:固定三项(食材纯净源链接·查看视频·更多⌄ hover 下拉)+ 状态按钮
|
||||
- 新建组配任务弹窗重构:改用下拉选择餐品/规格/份数/来源,移除手动食材清单
|
||||
- 新建净配任务弹窗重构:改用下拉选择餐品/规格/份数/来源,移除手动食材清单
|
||||
- 食材明细弹窗新增「分类」列(主料·辅料/调料 标识)
|
||||
- overflow-y 改为 visible 解决下拉菜单遮挡问题
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ function calcServableCount(packCount, packBaseGram, recipeGram) {
|
||||
| `parseLegacyCutSpec` | 历史字符串解析(迁移用) | clean-veg-packaging-labeler(01-orders / 02-labeling / label-preview) |
|
||||
| `ceilDiv` | 向上取整除法 | 所有规格换算场景 |
|
||||
| `calcTotalGram` | 计算规格总克重 | 采购/订单/包装等 |
|
||||
| `convertSpec` | 跨单位换算 | 入库/订单/组配 |
|
||||
| `convertSpec` | 跨单位换算 | 入库/订单/净配 |
|
||||
| `formatGramForCanteen` | 验收秤"斤"显示兼容 | raw-veg-inspection-scale / raw-veg-processing-scale |
|
||||
| `calcNetWeight` | L1→L2 扣出净率 | sup-in-clean / prod-proc-cook |
|
||||
| `calcPurchaseDemand` | L2→L1 反推采购需求 | sup-in-clean(反向推导) |
|
||||
|
||||
@@ -150,14 +150,14 @@
|
||||
|
||||
#### 9.3 健康生产
|
||||
|
||||
##### 1. prod-proc-combo.html(净菜组配)
|
||||
##### 1. prod-proc-combo.html(净配)
|
||||
- 筛选栏新增「当前食堂」「任务来源」「任务截止日期」筛选,截止日期默认当天
|
||||
- 表头重构:去掉需求规格列,新增餐品规格/份数/任务截止时间/任务来源/完成时间列
|
||||
- 食材配方列移至任务截止时间后,「查看配方」改为「食材明细」
|
||||
- 操作列重构:固定三项(食材溯源链路、查看视频、更多▾ hover 下拉)+ 状态按钮
|
||||
- 新建组配任务弹窗重构:改用下拉选择餐品/规格/份数/来源,移除手动食材清单
|
||||
- 新建净配任务弹窗重构:改用下拉选择餐品/规格/份数/来源,移除手动食材清单
|
||||
- 食材明细弹窗新增「分类」列(主材/辅材/调料 标签)
|
||||
- 「开始组配」「完成组配」按钮新增顶部 toast 提示交互(状态联动切换)
|
||||
- 「开始净配」「完成净配」按钮新增顶部 toast 提示交互(状态联动切换)
|
||||
- 新增「操作类型」列(负责人与完成时间之间),值为"设备自动"(蓝色tag)/"人工录入"(绿色tag)
|
||||
- 新建任务弹窗补充「操作类型」必填下拉字段
|
||||
- 新增右侧抽屉详情组件,下拉菜单"详情"以抽屉形式展示任务各字段
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|--------|---|-----------------------------------------|---|
|
||||
| 云端主服务 | 全局权威中心 | 主数据维护、任务生成、策略配置、跨食堂统计、异常监管、最终数据归档落库 | 直接访问食堂内网设备、依赖实时网络完成现场作业 |
|
||||
| 边缘服务器 | 食堂现场业务连续性保障节点 | 本地缓存、终端接入、本地写入、上行补传、下行应用、文件缓存、设备状态、异常告警 | 全局主数据最终编辑、跨食堂审批、长期历史分析、大模型推理 |
|
||||
| 现场终端设备 | 业务执行和采集终端 | 称重、菜品识别、取餐、入库、食材加工、结算、组配、烹制、上餐、打印、日志采集 | 全局业务规则判断、跨食堂数据查询、云端统计分析 |
|
||||
| 现场终端设备 | 业务执行和采集终端 | 称重、菜品识别、取餐、入库、食材加工、结算、净配、烹制、上餐、打印、日志采集 | 全局业务规则判断、跨食堂数据查询、云端统计分析 |
|
||||
|
||||
## 5. 功能清单
|
||||
|
||||
@@ -97,10 +97,10 @@
|
||||
| 毛菜入库 | 入库单、采购单、供应商、食材档案 | 1. 采购单、供应商、食材档案等信息已在其他模块中明确了下发规则。 | 称重记录、入库批次、质检状态、现场照片、操作员、设备编号 | 1. 验收称重完成后实时增量上报。<br>2. 入库批次、称重记录、质检状态按同一业务唯一键上报。 |
|
||||
| 净菜加工 | 加工任务、净菜类型、加工规格 | 1. 加工任务生成后实时下发。<br>2. 净菜类型已在食物成分模块明确下发规则。 | 毛菜加工、净菜加工、毛重、净重、出净率 | 1. 从毛菜加工开始、称重、完成节点分别写入边缘节点本地库。<br>2. 加工完成后汇总毛重、净重、出净率和损耗实时上报至云端服务。<br>3. 过程图片或者视频按文件队列异步补传。 |
|
||||
| 净菜入库 | 人工录入的入库信息、入库信息在后台被修改 | 1.后台人工录入的净菜入库信息需要实时下发至边缘节点服务器。<br>2. 入库信息在后台被手动编辑、删除、出库等信息需实时下发。 | 入库记录、库位绑定、入库重量 | 1. 入库完成后实时增量上报。 |
|
||||
| 净菜包装 | 包装任务、包装规格、溯源码规则 | 1. 包装任务生成后实时下发。。<br>2. 包装任务变更后增量下发。 | 包装批次、包装重量、组配信息、设备信息 | 1. 包装、称重、打标节点单菜品包装完成先记录与边缘服务器本地库。<br>2. 整个订单包装完成后实时上报云端。 |
|
||||
| 净菜包装 | 包装任务、包装规格、溯源码规则 | 1. 包装任务生成后实时下发。。<br>2. 包装任务变更后增量下发。 | 包装批次、包装重量、净配信息、设备信息 | 1. 包装、称重、打标节点单菜品包装完成先记录与边缘服务器本地库。<br>2. 整个订单包装完成后实时上报云端。 |
|
||||
| 净菜内供、净菜外售 | 内供订单、外售订单 | 1. 内供或内功订单产生后实时下发。<br>2. 配送计划变更后实时补发。<br>3. 订单状态变更后实时下发(如取消订单)。 | 暂无 | 暂无 |
|
||||
| 净菜进柜 | 暂无 | 暂不考虑后台手动添加的进柜记录同步至边缘节点,理论上进柜记录都是从设备端主动发起 | 进柜记录、格口占用、取放记录、柜体异常、重量变更 | 1. 进柜、取出等操作实时上报。<br>2. 格口占用变化需实时上报。。 |
|
||||
| 净菜组配 | 组配任务、餐品规格、份数、食材明细、标准用量、差异阈值 | 1. 组配任务发布后实时下发。<br>2. 餐品规格和标准用量按餐品版本下发(餐品总库)。 | 任务状态、称重明细、实际用量、设备编号、操作员、完成状态 | 1. 任务完成后汇总称重明细和完成状态上报。 |
|
||||
| 净配 | 净配任务、餐品规格、份数、食材明细、标准用量、差异阈值 | 1. 净配任务发布后实时下发。<br>2. 餐品规格和标准用量按餐品版本下发(餐品总库)。 | 任务状态、称重明细、实际用量、设备编号、操作员、完成状态 | 1. 任务完成后汇总称重明细和完成状态上报。 |
|
||||
| 按单烹制 | 烹制任务、餐品、需求份数、食材明细、工艺要求、温度阈值 | 1. 烹制任务发布后实时下发。<br>2. 任务调整后实时补发到后厨终端。<br>3. 工艺要求和温度阈值按版本发布后下发(餐品总库)。 | 开始/完成、厨师、生重、熟重、生熟比、温度过程、质量结果 | 1. 边缘节点服务器实时记录烹制过程,烹制完成后一次性实时上报至云端服务器。 |
|
||||
| 食谱管理 | 当周食谱数据、餐品数据、食谱状态 | 1. 后台周食谱生成后实时下发。<br>2. 食谱调整后实时下发。 | 暂无 | 暂无 |
|
||||
| 上餐监控 | 暂无 | 暂无(食谱、餐品、设备数据均已在其他模块下发) | 上餐时间、出锅温度、上餐重量、补餐记录、余量记录 | 1. 开餐信息实时上报。<br>2.补餐、余量监控等信息可3分钟周期性上报。 |
|
||||
@@ -246,8 +246,8 @@ sequenceDiagram
|
||||
|---|-----------------------------------------------|
|
||||
| 新节点初始化 | 新增边缘服务器后,可完成食堂基础资料、人脸、设备、餐品、食谱、字典、当日任务的全量初始化 |
|
||||
| 基础资料下发 | 云端修改员工、设备、餐品、食谱、字典后,指定边缘服务器可收到并应用,云端可查看结果 |
|
||||
| 生产任务下发 | 食谱、净菜订单、组配、烹制、上餐任务可下发到对应食堂边缘服务器并被终端查询 |
|
||||
| 现场数据上报 | 入库、称重、组配、烹制、上餐、取餐、消费、余量等数据可先写边缘并异步同步云端 |
|
||||
| 生产任务下发 | 食谱、净菜订单、净配、烹制、上餐任务可下发到对应食堂边缘服务器并被终端查询 |
|
||||
| 现场数据上报 | 入库、称重、净配、烹制、上餐、取餐、消费、余量等数据可先写边缘并异步同步云端 |
|
||||
| 断网续用 | 云边断连后,终端仍可查询已缓存任务并继续上报现场数据到边缘服务器 |
|
||||
| 网络恢复补传 | 网络恢复后,断网期间产生的数据可自动补传,云端不产生重复业务数据 |
|
||||
| 下发异常处理 | 下发失败、部分失败、终端应用失败可在云端查看失败原因,并支持重试或补发 |
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
营养管理服务后续会部署到多个食堂现场。不同食堂的网络环境、设备数量、设备类型、业务高峰和现场运维能力差异较大。如果所有现场设备都直接依赖云端服务,会带来几个明显问题:
|
||||
|
||||
- 网络不稳定时,称重、就餐、净菜组配、按单烹制、上餐等关键流程会被中断。
|
||||
- 网络不稳定时,称重、就餐、净配、按单烹制、上餐等关键流程会被中断。
|
||||
- 很多食堂现场设备处于内网环境,云端服务无法直接主动访问设备。
|
||||
- 现场设备数量增加后,高频称重、识别、就餐流水会对云端形成集中写入压力。
|
||||
- 人脸、菜品、物料、生产任务、升级包等资料需要提前下发到现场,不能完全依赖实时访问云端。
|
||||
@@ -46,7 +46,7 @@
|
||||
|----------|---|---|
|
||||
| 系统管理员 | 在管理端维护食堂、菜品、物料、用户、人脸、设备、任务和升级策略 | 资料是否下发成功、食堂是否在线、异常是否可处理 |
|
||||
| 食堂现场工作人员 | 管理本食堂当天供餐、设备运行、任务执行和异常处理 | 断网时能不能继续营业、设备能不能正常拿到任务 |
|
||||
| 设备操作员 | 在营养秤、组配秤、结算终端等设备上完成现场操作 | 操作是否顺畅、任务是否准确、数据是否能保存 |
|
||||
| 设备操作员 | 在营养秤、净配秤、结算终端等设备上完成现场操作 | 操作是否顺畅、任务是否准确、数据是否能保存 |
|
||||
| 运维人员 | 负责节点部署、网络排障、升级和故障恢复 | 节点是否在线、数据是否积压、服务是否异常 |
|
||||
| 现场设备 | 通过接口获取任务、资料并上报业务数据 | 接口稳定、响应快、弱网时可用 |
|
||||
|
||||
@@ -79,7 +79,7 @@ flowchart LR
|
||||
|
||||
subgraph Devices["现场设备"]
|
||||
D1["餐线营养秤"]
|
||||
D2["净菜/配比/组配秤"]
|
||||
D2["厨配秤/净配秤"]
|
||||
D3["结算终端/餐盘 RFID"]
|
||||
D4["人脸识别设备"]
|
||||
D5["大屏/落地屏"]
|
||||
@@ -108,7 +108,7 @@ flowchart LR
|
||||
|---|-----------------------------|---|
|
||||
| 云端主服务 | 全局权威、统一管理、策略配置、统计分析、数据最终一致性 | 主数据维护、食谱计划、生产任务、用户人脸、设备升级、跨食堂统计 |
|
||||
| 食堂边缘节点 | 现场缓存、设备接入、断网续用、同步中转站 | 本地任务缓存、现场数据接收、基础资料查询、同步状态维护、异常告警 |
|
||||
| 现场设备 | 现场作业、数据采集、人机交互 | 称重、组配、识别、结算、上餐、打印、展示、设备日志 |
|
||||
| 现场设备 | 现场作业、数据采集、人机交互 | 称重、净配、识别、结算、上餐、打印、展示、设备日志 |
|
||||
|
||||
## 6. 功能边界
|
||||
|
||||
@@ -117,7 +117,7 @@ flowchart LR
|
||||
- 维护食堂、档口、餐线、餐次等组织和场景资料。
|
||||
- 维护用户、部门、权限、餐盘、人脸等人员资料。
|
||||
- 维护菜品、餐品规格、食材、物料、净菜类型、包装规格等营养业务主数据。
|
||||
- 生成和管理生产任务,例如净菜订单、净菜组配、按单烹制、上餐任务。
|
||||
- 生成和管理生产任务,例如净菜订单、净配、按单烹制、上餐任务。
|
||||
- 配置设备升级策略、升级包、下发范围和升级批次。
|
||||
- 接收边缘节点上报的现场流水和任务状态。
|
||||
- 提供跨食堂查询、统计分析、异常监管和追溯能力。
|
||||
@@ -127,8 +127,8 @@ flowchart LR
|
||||
- 接入本食堂现场设备。
|
||||
- 缓存云端下发的基础资料、任务、人脸、升级包。
|
||||
- 在弱网或断网时,为现场设备提供本地查询和数据写入能力。
|
||||
- 记录现场称重、组配、就餐、余量、设备日志等数据。
|
||||
- 维护本食堂任务状态,例如待组配、组配中、已完成。
|
||||
- 记录现场称重、净配、就餐、余量、设备日志等数据。
|
||||
- 维护本食堂任务状态,例如待净配、净配中、已完成。
|
||||
- 将现场产生的数据异步同步到云端。
|
||||
- 接收云端下发内容并反馈处理结果。
|
||||
- 上报节点健康状态、设备在线状态和同步积压情况。
|
||||
@@ -136,7 +136,7 @@ flowchart LR
|
||||
### 6.3 现场设备负责
|
||||
|
||||
- 从边缘节点获取登录配置、基础资料和任务。
|
||||
- 执行称重、识别、组配、取餐、结算、上餐、打印等现场动作。
|
||||
- 执行称重、识别、净配、取餐、结算、上餐、打印等现场动作。
|
||||
- 将操作过程、称重结果、就餐流水、设备日志上报给边缘节点。
|
||||
- 接收边缘节点分发的升级任务和升级包。
|
||||
|
||||
@@ -190,13 +190,13 @@ flowchart LR
|
||||
- 食谱计划。
|
||||
- 净菜订单。
|
||||
- 净菜进柜。
|
||||
- 净菜组配。
|
||||
- 净配。
|
||||
- 按单烹制。
|
||||
- 上餐监控。
|
||||
- 余量处置。
|
||||
- 生产统计。
|
||||
|
||||
其中,净菜组配和按单烹制通常存在强关联。云端生成按单烹制任务时,应同步生成或关联净菜组配任务,并下发到对应食堂边缘节点。
|
||||
其中,净配和按单烹制通常存在强关联。云端生成按单烹制任务时,应同步生成或关联净配任务,并下发到对应食堂边缘节点。
|
||||
|
||||
### 7.5 就餐与称重数据采集
|
||||
|
||||
@@ -219,7 +219,7 @@ flowchart LR
|
||||
- 宣传图片。
|
||||
- 设备日志文件。
|
||||
- 现场视频或过程图片。
|
||||
- 组配、烹制、上餐相关附件。
|
||||
- 净配、烹制、上餐相关附件。
|
||||
|
||||
## 8. 核心业务规则
|
||||
|
||||
@@ -375,7 +375,7 @@ sequenceDiagram
|
||||
|
||||
### 10.2 现场数据上报
|
||||
|
||||
适用场景:就餐、入库、组配、余量、操作日志等现场数据由设备产生,需要保存并同步云端。
|
||||
适用场景:就餐、入库、净配、余量、操作日志等现场数据由设备产生,需要保存并同步云端。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
@@ -386,7 +386,7 @@ sequenceDiagram
|
||||
participant MQ as MQ Broker
|
||||
participant Cloud as 云端主服务
|
||||
|
||||
Device->>EdgeAPI: 上报就餐/组配/入库/余量数据
|
||||
Device->>EdgeAPI: 上报就餐/净配/入库/余量数据
|
||||
EdgeAPI->>DB: 写业务表
|
||||
EdgeAPI->>DB: 写 Outbox 事件
|
||||
EdgeAPI-->>Device: 返回成功
|
||||
@@ -406,9 +406,9 @@ sequenceDiagram
|
||||
2. 云端同步失败不影响设备继续操作。
|
||||
3. 管理端需要展示“未同步数据量”和“最后同步时间”。
|
||||
|
||||
### 10.3 净菜组配任务执行
|
||||
### 10.3 净配任务执行
|
||||
|
||||
适用场景:云端生成净菜组配任务后,食堂现场通过组配秤或终端设备执行任务。
|
||||
适用场景:云端生成净配任务后,食堂现场通过净配秤或终端设备执行任务。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
@@ -416,20 +416,20 @@ sequenceDiagram
|
||||
participant Cloud as 云端生产服务
|
||||
participant MQ as MQ
|
||||
participant Edge as 食堂边缘节点
|
||||
participant Scale as 组配秤/终端设备
|
||||
participant Scale as 净配秤/终端设备
|
||||
|
||||
Admin->>Cloud: 创建或生成净菜组配任务
|
||||
Admin->>Cloud: 创建或生成净配任务
|
||||
Cloud->>Cloud: 根据餐品规则计算食材明细
|
||||
Cloud->>MQ: 下发组配任务
|
||||
Cloud->>MQ: 下发净配任务
|
||||
MQ-->>Edge: 推送任务到指定食堂
|
||||
Edge->>Edge: 入库任务、食材配方、溯源码要求
|
||||
Scale->>Edge: 拉取待组配任务
|
||||
Scale->>Edge: 拉取待净配任务
|
||||
Edge-->>Scale: 返回任务与食材明细
|
||||
Scale->>Edge: 上报开始组配
|
||||
Edge->>Edge: 更新状态为组配中
|
||||
Scale->>Edge: 上报开始净配
|
||||
Edge->>Edge: 更新状态为净配中
|
||||
Scale->>Edge: 上报称重明细、设备编号、视频/图片索引
|
||||
Edge->>Edge: 本地保存过程数据
|
||||
Scale->>Edge: 上报完成组配
|
||||
Scale->>Edge: 上报完成净配
|
||||
Edge->>Edge: 更新状态为已完成
|
||||
Edge->>MQ: 异步上报任务完成事件
|
||||
MQ-->>Cloud: 云端入库并更新生产进度
|
||||
@@ -437,7 +437,7 @@ sequenceDiagram
|
||||
|
||||
产品规则:
|
||||
|
||||
1. 组配任务必须明确目标餐品、份数、截止时间和食材明细。
|
||||
1. 净配任务必须明确目标餐品、份数、截止时间和食材明细。
|
||||
2. 设备领取任务后,应记录设备编号和操作人员。
|
||||
3. 实际称重量与标准用量差异超过阈值时,应产生异常提示。
|
||||
4. 已完成任务需要支持云端查看过程记录和溯源链路。
|
||||
@@ -533,7 +533,7 @@ sequenceDiagram
|
||||
| 基础资料下发 | 云端更新菜品、食材、人脸、设备资料后,指定食堂边缘节点可收到并应用 |
|
||||
| 断网作业 | 云端断连后,现场设备仍可查询已缓存任务并上报业务数据 |
|
||||
| 网络恢复补传 | 网络恢复后,断网期间产生的数据可自动同步云端 |
|
||||
| 任务状态同步 | 组配任务从待组配到已完成的状态可在云端追踪 |
|
||||
| 任务状态同步 | 净配任务从待净配到已完成的状态可在云端追踪 |
|
||||
| 异常可见 | 同步失败、节点离线、设备离线、升级失败可在管理端查看 |
|
||||
| 数据不重复 | 重复上报或重复下发不会生成重复业务数据 |
|
||||
| 文件可分发 | 升级包可由边缘节点缓存并分发给现场设备 |
|
||||
@@ -579,7 +579,7 @@ sequenceDiagram
|
||||
| 层级 | 数据类型 | 同步方向 | 特点 |
|
||||
|---|---|---|---|
|
||||
| 基础主数据 | 食堂、档口、菜品、食材、物料、用户、设备 | 云端下发为主 | 版本号控制,支持全量初始化和增量更新 |
|
||||
| 任务数据 | 净菜、组配、烹制、上餐、升级任务 | 云端下发 + 边缘回写状态 | 有生命周期、状态机、截止时间 |
|
||||
| 任务数据 | 净菜、净配、烹制、上餐、升级任务 | 云端下发 + 边缘回写状态 | 有生命周期、状态机、截止时间 |
|
||||
| 现场流水 | 就餐、称重、余量、设备日志、操作记录 | 边缘上行为主 | 高频写入,必须本地优先保存 |
|
||||
| 文件数据 | 人脸图、日志、视频、升级包 | 双向 | MQ 只传元数据,文件走对象存储或分片下载 |
|
||||
| 统计数据 | 营养摄入、余量统计、生产统计 | 边缘可算 + 云端汇总 | 本地用于实时展示,云端用于全局分析 |
|
||||
|
||||
@@ -33,7 +33,7 @@ CQ 能源作为中国最大的油气田之一,员工分布广泛、工作环
|
||||
| Web 管理端 | PC 浏览器 | 营养师、食堂管理员、供应链管理员、系统管理员 | 全链路业务管理与数据分析 |
|
||||
| 微信小程序 | 移动端 | 能源员工 | 点餐、营养查询、食谱分享、溯源 |
|
||||
| 食堂边缘节点 | 服务器 | 食堂终端设备群 | 终端数据汇聚、断网可用、数据上报下发 |
|
||||
| 餐厅终端设备 | PAD/秤具 | 食堂操作员、就餐员工 | 称重、营养计算、出餐取餐、包装组配 |
|
||||
| 餐厅终端设备 | PAD/秤具 | 食堂操作员、就餐员工 | 称重、营养计算、出餐取餐、包装净配 |
|
||||
|
||||
---
|
||||
|
||||
@@ -98,7 +98,7 @@ CQ 能源作为中国最大的油气田之一,员工分布广泛、工作环
|
||||
| 营养用餐 | 取餐管理 | 取餐核销、余量处理 |
|
||||
| 营养用餐 | 终端管理 | 自助终端、点餐机远程管理 |
|
||||
| 健康生产 | 净菜下单 | 向供应端申请净菜 |
|
||||
| 健康生产 | 配菜组合 | 按配方组配净菜、生成烹制任务 |
|
||||
| 健康生产 | 配菜组合 | 按配方净配净菜、生成烹制任务 |
|
||||
| 健康生产 | 烹饪加工 | 记录烹制过程、AI 质量评分 |
|
||||
| 健康生产 | 出餐管理 | 实时监控出餐进度 |
|
||||
| 健康生产 | 余量处理 | 处理剩余菜品(福利/捐赠/废弃) |
|
||||
@@ -354,7 +354,7 @@ flowchart LR
|
||||
U -.福利捐赠.-> U
|
||||
```
|
||||
|
||||
**业务闭环**:营养师制定食谱后自动生成净菜需求,通过取餐柜管理食材存取,按配方组配后进入烹制环节(含 AI 质量评分),出餐全程监控,剩余菜品通过员工福利、社会捐赠等方式处置,减少浪费。
|
||||
**业务闭环**:营养师制定食谱后自动生成净菜需求,通过取餐柜管理食材存取,按配方净配后进入烹制环节(含 AI 质量评分),出餐全程监控,剩余菜品通过员工福利、社会捐赠等方式处置,减少浪费。
|
||||
|
||||
### 4.4 主线四:员工服务链(餐桌→健康)
|
||||
|
||||
@@ -538,7 +538,7 @@ flowchart LR
|
||||
| 食谱排期-创建/编辑(prod-recipe-plan-create) | 营养师按周创建或修改食谱排期,关联食堂/餐次/餐品并实时回显营养信息,为生产任务派发提供依据 |
|
||||
| 食谱排期-详情(prod-recipe-plan-detail) | 食堂管理员/营养师查看食谱排期详情,含餐品清单与营养汇总,作为生产任务派发的依据 |
|
||||
| 净菜下单(prod-clean-order) | 食堂管理员根据食谱排期向供应端申请净菜,跟踪备货→配送→收货全流程,是配菜组合的前置环节 |
|
||||
| 配菜组合(prod-proc-combo) | 配菜员按菜品配方组配净菜并生成烹制任务单,溯源码贯穿,为烹饪加工提供食材准备 |
|
||||
| 配菜组合(prod-proc-combo) | 配菜员按菜品配方净配净菜并生成烹制任务单,溯源码贯穿,为烹饪加工提供食材准备 |
|
||||
| 烹饪加工(prod-proc-cook) | 厨师记录烹制过程(生重/熟重/生熟比),AI 模型质量评分,完成后触发出餐管理流程 |
|
||||
| 出餐管理(prod-proc-serve) | 食堂管理员实时监控出餐进度,记录出锅温度/送达时间/上餐重量,为取餐核销与营养摄入计算提供数据 |
|
||||
| 余量处理(prod-proc-surplus) | 食堂管理员处理剩余菜品(员工福利/社会捐赠/废弃/再利用),员工福利场景支持领取记录台账,是浪费控制与成本核算的闭环环节 |
|
||||
@@ -657,7 +657,7 @@ flowchart LR
|
||||
| 社区生态 | UGC 食谱创作 + 排行激励 + 分享收益,形成营养知识传播网络 |
|
||||
| 双端协同 | B 端精细化管理 + C 端便捷化服务,数据实时互通 |
|
||||
| 边缘云端协同 | 食堂边缘节点缓存中转,终端设备断网可用;数据下发/上报任务化追踪;异常告警集中处置 |
|
||||
| 多终端设备生态 | 餐线营养秤、净菜包装秤、净菜组配秤、毛菜验收秤、加工毛菜秤等专用终端设备覆盖生产全流程 |
|
||||
| 多终端设备生态 | 餐线营养秤、净菜包装秤、净配秤、毛菜验收秤、加工毛菜秤等专用终端设备覆盖生产全流程 |
|
||||
|
||||
---
|
||||
|
||||
@@ -676,10 +676,10 @@ flowchart LR
|
||||
| 2 | 毛菜验收秤 | 已设计 | 称重传感器、食材识别摄像头、人脸识别摄像头、场景监控摄像头 | 卫生供应、绿色种采 | 绿色种采(采供管理、供商管理) | 卫生供应(毛菜入库、卫生检测) |
|
||||
| 3 | 毛菜库存柜 | 无需设计 | 温湿度传感器、库存货架 | 卫生供应 | 卫生供应(毛菜入库、卫生检测) | 卫生供应(净菜加工出库) |
|
||||
| 4 | 加工毛菜秤 | 已设计 | 称重传感器、AI识别摄像头、人脸识别摄像头 | 卫生供应 | 卫生供应(仓储管理→出库领料) | 卫生供应(净菜加工→净菜入库) |
|
||||
| 5 | 净菜加工秤 | 已设计 | 称重传感器、AI识别摄像头、人脸识别摄像头 | 卫生供应 | 卫生供应(仓储管理→毛菜出库) | 卫生供应(净菜入库、净菜包装) |
|
||||
| 5 | 厨配秤 | 已设计 | 称重传感器、AI识别摄像头、人脸识别摄像头 | 卫生供应 | 卫生供应(仓储管理→毛菜出库) | 卫生供应(净菜入库、净菜包装) |
|
||||
| 6 | 净菜库存柜 | 无需设计 | 温湿度传感器、库存货架 | 卫生供应 | 卫生供应(净菜加工→净菜入库) | 卫生供应(净菜包装)、健康生产(净菜下单→配菜组合) |
|
||||
| 7 | 净菜组配秤 | 已设计 | 称重传感器(主辅材+调料双区)、食材识别摄像头、人脸识别摄像头 | 卫生供应、健康生产 | 卫生供应(净菜包装出库)、健康生产(净菜下单) | 卫生供应(净菜包装→内供/外售)、健康生产(配菜组合→烹饪加工) |
|
||||
| 8 | 净菜包装贴标秤 | 已设计 | 称重传感器、标签打印机、食材识别摄像头、人脸识别摄像头 | 卫生供应、健康生产 | 卫生供应(净菜组配)、健康生产(配菜组合) | 卫生供应(净菜内供配送、净菜外售发货) |
|
||||
| 7 | 净配秤 | 已设计 | 称重传感器(主辅材+调料双区)、食材识别摄像头、人脸识别摄像头 | 卫生供应、健康生产 | 卫生供应(净菜包装出库)、健康生产(净菜下单) | 卫生供应(净菜包装→内供/外售)、健康生产(配菜组合→烹饪加工) |
|
||||
| 8 | 净菜包装贴标秤 | 已设计 | 称重传感器、标签打印机、食材识别摄像头、人脸识别摄像头 | 卫生供应、健康生产 | 卫生供应(净配)、健康生产(配菜组合) | 卫生供应(净菜内供配送、净菜外售发货) |
|
||||
|
||||
#### 7.1.2 健康生产域(4 款)
|
||||
|
||||
@@ -717,14 +717,14 @@ flowchart LR
|
||||
食材加工链(仓库→厨房) 毛菜卫检设备 ①
|
||||
毛菜入库 → 卫生检测 → 仓储管理 → 净菜加工 → 净菜入库 → 净菜包装 → 内供/外售
|
||||
↑ ↑ ↑ ↑ ↑ ↑ ↑
|
||||
毛菜库存柜 ③ 毛菜卫检 毛菜库存柜 加工毛菜秤 ④ 净菜库存柜 ⑥ 净菜包装 净菜组配秤 ⑦
|
||||
净菜加工秤 ⑤ 贴标秤 ⑧ 净菜包装贴标秤 ⑧
|
||||
毛菜库存柜 ③ 毛菜卫检 毛菜库存柜 加工毛菜秤 ④ 净菜库存柜 ⑥ 净菜包装 净配秤 ⑦
|
||||
厨配秤 ⑤ 贴标秤 ⑧ 净菜包装贴标秤 ⑧
|
||||
|
||||
配餐生产链(厨房→餐桌)
|
||||
食谱排期 → 净菜下单 → 配菜组合 → 烹饪加工 → 出餐管理 → 余量处理
|
||||
↑ ↑
|
||||
净菜厨房柜 ⑪ 加工提示屏 ⑫
|
||||
净菜组配秤 ⑦ 食安监控设备 ⑨
|
||||
净配秤 ⑦ 食安监控设备 ⑨
|
||||
水电气计量表 ⑩
|
||||
|
||||
员工服务链(餐桌→健康)
|
||||
@@ -736,5 +736,5 @@ flowchart LR
|
||||
餐线充值屏 ⑮
|
||||
```
|
||||
|
||||
> 图中编号对应 7.1 节硬件清单中的序号。同一设备可能横跨多个业务链环节(如净菜组配秤同时服务于食材加工链和配餐生产链)。
|
||||
> 图中编号对应 7.1 节硬件清单中的序号。同一设备可能横跨多个业务链环节(如净配秤同时服务于食材加工链和配餐生产链)。
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
| 端 | 改动菜单数 | 涉及核心场景 |
|
||||
|----|-----------|-------------|
|
||||
| 管理端(营养管理) | 19 个菜单 | 食材档案、餐品档案、字典、订单、加工、包装、质控、分析 |
|
||||
| 设备端(食堂设备) | 7 个设备模块(共 14 个页面) | 验收秤、毛菜加工秤、净菜加工秤、包装贴标秤、组配秤 |
|
||||
| 设备端(食堂设备) | 7 个设备模块(共 14 个页面) | 验收秤、毛菜加工秤、厨配秤、包装贴标秤、净配秤 |
|
||||
|
||||
---
|
||||
|
||||
@@ -111,13 +111,13 @@
|
||||
|
||||
### 6. 生产加工
|
||||
|
||||
#### 6.1 净菜组配
|
||||
#### 6.1 净配
|
||||
- 删除"3 个全局固定份型选项"(小份/标准份/大份),原下拉与所选餐品完全脱钩
|
||||
- 改为餐品级联动:选择餐品后,份型下拉动态加载该餐品在总库的份型列表
|
||||
- 选中份型后自动带出对应克重与单位
|
||||
|
||||
#### 6.2 按单烹制
|
||||
- 规格字段改为只读回显(从上游组配单继承,本页不再允许编辑规格)
|
||||
- 规格字段改为只读回显(从上游净配单继承,本页不再允许编辑规格)
|
||||
- 规格展示统一为 `克重g/份型`
|
||||
- **新增"生熟比"自动计算**:输入生重后,熟重按出净率自动计算(向上取整),出净率从食材档案关联读取
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
|
||||
---
|
||||
|
||||
### 12. 净菜加工秤 - 设置
|
||||
### 12. 厨配秤 - 设置
|
||||
|
||||
#### 12.1 加工参数设置
|
||||
- 删除"重量单位"下拉(与设备端"克存储"统一方向冲突),改为说明文字提示(由系统自动还原"斤"显示)
|
||||
@@ -208,12 +208,12 @@
|
||||
|
||||
---
|
||||
|
||||
### 14. 净菜组配秤(3 个页面)
|
||||
### 14. 净配秤(3 个页面)
|
||||
|
||||
#### 14.1 组配任务 / 14.2 称重包装 / 14.3 校验历史
|
||||
#### 14.1 净配任务 / 14.2 称重包装 / 14.3 校验历史
|
||||
- 包装规格从描述性字符串(如 `大份800g`)统一结构化为"份型 + 克重 + 单位"三要素
|
||||
- 切配规格字符串统一结构化,展示层统一格式输出
|
||||
- **接入餐品总库**:组配任务页启动时从餐品总库加载餐品清单与份型选项,不再使用页内写死数据
|
||||
- **接入餐品总库**:净配任务页启动时从餐品总库加载餐品清单与份型选项,不再使用页内写死数据
|
||||
- 与"净菜包装贴标秤"口径完全对齐
|
||||
|
||||
---
|
||||
|
||||
@@ -40,16 +40,16 @@
|
||||
第三批(并行,阶段三):
|
||||
任务3.1 净菜订单(依赖 3.0)
|
||||
任务3.2 外售/内供(依赖 3.0、3.1)
|
||||
任务3.3a 净菜组配(依赖 3.0)
|
||||
任务3.3a 净配(依赖 3.0)
|
||||
任务3.3b 按单烹制生熟比(独立)
|
||||
任务3.4 包装标签(依赖 3.0)
|
||||
|
||||
第四批(并行,阶段四设备端):
|
||||
任务4.1a/4.1b 毛菜验收秤
|
||||
任务4.2a/4.2b/4.2c 毛菜加工秤(斤→克)
|
||||
任务4.3 净菜加工秤设置
|
||||
任务4.3 厨配秤设置
|
||||
任务4.4 净菜包装贴标秤(最复杂)
|
||||
任务4.5 净菜组配秤
|
||||
任务4.5 净配秤
|
||||
|
||||
第五批(可选,阶段五):
|
||||
任务5.1 营养质控(依赖 3.0)
|
||||
@@ -86,17 +86,17 @@
|
||||
阶段二(核心业务)
|
||||
─────────────────
|
||||
任务2.1 餐品总库 ──────┬─→ 任务3.1 净菜订单(cp下拉)
|
||||
├─→ 任务3.3 净菜组配/烹制(餐品L4)
|
||||
├─→ 任务3.3 净配/烹制(餐品L4)
|
||||
├─→ 任务4.5 ratio-scale(餐品净菜包)
|
||||
└─→ 任务5.1 prod-qc-nutrition(同步目标)
|
||||
|
||||
任务2.2 sup-in-clean ─→ 任务2.3 sup-in-cleanin(URL参数)
|
||||
|
||||
|
||||
阶段三(订单/组配/包装)
|
||||
阶段三(订单/净配/包装)
|
||||
─────────────────────
|
||||
任务3.1 净菜订单 ─────→ 任务3.2 外售/内供
|
||||
任务3.1 净菜订单 ─────→ 任务3.3 组配/烹制(部分依赖)
|
||||
任务3.1 净菜订单 ─────→ 任务3.3 净配/烹制(部分依赖)
|
||||
|
||||
|
||||
阶段四(设备端)
|
||||
@@ -472,7 +472,7 @@ specs:[
|
||||
|
||||
---
|
||||
|
||||
## 五、阶段三:订单/组配/包装改造(v2 修订)
|
||||
## 五、阶段三:订单/净配/包装改造(v2 修订)
|
||||
|
||||
> **v2 修订背景**:核查发现 3 套互不相同的 cp 规格硬编码字典(`cpSpecOptions`/`cpSpecOptsExt`/`DISH_SPEC`),且 prod-proc-combo/cook 的规格下拉是"全局固定选项"与所选餐品完全脱钩,原方案的"从总库读取"缺数据接入路径。本阶段新增**任务 3.0 跨页共享前置**,并把 3.3 拆分为 3.3a/3.3b。
|
||||
|
||||
@@ -603,7 +603,7 @@ specs:[
|
||||
|
||||
---
|
||||
|
||||
### 任务 3.3a:净菜组配改造 `prod-proc-combo.html`
|
||||
### 任务 3.3a:净配改造 `prod-proc-combo.html`
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
@@ -645,7 +645,7 @@ specs:[
|
||||
|
||||
**v2 改造点**:
|
||||
1. **规格只读回显**(保留 disabled):
|
||||
- 数据源改为从组配单(COOK-xxx)继承,**不新增编辑能力**
|
||||
- 数据源改为从净配单(COOK-xxx)继承,**不新增编辑能力**
|
||||
- 格式:`formatSpec({baseGram,unit}) + ' / ' + portion`
|
||||
2. **新增 yieldRate 字段**:
|
||||
- 在每条烹制任务数据中新增 `yieldRate`(从 vegTypes.yieldRate 关联)
|
||||
@@ -798,7 +798,7 @@ specs:[
|
||||
|
||||
---
|
||||
|
||||
### 任务 4.3:净菜加工秤设置页改造 `clean-veg-ratio-scale/03-settings.html`
|
||||
### 任务 4.3:厨配秤设置页改造 `clean-veg-ratio-scale/03-settings.html`
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
@@ -884,7 +884,7 @@ specs:[
|
||||
|
||||
---
|
||||
|
||||
### 任务 4.5:净菜组配秤改造 `ratio-scale/*.html` ⭐(3 个文件)
|
||||
### 任务 4.5:净配秤改造 `ratio-scale/*.html` ⭐(3 个文件)
|
||||
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
@@ -1028,7 +1028,7 @@ specs:[
|
||||
| **3.0** | **跨页 specs 共享机制(v2新增)** | 三 | ✅ 已完成 | 2026-08-04 | din-dish-all.html 持久化 dish_specs_v1;下游页 loadDishSpecs/getSpecsByDish/getAllDishNames 三函数内联 |
|
||||
| 3.1 | 净菜订单改造 | 三 | ✅ 已完成 | 2026-08-04 | 删除 cpSpecOptions/DISH_SPEC;newOrderLines 改 portion+baseGram+unit;nc 模式左克重+右单位,cp 模式左克重+右份型;食谱快速创建改 getDishSpecForRecipe |
|
||||
| 3.2 | 外售/内供 | 三 | ✅ 已完成 | 2026-08-04 | sup-clean-external.html 删 cpSpecOptsExt;sup-clean-internal.html 删 cpSpecOptions;统一改为 portion+baseGram+unit + spec-input-group 连体组件;外售默认 unit=箱,内供默认 unit=盘 |
|
||||
| **3.3a** | **净菜组配(v2拆分)** | 三 | ✅ 已完成 | 2026-08-04 | prod-proc-combo.html 删除 formSpec 全局固定 3 选项;改 onComboDishChange 动态从总库加载份型;_selectedSpec 缓存 baseGram/unit |
|
||||
| **3.3a** | **净配(v2拆分)** | 三 | ✅ 已完成 | 2026-08-04 | prod-proc-combo.html 删除 formSpec 全局固定 3 选项;改 onComboDishChange 动态从总库加载份型;_selectedSpec 缓存 baseGram/unit |
|
||||
| **3.3b** | **按单烹制生熟比(v2拆分)** | 三 | ✅ 已完成 | 2026-08-04 | prod-proc-cook.html 新增 DISH_YIELD_RATE 字典+autoCalcCookedWeight 函数;editSpec 改 formatSpecDisplay(matchSpecByText());生重变化触发熟重自动计算(向上取整) |
|
||||
| 3.4 | 包装标签 sup-pkg-label | 三 | ✅ 已完成 | 2026-08-04 | 净菜包规格改 spec-input-group(左克重+右单位,SPEC_UNIT_OPTIONS 9 项);餐品包规格改 buildDishSpecOptions(dish) 动态按餐品加载 |
|
||||
| **4.1a** | **食材目录 verifyType 字段(v2拆分)** | 四 | ✅ 已完成 | 2026-08-05 | foodCatalog 20 项默认 verifyType='weight';spiceCatalog 8 项默认 verifyType='count'(二分法够用,体积类由操作员自行转质量) |
|
||||
@@ -1036,9 +1036,9 @@ specs:[
|
||||
| **4.2a** | **毛菜加工秤存储层斤→克(v2拆分)** | 四 | ✅ 已完成 | 2026-08-05 | 4 文件 mock 数据全部改为 data-weight-g 克存储;taskDataMap 值改为克;record-card 添加 data-weight-g 属性 |
|
||||
| **4.2b** | **毛菜加工秤展示层兼容(v2拆分)** | 四 | ✅ 已完成 | 2026-08-05 | 依赖 4.2a;内联 formatGramForCanteen,展示层自动还原"斤"显示(如 2000g→4 斤);修复 04-history 语法错误(g===null===''→g===null||g==='') |
|
||||
| **4.2c** | **新增 yieldRate 字段(v2拆分,仅1文件)** | 四 | ✅ 已完成 | 2026-08-05 | 03-batch-washing.html aiResults 6 项 + foodDB 12 项新增 yieldRate(85~95);UI 列展示暂未添加,允许后续从食材档案带出 |
|
||||
| 4.3 | 净菜加工秤设置 | 四 | ✅ 已完成 | 2026-08-05 | 删除"重量单位" select 替换为 hint 说明(formatGramForCanteen 自动还原);滚刀块 cm→mm 统一(2cm→20mm,3cm→30mm,5cm→50mm);加工参数区添加 hint 引用 vegTypes.cutSpec + spec_cut_shape 字典 |
|
||||
| 4.3 | 厨配秤设置 | 四 | ✅ 已完成 | 2026-08-05 | 删除"重量单位" select 替换为 hint 说明(formatGramForCanteen 自动还原);滚刀块 cm→mm 统一(2cm→20mm,3cm→30mm,5cm→50mm);加工参数区添加 hint 引用 vegTypes.cutSpec + spec_cut_shape 字典 |
|
||||
| 4.4 | 净菜包装贴标秤(3文件) | 四 | ✅ 已完成 | 2026-08-05 | 3 文件全部内联 5 个工具函数(formatSpec/formatCutSpec/parseLegacyCutSpec/parseLegacyPackSpec/formatGram);cutSpec/packSpec/portions 字段全部结构化,portions→packSize 重命名,渲染使用 formatSpec/formatCutSpec;"真空包装"作为 packMethod 不在 packSpec 内 |
|
||||
| 4.5 | 净菜组配秤(3文件) | 四 | ✅ 已完成 | 2026-08-05 | 3 文件全部内联 5 个工具函数;RECORDS/taskData/mockData 中 packSpec 全部结构化为 {portion,baseGram,unit} 对象;cutSpec 结构化为 {length,width,height,shape,technique};渲染层统一 formatSpec/formatCutSpec;接入 localStorage dish_specs_v1 跨页共享(resolvePackSpec 兜底) |
|
||||
| 4.5 | 净配秤(3文件) | 四 | ✅ 已完成 | 2026-08-05 | 3 文件全部内联 5 个工具函数;RECORDS/taskData/mockData 中 packSpec 全部结构化为 {portion,baseGram,unit} 对象;cutSpec 结构化为 {length,width,height,shape,technique};渲染层统一 formatSpec/formatCutSpec;接入 localStorage dish_specs_v1 跨页共享(resolvePackSpec 兜底) |
|
||||
| 5.1 | 营养质控(3文件) | 五 | ✅ 已完成 | 2026-08-05 | prod-qc-nutrition confirmSync 真落地:按 dish 聚合后写入 localStorage['dish_specs_v1'] 的 nutri 字段 + 追加日志到 localStorage['nutri_sync_log_v1'](限 100 条);prod-qc-nutrition-add 改为 loadDishSpecs 按 CANTEEN_LINE_MAP 分组(回退到 FALLBACK_DISH_DATA);prod-qc-nutrition-sync 优先读取 nutri_sync_log_v1(avgCache 直读),无日志时回退到内置示例 |
|
||||
| 5.2 | 分析页(2文件) | 五 | ✅ 已完成 | 2026-08-05 | sup-process-analysis 切配分类"切丝/切片/切丁/切段"→"丝/片/丁/段"(形状名词,对齐 spec_cut_shape 字典);表格行"土豆(切丝)"→"土豆(丝)";包装线新增独立 pack_method 字典(真空/气调/普通/冷鲜/保鲜盒/散装托盘);sup-biz-analysis 生产分析表"投料重量(吨)/产出重量(吨)"→改为 kg 与 sup-process-analysis 口径一致 |
|
||||
| 5.3 | 净菜进柜 | 五 | ✅ 已完成 | 2026-08-05 | CABINET_DATA.spec 多形态字符串('5kg/袋'/'标准份(500g)'/'6份/箱')统一结构化为 {baseGram,unit,portion?};内联 formatSpec 工具函数;Drawer 回显与 dishSelect 渲染统一 formatSpec 输出;'6份/箱' 决策为 6 个份型单位装一箱 → {baseGram:6,unit:'箱',portion:'份'} |
|
||||
|
||||
@@ -47,7 +47,7 @@ din-ingredient packSpec: '500g'/'大份800g' 包装规
|
||||
⚠ 同字段命名不一致(vegCutType
|
||||
clean-veg-ratio-scale ⚠(修订) vs cutSpec vs cutType)
|
||||
03-settings.html:
|
||||
丝条粗细(mm) ratio-scale ⚠(UI:净菜组配秤)
|
||||
丝条粗细(mm) ratio-scale ⚠(UI:净配秤)
|
||||
滚刀块大小(cm枚举) 02-weighing-packing:
|
||||
方丁大小(mm枚举) cutSpec: '20mm*1mm*1mm·细丝·平刀'
|
||||
刀工(直刀/斜刀/花刀)⚠新发现 seasoning cellIdx + weightPerPack
|
||||
@@ -77,7 +77,7 @@ specs: [
|
||||
- 规格名称枚举:`标准份` / `小份` / `大份` / `迷你份` / `加大份`(在 `din-dish-add.html` 的下拉中定义)
|
||||
- 关键字段:name(份型名称)、weight(成品重量g)、price(餐品售价)、cleanPrice(净菜包售价)、cleanSale(是否外售上架)
|
||||
- **潜在规格数据源**:`prod-qc-nutrition.html` 有"同步检验均值→餐品总库"入口(目前只 toast),意味着餐品总库 specs 未来可能还需要承载营养规格字段(8项营养指标 + 4类感官评级)
|
||||
- **问题**:组配/烹制等下游环节使用另一套硬编码的规格下拉,与总库中定义的不联动
|
||||
- **问题**:净配/烹制等下游环节使用另一套硬编码的规格下拉,与总库中定义的不联动
|
||||
|
||||
### 3.2 食物成分库 `din-ingredient.html`——食材级"规格"
|
||||
|
||||
@@ -206,9 +206,9 @@ var FOOD_VEG_MAP = {
|
||||
- 接收上游 `sup-in-clean.html` 的 URL 参数(`batch/trace/food/cleanType/weight`)
|
||||
- **问题**:食材→净菜类型的映射(`cleanFoodTypeOptions`)在本页面独立维护,与 `sup-in-clean.html` 的 `FOOD_VEG_MAP`、`din-ingredient.html` 的 `vegTypes` **三处重复定义且命名不完全一致**
|
||||
|
||||
### 3.9 净菜加工秤 `clean-veg-ratio-scale/*.html` ⚠(v3 修订)
|
||||
### 3.9 厨配秤 `clean-veg-ratio-scale/*.html` ⚠(v3 修订)
|
||||
|
||||
**业务定位**(由设备 index.html 卡片确认):**净菜加工秤**——承担"毛菜→净菜"的 AI 识别 + 加工(切配)验收。**注意:不是 ratio-scale 的旧版本,两者是并行设备**(详见 3.12)。
|
||||
**业务定位**(由设备 index.html 卡片确认):**厨配秤**——承担"毛菜→净菜"的 AI 识别 + 加工(切配)验收。**注意:不是 ratio-scale 的旧版本,两者是并行设备**(详见 3.12)。
|
||||
|
||||
页面清单:`00-welcome` / `01-accept` / `02-history` / `03-settings` / `04-sampling` / `05-sampling-history`
|
||||
|
||||
@@ -260,9 +260,9 @@ cpSpecOptions = {
|
||||
|
||||
- **问题**:外售用 `箱`,内供用 `袋/盘`,包装容器类型没有统一约束
|
||||
|
||||
### 3.12 净菜组配秤 `ratio-scale/*.html` ⚠(v3 新增,UI 名"净菜组配秤")
|
||||
### 3.12 净配秤 `ratio-scale/*.html` ⚠(v3 新增,UI 名"净配秤")
|
||||
|
||||
**业务定位**(由设备 index.html 卡片确认):**净菜组配秤**——把多份净菜 + 调料按餐品配料表分装组合成"餐品净菜包"(如宫保鸡丁套餐)。**与 clean-veg-ratio-scale 是不同业务定位的并行设备,不是新旧替代关系**。
|
||||
**业务定位**(由设备 index.html 卡片确认):**净配秤**——把多份净菜 + 调料按餐品配料表分装组合成"餐品净菜包"(如宫保鸡丁套餐)。**与 clean-veg-ratio-scale 是不同业务定位的并行设备,不是新旧替代关系**。
|
||||
|
||||
页面清单:`index` / `00-welcome` / `00-login` / `01-packing-tasks` / `02-weighing-packing` / `03-verify-history` / `04-seasoning-settings`
|
||||
|
||||
@@ -312,15 +312,15 @@ cpSpecOptions = {
|
||||
4. **同义字段多命名**:`packSize` vs `portions`(同义,两处命名不同)
|
||||
5. **与管理端口径不一致**:sup-pkg-label.html 的 `packSpec` 是纯整数(`500`/`1000`),本设备是带单位字符串(`'500g'`/`'大份800g'`),**无法直接 join**
|
||||
|
||||
### 3.14 净菜组配 + 按单烹制 `prod-proc-combo.html` / `prod-proc-cook.html`
|
||||
### 3.14 净配 + 按单烹制 `prod-proc-combo.html` / `prod-proc-cook.html`
|
||||
|
||||
```
|
||||
组配: 餐品规格下拉 → 小份(200g) | 标准份(300g) | 大份(500g)【硬编码】
|
||||
净配: 餐品规格下拉 → 小份(200g) | 标准份(300g) | 大份(500g)【硬编码】
|
||||
烹制: 餐品规格只读显示 + 生重(kg)/熟重(kg)/生熟比
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 组配中每份餐品的食材配方(重量以克为单位)
|
||||
// 净配中每份餐品的食材配方(重量以克为单位)
|
||||
recipeData = {
|
||||
'宫保鸡丁': [
|
||||
{trace:'NC-PROC-JXR-...', name:'鸡胸肉', type:'主材', amount:120, unit:'g'},
|
||||
@@ -390,7 +390,7 @@ recipeData = {
|
||||
|
||||
## 四、加工规格的额外维度
|
||||
|
||||
在净菜加工秤设置页 `clean-veg-ratio-scale/03-settings.html` 中:
|
||||
在厨配秤设置页 `clean-veg-ratio-scale/03-settings.html` 中:
|
||||
|
||||
```
|
||||
默认丝条粗细(mm) → 丝条规格的标准粗细
|
||||
@@ -411,7 +411,7 @@ recipeData = {
|
||||
| # | 问题 | 严重程度 | 涉及页面/设备数 |
|
||||
|---|------|---------|-----------|
|
||||
| 1 | **采购规格自由文本,无约束无联动**——下游入库要重选 | 🔴 高 | 3 |
|
||||
| 2 | **餐品总库定义了 specs,但组配/烹制用另一套硬编码下拉** | 🔴 高 | 3 |
|
||||
| 2 | **餐品总库定义了 specs,但净配/烹制用另一套硬编码下拉** | 🔴 高 | 3 |
|
||||
| 3 | **包装容器类型(袋/盒/箱/盘/筐/桶/瓶)散落各处,无统一枚举** | 🔴 高 | 6+ |
|
||||
| 4 | **食材→净菜类型映射在 4+ 处重复定义且命名不一致**:`din-ingredient.vegTypes` / `sup-in-clean.FOOD_VEG_MAP` / `sup-in-cleanin.cleanFoodTypeOptions` / `clean-veg-ratio-scale` 默认值 | 🔴 高 | 4 |
|
||||
| 5 | **采购→净菜→餐品三级规格之间没有任何转换规则** | 🔴 高 | 全部 |
|
||||
@@ -486,7 +486,7 @@ type SpecUnit =
|
||||
| `盒` | 成品盒装 | 是 | 净菜入库 | 300g/盒 |
|
||||
| `箱` | 外售/内供包装 | 是 | 外售、内供 | 5kg/箱 → baseGram=5000 |
|
||||
| `盘` | 内供专用 | 是 | 内供 | 2kg/盘 → baseGram=2000 |
|
||||
| `份` | 餐品规格 | 是 | 餐品总库、组配、烹制 | 标准份300g |
|
||||
| `份` | 餐品规格 | 是 | 餐品总库、净配、烹制 | 标准份300g |
|
||||
| `筐` | 周转筐 | 是 | 毛菜入库 | 10kg/筐 |
|
||||
| `瓶`/`罐`/`桶` | 调料/液体容器(方案A:体积≈质量) | 是 | 采购调料、验收 | 5L/桶 → baseGram=5000、500ml/瓶 → baseGram=500 |
|
||||
| `把`/`个`/`只`/`条` | 计数类(**必须填预估克重**) | 是 | 调料/配料点收 | 蒜苗1把≈100g、鸡蛋10个≈500g |
|
||||
@@ -785,13 +785,13 @@ vegTypes: [
|
||||
| **毛菜入库** `sup-in-raw` | 7种固定下拉 | 从采购订单继承 L1,无需重选 |
|
||||
| **净菜加工批次** `sup-in-clean` ⚠ | `FOOD_VEG_MAP` 硬编码 + 出净率"自动计算"未实现 | **删除 `FOOD_VEG_MAP`**,改为按选中食材从 `din-ingredient.vegTypes` 动态拉取(含 cutSpec);出净率真实计算: `⌈净菜产出/毛菜 × 100⌉`;反向推导: `毛菜需求 = ⌈净菜需求 / yieldRate⌉` |
|
||||
| **净菜入库** `sup-in-cleanin` | 净菜类型+入库规格分开选,`cleanFoodTypeOptions` 硬编码 | **删除 `cleanFoodTypeOptions`**,改为从 `din-ingredient.vegTypes` 拉取;选 L2(净菜类型,带 cutSpec) + 选/带出 L3(`baseGram+unit`) |
|
||||
| **净菜加工秤设置** `clean-veg-ratio-scale/03-settings` ⚠ | 4 字段单位混乱(mm/cm) | 默认值从 `din-ingredient.vegTypes.cutSpec` 读取(本设备仅作覆盖);**统一单位为 mm**;刀工字段作独立枚举 |
|
||||
| **厨配秤设置** `clean-veg-ratio-scale/03-settings` ⚠ | 4 字段单位混乱(mm/cm) | 默认值从 `din-ingredient.vegTypes.cutSpec` 读取(本设备仅作覆盖);**统一单位为 mm**;刀工字段作独立枚举 |
|
||||
| **净菜订单** `prod-clean-order` | nc 自由输克数 / cp 硬编码下拉 | nc 录入 `baseGram+unit`;cp 从餐品总库 L4 动态拉取 |
|
||||
| **外售/内供** | nc 自由输克数 | 同净菜订单,`unit` 允许选 箱/盘/袋 |
|
||||
| **净菜组配秤** `ratio-scale/*` ⚠ | `cutSpec='20mm*1mm*1mm·细丝·平刀'` 字符串 | `cutSpec` 改为结构化对象 `{length,width,height,shape,technique}`;`packSpec='大份800g'` 拆为 `{specName:'大份', baseGram:800}`;`packSize` 统一命名(取消 `portions`) |
|
||||
| **净配秤** `ratio-scale/*` ⚠ | `cutSpec='20mm*1mm*1mm·细丝·平刀'` 字符串 | `cutSpec` 改为结构化对象 `{length,width,height,shape,technique}`;`packSpec='大份800g'` 拆为 `{specName:'大份', baseGram:800}`;`packSize` 统一命名(取消 `portions`) |
|
||||
| **净菜包装贴标秤** `clean-veg-packaging-labeler/*` ⚠ | `vegCutType`/`cutSpec` 字段名不一致 + 3 种字符串格式 | 字段名统一为 `cutSpec`(结构化对象);字符串格式统一通过 `formatCutSpec()` 生成 |
|
||||
| **包装标签** `sup-pkg-label` | `packSpec` 纯整数 | 改为 `baseGram+unit` 结构;`portions` 改为 `packSize`;**与设备端口径统一** |
|
||||
| **净菜组配** `prod-proc-combo` | 硬编码规格下拉 | 从餐品总库 L4 动态拉取 `specName+baseGram` |
|
||||
| **净配** `prod-proc-combo` | 硬编码规格下拉 | 从餐品总库 L4 动态拉取 `specName+baseGram` |
|
||||
| **按单烹制** `prod-proc-cook` | 规格只读 + 硬编码 | 规格从 L4 带出,生熟比关联 L2 出净率 |
|
||||
| **营养质控** `prod-qc-nutrition*` ⚠ | 同步入口未落地 | 落地同步逻辑:把检验均值写入餐品总库 specs 的营养规格字段 |
|
||||
| **加工/经营分析** `sup-process-analysis` / `sup-biz-analysis` ⚠ | 切配/包装分类各自硬编码;重量用 kg/吨不一致 | 切配分类引用"切配类型字典";包装分类独立字典;**展示单位统一格式化**(存储 g,展示 kg/吨自动转换) |
|
||||
@@ -908,7 +908,7 @@ vegTypes: [
|
||||
3. 新页面涉及"食材→净菜类型"级联时,直接从 `din-ingredient.vegTypes` 读取
|
||||
|
||||
### 阶段三:旧页面逐步改造
|
||||
1. 按 `食材档案/字典 → 餐品总库 → 采供/入库 → 订单/组配 → 设备端` 顺序逐页面改造
|
||||
1. 按 `食材档案/字典 → 餐品总库 → 采供/入库 → 订单/净配 → 设备端` 顺序逐页面改造
|
||||
2. 每个页面改造时,同步更新 Mock 数据结构,删除局部硬编码字典:
|
||||
- `cpSpecOptions`(prod-clean-order)
|
||||
- `UNIT_OPTIONS`(raw-veg-inspection-scale)
|
||||
|
||||
@@ -195,7 +195,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<table>
|
||||
<thead><tr><th>设备名称</th><th>设备编号</th><th>设备类型</th><th>业务场景</th><th>绑定位置</th><th>在线状态</th><th>最近活跃</th><th>当前版本</th><th>最近异常</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>智能组配秤 01</td><td>CMB-SCALE-001</td><td>净菜组配秤</td><td>净菜组配</td><td>后厨组配区</td><td><span class="tag tag-green">在线</span></td><td>2026-06-24 09:17:42</td><td>v1.8.2</td><td>无</td><td class="td-actions"><a class="link" onclick="showToast('设备详情原型待补充')">查看详情</a></td></tr>
|
||||
<tr><td>智能净配秤 01</td><td>CMB-SCALE-001</td><td>净配秤</td><td>净配</td><td>后厨净配区</td><td><span class="tag tag-green">在线</span></td><td>2026-06-24 09:17:42</td><td>v1.8.2</td><td>无</td><td class="td-actions"><a class="link" onclick="showToast('设备详情原型待补充')">查看详情</a></td></tr>
|
||||
<tr><td>餐线营养秤 02</td><td>LINE-SCALE-002</td><td>餐线营养秤</td><td>上餐监控</td><td>一号餐线</td><td><span class="tag tag-orange">离线</span></td><td>2026-06-24 08:42:18</td><td>v1.7.6</td><td>离线 35 分钟</td><td class="td-actions"><a class="link" href="edge-alert-center.html">查看告警</a></td></tr>
|
||||
<tr><td>人脸识别终端 03</td><td>FACE-TERM-003</td><td>人脸识别设备</td><td>就餐识别</td><td>入口闸机</td><td><span class="tag tag-green">在线</span></td><td>2026-06-24 09:17:55</td><td>v3.2.0</td><td>增量包待应用</td><td class="td-actions"><a class="link" href="edge-dispatch-detail.html">查看任务</a></td></tr>
|
||||
</tbody>
|
||||
|
||||
@@ -228,7 +228,7 @@ tr.detail-row td.td-actions{background:#FAFBFC;box-shadow:-4px 0 8px rgba(0,0,0,
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item active"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
@@ -368,14 +368,14 @@ tr.detail-row td.td-actions{background:#FAFBFC;box-shadow:-4px 0 8px rgba(0,0,0,
|
||||
<div class="drawer-section__title">餐品明细(重复餐品已自动合并,请逐道确认是否需要内配调料)</div>
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="inline-builder-table" id="dishSeasoningTable" style="margin-bottom:0;">
|
||||
<thead><tr><th style="width:34%;">菜品名称</th><th>包装规格</th><th>内配调料</th></tr></thead>
|
||||
<thead><tr><th style="width:24%;">菜品名称</th><th>份数</th><th>内配调料</th></tr></thead>
|
||||
<tbody id="dishSeasoningBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="recipe-tip">
|
||||
<span class="recipe-tip__icon">⏳</span>
|
||||
<span class="recipe-tip__text"><b>系统智能核算提示:</b>订单提交后,系统将依据您填写的各餐次就餐人数自动核算采购量并生成净菜明细,处理过程约需 1~2 分钟。生成结果与最终订单金额将在 <b>订单列表</b> 中实时更新,请耐心等待并关注订单状态变化。</span>
|
||||
<span class="recipe-tip__text"><b>系统智能核算提示:</b>订单提交后,系统将依据您填写的各餐次就餐人数自动核算采购量并生成净菜明细,处理过程约需 1~2 分钟。生成结果与最终订单金额将在 <b>订单列表</b> 中实时更新,请耐心等待并关注订单状态变化。<br><br>系统默认最大包装克重为4kg/份,若预测生重 ➗ 份数 > 4kg,则会默认按照最大包装克重反算份数。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1357,8 +1357,8 @@ function onRecipeDateChange(){
|
||||
/* ========== 餐品调料配置表(食谱快速创建专用) ========== */
|
||||
/* recipeSeasoningMap: { 菜名: true|false },跨餐次重复菜品自动合并,状态在重渲时保留 */
|
||||
var recipeSeasoningMap={};
|
||||
/* recipeSpecMap: { 菜名: {baseGram, portion, unit} },餐品明细表包装规格编辑值,重渲时保留 */
|
||||
var recipeSpecMap={};
|
||||
/* recipeServingMap: { 菜名: 份数 },餐品明细表份数编辑值,重渲时保留 */
|
||||
var recipeServingMap={};
|
||||
function renderDishSeasoningTable(){
|
||||
var section=document.getElementById('dishSeasoningSection');
|
||||
var tbody=document.getElementById('dishSeasoningBody');
|
||||
@@ -1366,15 +1366,18 @@ function renderDishSeasoningTable(){
|
||||
var dateVal=document.getElementById('recipeDate').value;
|
||||
var daily=dateVal?getDailyMenu(dateVal):null;
|
||||
if(!daily){section.style.display='none';tbody.innerHTML='';return;}
|
||||
/* 收集所有已勾选餐次的菜品,按菜名去重 */
|
||||
/* 收集所有已勾选餐次的菜品,按菜名去重并累加就餐人数 */
|
||||
var checkedMealIndices=[];
|
||||
document.querySelectorAll('#dailyMenuBody .meal-cb:checked').forEach(function(cb){checkedMealIndices.push(parseInt(cb.value));});
|
||||
var dishSet={};
|
||||
var dishDinerMap={};
|
||||
var orderedNames=[];
|
||||
checkedMealIndices.forEach(function(mi){
|
||||
var input=document.querySelector('#dailyMenuBody .meal-diner-input[data-meal-idx="'+mi+'"]');
|
||||
var dinerCount=parseInt(input&&input.value.trim(),10)||0;
|
||||
var dayDishes=daily.meals[mi]&&daily.meals[mi][daily.dayIdx]||[];
|
||||
dayDishes.forEach(function(name){
|
||||
if(!dishSet[name]){dishSet[name]=true;orderedNames.push(name);}
|
||||
if(!(name in dishDinerMap)){dishDinerMap[name]=0;orderedNames.push(name);}
|
||||
dishDinerMap[name]+=dinerCount;
|
||||
});
|
||||
});
|
||||
if(orderedNames.length===0){
|
||||
@@ -1388,17 +1391,22 @@ function renderDishSeasoningTable(){
|
||||
/* 未配置过的菜品默认 false(不需要);已配置的保留 */
|
||||
if(typeof recipeSeasoningMap[name]!=='boolean')recipeSeasoningMap[name]=false;
|
||||
var ns=recipeSeasoningMap[name]===true;
|
||||
/* 未编辑过包装规格的菜品,用兜底规格初始化 */
|
||||
if(!recipeSpecMap[name]){
|
||||
var sp=getDishSpecForRecipe(name);
|
||||
recipeSpecMap[name]={baseGram:sp.baseGram,unit:sp.unit||'袋'};
|
||||
/* 每份生重(克)取系统默认规格;就餐人数为该菜品跨餐次累加值 */
|
||||
var sp=getDishSpecForRecipe(name);
|
||||
var baseGram=sp.baseGram||500;
|
||||
var dinerCount=dishDinerMap[name]||0;
|
||||
/* 预测生重(总,克)= 每份生重 × 就餐人数 */
|
||||
var predictedRawG=baseGram*dinerCount;
|
||||
/* 份数:每份生重>4kg 时按最大包装克重 4kg/份 反算,否则取就餐人数 */
|
||||
if(!recipeServingMap[name]){
|
||||
recipeServingMap[name]=baseGram>4000?Math.ceil(predictedRawG/4000):dinerCount;
|
||||
}
|
||||
var spec=recipeSpecMap[name];
|
||||
var servings=recipeServingMap[name];
|
||||
html+='<tr><td style="font-weight:500;">'+name+'</td>';
|
||||
/* 包装规格:左 input 克数 + 右 select 包装单位 */
|
||||
html+='<td><div class="spec-input-group">'
|
||||
+'<input inputmode="numeric" placeholder="克数" value="'+(spec.baseGram||'')+'" oninput="this.value=this.value.replace(/[^\\d]/g,\'\');updateRecipeSpecGram(\''+name.replace(/'/g,'\\\'')+'\',this.value)">'
|
||||
+'<select onchange="updateRecipeSpecUnit(\''+name.replace(/'/g,'\\\'')+'\',this.value)">'+buildUnitOptions(spec.unit)+'</select>'
|
||||
/* 份数:固定单位「份」,输入框编辑份数 */
|
||||
html+='<td><div class="spec-input-group" style="justify-content:center;">'
|
||||
+'<input inputmode="numeric" placeholder="份数" value="'+servings+'" oninput="this.value=this.value.replace(/[^\\d]/g,\'\');updateRecipeServing(\''+name.replace(/'/g,'\\\'')+'\',this.value)" style="width:56px;">'
|
||||
+'<span style="color:var(--text-secondary);font-size:var(--font-sm);white-space:nowrap;">份</span>'
|
||||
+'</div></td>';
|
||||
html+='<td><div class="seasoning-radio-group">';
|
||||
html+='<label><input type="radio" name="rs-'+encodeURIComponent(name)+'" '+(ns?'checked':'')+' onchange="updateRecipeSeasoning(\''+name.replace(/'/g,'\\\'')+'\',true)"> 是</label>';
|
||||
@@ -1410,15 +1418,9 @@ function renderDishSeasoningTable(){
|
||||
function updateRecipeSeasoning(name,val){
|
||||
recipeSeasoningMap[name]=!!val;
|
||||
}
|
||||
/* 更新食谱餐品包装规格克数 */
|
||||
function updateRecipeSpecGram(name,val){
|
||||
if(!recipeSpecMap[name])recipeSpecMap[name]={baseGram:0,unit:'袋'};
|
||||
recipeSpecMap[name].baseGram=parseInt(val,10)||0;
|
||||
}
|
||||
/* 更新食谱餐品包装规格单位 */
|
||||
function updateRecipeSpecUnit(name,val){
|
||||
if(!recipeSpecMap[name])recipeSpecMap[name]={baseGram:0,unit:'袋'};
|
||||
recipeSpecMap[name].unit=val;
|
||||
/* 更新食谱餐品份数 */
|
||||
function updateRecipeServing(name,val){
|
||||
recipeServingMap[name]=parseInt(val,10)||0;
|
||||
}
|
||||
|
||||
/* 校验所选餐次是否都已填写就餐人数,并返回缺失餐次名 */
|
||||
@@ -1463,18 +1465,17 @@ function submitRecipeOrder(){
|
||||
for(var name in dishMap){
|
||||
var item=dishMap[name];
|
||||
var sp=item.spec;
|
||||
/* 优先使用餐品明细表中编辑过的包装规格,未编辑则回退默认规格 */
|
||||
var edited=recipeSpecMap[name];
|
||||
var baseGram=edited?edited.baseGram:sp.baseGram;
|
||||
var unit=edited?(edited.unit||sp.unit):sp.unit;
|
||||
var servings=item.dinerCount;
|
||||
/* 包装规格固定为「份」,份数优先取餐品明细表编辑值,未编辑回退就餐人数 */
|
||||
var baseGram=sp.baseGram||500;
|
||||
var unit='份';
|
||||
var servings=recipeServingMap[name]||item.dinerCount;
|
||||
newLines.push({
|
||||
id:'L'+(Date.now()+di),
|
||||
ncType:'cp',foodName:name,
|
||||
spec:baseGram+'g/'+unit,
|
||||
portion:sp.portion,baseGram:baseGram,unit:unit,
|
||||
qty:servings,
|
||||
weight:parseFloat((baseGram*servings/1000).toFixed(2)),
|
||||
weight:parseFloat((baseGram*item.dinerCount/1000).toFixed(2)),
|
||||
status:'生成中',
|
||||
needSeasoning:recipeSeasoningMap[name]===true
|
||||
});
|
||||
@@ -1505,7 +1506,7 @@ function sanitizeIntegerInput(el){el.value=el.value.replace(/[^\d]/g,'');}
|
||||
document.getElementById('recipeDeliveryTime').value=getDefaultDeliveryTime();
|
||||
document.getElementById('dailyMenuSection').style.display='none';
|
||||
recipeSeasoningMap={};
|
||||
recipeSpecMap={};
|
||||
recipeServingMap={};
|
||||
onRecipeDateChange();
|
||||
}
|
||||
document.getElementById(id).classList.add('show');
|
||||
|
||||
@@ -232,7 +232,7 @@ tbody tr.row-active td.td-actions{background:var(--primary-light);}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item active"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>营养管理 - 净菜组配 | 健康CQ原型</title>
|
||||
<title>营养管理 - 净配 | 健康CQ原型</title>
|
||||
<style>
|
||||
/* 设计令牌 */
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
@@ -154,7 +154,7 @@ tr.order-last td{border-bottom:2px solid #d9d9d9;}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item active"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item active"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
@@ -166,7 +166,7 @@ tr.order-last td{border-bottom:2px solid #d9d9d9;}
|
||||
<div class="filter-item"><label>当前场所</label><select class="filter-select"><option selected>CQ一场所</option><option>CQ二场所</option><option>CQ三场所</option></select></div>
|
||||
<div class="filter-item"><label>任务编号</label><input class="filter-input" type="text" placeholder="请输入任务编号"></div>
|
||||
<div class="filter-item"><label>目标菜品</label><input class="filter-input" type="text" placeholder="请输入目标菜品"></div>
|
||||
<div class="filter-item"><label>状态</label><select class="filter-select"><option>全部</option><option>已完成</option><option>组配中</option><option>待组配</option></select></div>
|
||||
<div class="filter-item"><label>状态</label><select class="filter-select"><option>全部</option><option>已完成</option><option>净配中</option><option>待净配</option></select></div>
|
||||
<div class="filter-item"><label>任务来源</label><select class="filter-select"><option>全部</option><option>录入</option><option>点餐</option><option>餐线</option></select></div>
|
||||
<div class="filter-item"><label>任务截止日期</label><input class="filter-input" id="filterDeadline" type="date" style="min-width:160px"></div>
|
||||
<button class="btn btn-primary">查询</button>
|
||||
@@ -174,27 +174,27 @@ tr.order-last td{border-bottom:2px solid #d9d9d9;}
|
||||
</div>
|
||||
|
||||
<div class="action-bar">
|
||||
<button class="btn btn-primary" onclick="openModal()">新建组配任务</button>
|
||||
<button class="btn btn-primary" onclick="openModal()">新建净配任务</button>
|
||||
</div>
|
||||
|
||||
<!-- 原型备注区域(仅供内部参考,不交付) -->
|
||||
<div class="dev-note" style="margin:12px 0;padding:12px 16px;background:#fffbe6;border:2px dashed #faad14;border-radius:6px;font-size:13px;color:#874d00;line-height:1.8;">
|
||||
<div style="font-weight:600;margin-bottom:4px;">📌 原型备注区域(不包含在交付效果内)</div>
|
||||
<div>净菜组配和按单烹制绝大多数场景下是一一对应的关系,服务端需要在生成按单烹制任务的同时生成净菜组配任务并做关联处理;组配任务中具体使用到的净菜溯源码可在编辑或食材明细弹窗中进行补全。食材明细根据餐品规则自动计算。</div>
|
||||
<div>净配和按单烹制绝大多数场景下是一一对应的关系,服务端需要在生成按单烹制任务的同时生成净配任务并做关联处理;净配任务中具体使用到的净菜溯源码可在编辑或食材明细弹窗中进行补全。食材明细根据餐品规则自动计算。</div>
|
||||
</div>
|
||||
|
||||
<div class="table-card">
|
||||
<div class="table-card__header"><div class="table-card__title">净菜组配</div></div>
|
||||
<div class="table-card__header"><div class="table-card__title">净配</div></div>
|
||||
<div class="table-card__content">
|
||||
<table>
|
||||
<thead><tr><th>任务编号</th><th>关联订单</th><th>目标餐品</th><th>餐品规格</th><th>份数</th><th>任务截止时间</th><th>食材配方</th><th>任务来源</th><th>组配状态</th><th>负责人</th><th>操作类型</th><th>终端设备</th><th>完成时间</th><th>操作</th></tr></thead>
|
||||
<thead><tr><th>任务编号</th><th>关联订单</th><th>目标餐品</th><th>餐品规格</th><th>份数</th><th>任务截止时间</th><th>食材配方</th><th>任务来源</th><th>净配状态</th><th>负责人</th><th>操作类型</th><th>终端设备</th><th>完成时间</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr data-order="CO-001"><td>CMB-001</td><td><span class="link">CO-001</span></td><td>宫保鸡丁</td><td>标准份(300g)</td><td>50</td><td>2026-05-20 11:00</td><td><a class="link" onclick="openRecipeModal('宫保鸡丁')">食材明细</a></td><td><span class="tag tag-blue">餐线</span></td><td><span class="tag tag-green">已完成</span></td><td>李组配</td><td><span class="tag tag-blue">设备自动</span></td><td><div>智能组配秤</div><div style="font-size:var(--font-xs);color:var(--text-secondary);">CMB-SCALE-001</div></td><td>2026-05-20 10:30:00</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span></td></tr>
|
||||
<tr data-order="CO-001" class="order-last"><td>CMB-002</td><td><span class="link">CO-001</span></td><td>番茄炒蛋</td><td>大份(500g)</td><td>80</td><td>2026-05-20 11:00</td><td><a class="link" onclick="openRecipeModal('番茄炒蛋')">食材明细</a></td><td><span class="tag tag-blue">餐线</span></td><td><span class="tag tag-green">已完成</span></td><td>王组配</td><td><span class="tag tag-blue">设备自动</span></td><td><div>智能组配秤</div><div style="font-size:var(--font-xs);color:var(--text-secondary);">CMB-SCALE-002</div></td><td>2026-05-20 10:45:00</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span></td></tr>
|
||||
<tr data-order="CO-002"><td>CMB-003</td><td><span class="link">CO-002</span></td><td>红烧肉</td><td>标准份(300g)</td><td>60</td><td>2026-05-20 11:30</td><td><a class="link" onclick="openRecipeModal('红烧肉')">食材明细</a></td><td><span class="tag tag-blue">餐线</span></td><td><span class="tag tag-blue">组配中</span></td><td>张组配</td><td><span class="tag tag-green">人工录入</span></td><td>—</td><td>—</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span> <a class="link" style="color:var(--success)" onclick="finishCombo(this)">完成组配</a></td></tr>
|
||||
<tr data-order="CO-002" class="order-last"><td>CMB-004</td><td><span class="link">CO-002</span></td><td>清蒸鱼</td><td>标准份(500g)</td><td>40</td><td>2026-05-20 12:00</td><td><a class="link" onclick="openRecipeModal('清蒸鱼')">食材明细</a></td><td><span class="tag tag-orange">点餐</span></td><td><span class="tag tag-orange">待组配</span></td><td>—</td><td><span class="tag tag-green">人工录入</span></td><td>—</td><td>—</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span> <a class="link" onclick="startCombo(this)">开始组配</a></td></tr>
|
||||
<tr data-order="CO-003"><td>CMB-005</td><td><span class="link">CO-003</span></td><td>炒时蔬</td><td>小份(200g)</td><td>20</td><td>2026-05-20 11:00</td><td><a class="link" onclick="openRecipeModal('炒时蔬')">食材明细</a></td><td><span class="tag tag-gray">录入</span></td><td><span class="tag tag-green">已完成</span></td><td>赵组配</td><td><span class="tag tag-blue">设备自动</span></td><td><div>智能组配秤</div><div style="font-size:var(--font-xs);color:var(--text-secondary);">CMB-SCALE-003</div></td><td>2026-05-20 10:50:00</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span></td></tr>
|
||||
<tr data-order="CO-004"><td>CMB-006</td><td><span class="link">CO-004</span></td><td>豆腐</td><td>小份(200g)</td><td>35</td><td>2026-05-20 12:00</td><td><a class="link" onclick="openRecipeModal('豆腐')">食材明细</a></td><td><span class="tag tag-orange">点餐</span></td><td><span class="tag tag-orange">待组配</span></td><td>—</td><td><span class="tag tag-green">人工录入</span></td><td>—</td><td>—</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span> <a class="link" onclick="startCombo(this)">开始组配</a></td></tr>
|
||||
<tr data-order="CO-001"><td>CMB-001</td><td><span class="link">CO-001</span></td><td>宫保鸡丁</td><td>标准份(300g)</td><td>50</td><td>2026-05-20 11:00</td><td><a class="link" onclick="openRecipeModal('宫保鸡丁')">食材明细</a></td><td><span class="tag tag-blue">餐线</span></td><td><span class="tag tag-green">已完成</span></td><td>李净配</td><td><span class="tag tag-blue">设备自动</span></td><td><div>智能净配秤</div><div style="font-size:var(--font-xs);color:var(--text-secondary);">CMB-SCALE-001</div></td><td>2026-05-20 10:30:00</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span></td></tr>
|
||||
<tr data-order="CO-001" class="order-last"><td>CMB-002</td><td><span class="link">CO-001</span></td><td>番茄炒蛋</td><td>大份(500g)</td><td>80</td><td>2026-05-20 11:00</td><td><a class="link" onclick="openRecipeModal('番茄炒蛋')">食材明细</a></td><td><span class="tag tag-blue">餐线</span></td><td><span class="tag tag-green">已完成</span></td><td>王净配</td><td><span class="tag tag-blue">设备自动</span></td><td><div>智能净配秤</div><div style="font-size:var(--font-xs);color:var(--text-secondary);">CMB-SCALE-002</div></td><td>2026-05-20 10:45:00</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span></td></tr>
|
||||
<tr data-order="CO-002"><td>CMB-003</td><td><span class="link">CO-002</span></td><td>红烧肉</td><td>标准份(300g)</td><td>60</td><td>2026-05-20 11:30</td><td><a class="link" onclick="openRecipeModal('红烧肉')">食材明细</a></td><td><span class="tag tag-blue">餐线</span></td><td><span class="tag tag-blue">净配中</span></td><td>张净配</td><td><span class="tag tag-green">人工录入</span></td><td>—</td><td>—</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span> <a class="link" style="color:var(--success)" onclick="finishCombo(this)">完成净配</a></td></tr>
|
||||
<tr data-order="CO-002" class="order-last"><td>CMB-004</td><td><span class="link">CO-002</span></td><td>清蒸鱼</td><td>标准份(500g)</td><td>40</td><td>2026-05-20 12:00</td><td><a class="link" onclick="openRecipeModal('清蒸鱼')">食材明细</a></td><td><span class="tag tag-orange">点餐</span></td><td><span class="tag tag-orange">待净配</span></td><td>—</td><td><span class="tag tag-green">人工录入</span></td><td>—</td><td>—</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span> <a class="link" onclick="startCombo(this)">开始净配</a></td></tr>
|
||||
<tr data-order="CO-003"><td>CMB-005</td><td><span class="link">CO-003</span></td><td>炒时蔬</td><td>小份(200g)</td><td>20</td><td>2026-05-20 11:00</td><td><a class="link" onclick="openRecipeModal('炒时蔬')">食材明细</a></td><td><span class="tag tag-gray">录入</span></td><td><span class="tag tag-green">已完成</span></td><td>赵净配</td><td><span class="tag tag-blue">设备自动</span></td><td><div>智能净配秤</div><div style="font-size:var(--font-xs);color:var(--text-secondary);">CMB-SCALE-003</div></td><td>2026-05-20 10:50:00</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span></td></tr>
|
||||
<tr data-order="CO-004"><td>CMB-006</td><td><span class="link">CO-004</span></td><td>豆腐</td><td>小份(200g)</td><td>35</td><td>2026-05-20 12:00</td><td><a class="link" onclick="openRecipeModal('豆腐')">食材明细</a></td><td><span class="tag tag-orange">点餐</span></td><td><span class="tag tag-orange">待净配</span></td><td>—</td><td><span class="tag tag-green">人工录入</span></td><td>—</td><td>—</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span> <a class="link" onclick="startCombo(this)">开始净配</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -212,7 +212,7 @@ tr.order-last td{border-bottom:2px solid #d9d9d9;}
|
||||
<div class="modal-overlay" id="modalOverlay" style="display:none;">
|
||||
<div class="modal" style="width:680px;">
|
||||
<div class="modal-header">
|
||||
<span class="modal-title" id="modalTitle">新建组配任务</span>
|
||||
<span class="modal-title" id="modalTitle">新建净配任务</span>
|
||||
<span class="modal-close" onclick="closeModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@@ -309,7 +309,7 @@ function getAllDishNames(){
|
||||
var dishes=loadDishSpecs();
|
||||
return dishes?dishes.map(function(d){return d.name;}):[];
|
||||
}
|
||||
/* 组配页兜底:总库未覆盖时的页内默认规格 */
|
||||
/* 净配页兜底:总库未覆盖时的页内默认规格 */
|
||||
var COMBO_FALLBACK_DISHES={
|
||||
'宫保鸡丁':[{portion:'标准份',baseGram:300,unit:'份'}],
|
||||
'番茄炒蛋':[{portion:'标准份',baseGram:300,unit:'份'}],
|
||||
@@ -414,7 +414,7 @@ var _editRow=null;
|
||||
function openModal(){
|
||||
_modalMode='create';
|
||||
_editRow=null;
|
||||
document.getElementById('modalTitle').textContent='新建组配任务';
|
||||
document.getElementById('modalTitle').textContent='新建净配任务';
|
||||
document.getElementById('modalSubmitBtn').textContent='确定';
|
||||
// 重置表单
|
||||
document.getElementById('formTaskId').value='';
|
||||
@@ -430,7 +430,7 @@ function openModal(){
|
||||
document.getElementById('modalOverlay').style.display='flex';
|
||||
}
|
||||
|
||||
/* 填充组配页餐品下拉 */
|
||||
/* 填充净配页餐品下拉 */
|
||||
function populateComboDishOptions(selected){
|
||||
var sel=document.getElementById('createDish');
|
||||
var names=getDishNamesWithFallback();
|
||||
@@ -443,7 +443,7 @@ function openEditModal(el){
|
||||
_modalMode='edit';
|
||||
_editRow=el.closest('tr');
|
||||
var tds=_editRow.querySelectorAll('td');
|
||||
document.getElementById('modalTitle').textContent='编辑组配任务';
|
||||
document.getElementById('modalTitle').textContent='编辑净配任务';
|
||||
document.getElementById('modalSubmitBtn').textContent='保存';
|
||||
// 回填数据
|
||||
document.getElementById('formTaskId').value=tds[0].textContent;
|
||||
@@ -494,7 +494,7 @@ function submitModal(){
|
||||
showToast('✅ 任务信息已更新');
|
||||
} else {
|
||||
closeModal();
|
||||
showToast('✅ 组配任务创建成功');
|
||||
showToast('✅ 净配任务创建成功');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,19 +582,19 @@ function showToast(msg){
|
||||
_toastTimer=setTimeout(function(){el.classList.remove('show');},2000);
|
||||
}
|
||||
|
||||
/* ========== 开始组配 ========== */
|
||||
/* ========== 开始净配 ========== */
|
||||
function startCombo(el){
|
||||
var tr=el.closest('tr');
|
||||
var tds=tr.querySelectorAll('td');
|
||||
var taskId=tds[0].textContent;
|
||||
tds[8].innerHTML='<span class="tag tag-blue">组配中</span>';
|
||||
tds[8].innerHTML='<span class="tag tag-blue">净配中</span>';
|
||||
el.style.color='var(--success)';
|
||||
el.setAttribute('onclick','finishCombo(this)');
|
||||
el.textContent='完成组配';
|
||||
showToast('✅ '+taskId+' 已开始组配');
|
||||
el.textContent='完成净配';
|
||||
showToast('✅ '+taskId+' 已开始净配');
|
||||
}
|
||||
|
||||
/* ========== 完成组配 ========== */
|
||||
/* ========== 完成净配 ========== */
|
||||
function finishCombo(el){
|
||||
var tr=el.closest('tr');
|
||||
var tds=tr.querySelectorAll('td');
|
||||
@@ -604,7 +604,7 @@ function finishCombo(el){
|
||||
var ts=now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-'+String(now.getDate()).padStart(2,'0')+' '+String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0')+':'+String(now.getSeconds()).padStart(2,'0');
|
||||
tds[12].textContent=ts;
|
||||
el.remove();
|
||||
showToast('✅ '+taskId+' 组配已完成');
|
||||
showToast('✅ '+taskId+' 净配已完成');
|
||||
}
|
||||
|
||||
/* ========== 详情抽屉(含食材明细) ========== */
|
||||
@@ -629,7 +629,7 @@ function showDetail(el){
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">份数</div><div class="drawer-field__value">'+qty+'</div></div>';
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">任务截止时间</div><div class="drawer-field__value">'+deadline+'</div></div>';
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">任务来源</div><div class="drawer-field__value">'+source+'</div></div>';
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">组配状态</div><div class="drawer-field__value">'+status+'</div></div>';
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">净配状态</div><div class="drawer-field__value">'+status+'</div></div>';
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">负责人</div><div class="drawer-field__value">'+person+'</div></div>';
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">操作类型</div><div class="drawer-field__value">'+opType+'</div></div>';
|
||||
html+='<div class="drawer-field"><div class="drawer-field__label">完成时间</div><div class="drawer-field__value">'+finishTime+'</div></div>';
|
||||
@@ -649,7 +649,7 @@ function showDetail(el){
|
||||
}
|
||||
html+='</tbody></table></div></div>';
|
||||
|
||||
Drawer.open('组配详情 — '+taskId,html);
|
||||
Drawer.open('净配详情 — '+taskId,html);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ tr.order-last td{border-bottom:2px solid #d9d9d9;}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item active"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
@@ -200,7 +200,7 @@ tr.order-last td{border-bottom:2px solid #d9d9d9;}
|
||||
<div class="table-card__header"><div class="table-card__title">按单烹制</div></div>
|
||||
<div class="table-card__content">
|
||||
<table>
|
||||
<thead><tr><th>烹制任务号</th><th>关联订单</th><th>来源类型</th><th>组配任务号</th><th>菜品名称</th><th>餐品规格</th><th>需求份数</th><th>烹制份数</th><th>烹制日期</th><th>烹制开始</th><th>烹制结束</th><th>烹制状态</th><th>厨师</th><th>食材明细</th><th>温度过程</th><th>模型数据</th><th>生重(kg)</th><th>熟重(kg)</th><th>生熟比</th><th>操作</th></tr></thead>
|
||||
<thead><tr><th>烹制任务号</th><th>关联订单</th><th>来源类型</th><th>净配任务号</th><th>菜品名称</th><th>餐品规格</th><th>需求份数</th><th>烹制份数</th><th>烹制日期</th><th>烹制开始</th><th>烹制结束</th><th>烹制状态</th><th>厨师</th><th>食材明细</th><th>温度过程</th><th>模型数据</th><th>生重(kg)</th><th>熟重(kg)</th><th>生熟比</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr data-order="CO-001"><td>COOK-001</td><td><span class="link" onclick="showCookOrderDetail('CO-001')">CO-001</span><span class="tag tag-green" style="margin-left:4px;">✓ 已烹齐</span></td><td><span class="tag tag-blue">点餐</span></td><td>CMB-001</td><td>宫保鸡丁</td><td>标准份(300g)</td><td>50</td><td>50</td><td>2026-05-20</td><td>11:00</td><td>11:20</td><td><span class="tag tag-green">已完成</span></td><td>张厨师</td><td><a class="link" onclick="openIngredientModal('宫保鸡丁')">食材明细</a></td><td><a class="link" onclick="openTempModal()">温度曲线</a></td><td><a class="link" onclick="openModelModal()"><span class="tag tag-green">92分</span></a></td><td>8.5</td><td>7.2</td><td>1:0.85</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span><span class="link" onclick="notifyPickup(this)">通知取餐</span></td></tr>
|
||||
<tr data-order="CO-001" class="order-last"><td>COOK-002</td><td><span class="link" onclick="showCookOrderDetail('CO-001')">CO-001</span></td><td><span class="tag tag-blue">点餐</span></td><td>CMB-002</td><td>番茄炒蛋</td><td>大份(500g)</td><td>80</td><td>80</td><td>2026-05-20</td><td>11:05</td><td>11:20</td><td><span class="tag tag-green">已完成</span></td><td>李厨师</td><td><a class="link" onclick="openIngredientModal('番茄炒蛋')">食材明细</a></td><td><a class="link" onclick="openTempModal()">温度曲线</a></td><td><a class="link" onclick="openModelModal()"><span class="tag tag-green">88分</span></a></td><td>12.0</td><td>10.4</td><td>1:0.87</td><td class="td-actions"><a class="link">食材溯源链路</a> <a class="link">查看视频</a> <span class="action-more"><a class="action-more__trigger">更多 ▾</a><div class="action-more__menu"><a class="action-more__item" onclick="showDetail(this)">详情</a><a class="action-more__item" onclick="openEditModal(this)">编辑</a><a class="action-more__item action-more__item--danger">删除</a></div></span><span class="link" onclick="notifyPickup(this)">通知取餐</span></td></tr>
|
||||
@@ -284,7 +284,7 @@ tr.order-last td{border-bottom:2px solid #d9d9d9;}
|
||||
<div class="model-meta">
|
||||
<div class="model-meta__item"><span class="model-meta__label">菜品</span><span class="model-meta__value">宫保鸡丁</span></div>
|
||||
<div class="model-meta__item"><span class="model-meta__label">烹制任务号</span><span class="model-meta__value">COOK-001</span></div>
|
||||
<div class="model-meta__item"><span class="model-meta__label">来源组配</span><span class="model-meta__value">CMB-001</span></div>
|
||||
<div class="model-meta__item"><span class="model-meta__label">来源净配</span><span class="model-meta__value">CMB-001</span></div>
|
||||
<div class="model-meta__item"><span class="model-meta__label">厨师</span><span class="model-meta__value">张厨师</span></div>
|
||||
<div class="model-meta__item"><span class="model-meta__label">分析时间</span><span class="model-meta__value">2026-04-22 11:20</span></div>
|
||||
</div>
|
||||
|
||||
@@ -153,7 +153,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item active"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -111,7 +111,7 @@ tr.summary-row td{font-weight:700;background:#FAFAFA;}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -160,7 +160,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item active"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -245,7 +245,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -151,7 +151,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item active"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -106,7 +106,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item active"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -152,7 +152,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<a href="prod-recipe-plan.html" class="sidebar-item active"><span class="mi">📋</span>食谱规划</a>
|
||||
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
|
||||
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
|
||||
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净配</a>
|
||||
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
|
||||
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
|
||||
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
|
||||
|
||||
@@ -320,8 +320,8 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<tbody>
|
||||
<tr><td>PKG-T01</td><td>真空机-1号</td><td>A区净菜加工线</td><td>186</td><td>0</td><td>186</td><td>2026-04-15 11:45</td><td><span class="tag tag-green">在线</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
|
||||
<tr><td>PKG-T02</td><td>真空机-2号</td><td>B区净菜加工线</td><td>62</td><td>0</td><td>62</td><td>2026-04-15 11:30</td><td><span class="tag tag-green">在线</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
|
||||
<tr><td>PKG-T03</td><td>封口机-1号</td><td>A区组配工作台</td><td>0</td><td>86</td><td>86</td><td>2026-04-15 11:50</td><td><span class="tag tag-green">在线</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
|
||||
<tr><td>PKG-T04</td><td>封口机-2号</td><td>B区组配工作台</td><td>0</td><td>52</td><td>52</td><td>2026-04-15 11:20</td><td><span class="tag tag-gray">离线</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
|
||||
<tr><td>PKG-T03</td><td>封口机-1号</td><td>A区净配工作台</td><td>0</td><td>86</td><td>86</td><td>2026-04-15 11:50</td><td><span class="tag tag-green">在线</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
|
||||
<tr><td>PKG-T04</td><td>封口机-2号</td><td>B区净配工作台</td><td>0</td><td>52</td><td>52</td><td>2026-04-15 11:20</td><td><span class="tag tag-gray">离线</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -208,21 +208,21 @@
|
||||
var NODE_COLORS = {
|
||||
'采供入库':'#52C41A','卫生检测':'#1890FF','毛菜入库':'#13C2C2','毛菜存储':'#FA8C16',
|
||||
'净菜加工':'#52C41A','净菜入库':'#2F54EB','净菜内供':'#FA541C','净菜进柜':'#722ED1',
|
||||
'净菜组配':'#EB2F96','按单烹制':'#FAAD14','上餐监控':'#F5222D','余量处置':'#8C8C8C',
|
||||
'净配':'#EB2F96','按单烹制':'#FAAD14','上餐监控':'#F5222D','余量处置':'#8C8C8C',
|
||||
'营养检验':'#13C2C2','用户取餐':'#1890FF','种植采收':'#52C41A','农残检测':'#1890FF',
|
||||
'干货入库':'#13C2C2','干货存储':'#FA8C16','预处理':'#52C41A'
|
||||
};
|
||||
var NODE_ICONS = {
|
||||
'采供入库':'🛒','卫生检测':'🔬','毛菜入库':'📥','毛菜存储':'🏪',
|
||||
'净菜加工':'🥬','净菜入库':'📦','净菜内供':'🚚','净菜进柜':'🗄️',
|
||||
'净菜组配':'🔪','按单烹制':'🍳','上餐监控':'🍽️','余量处置':'♻️',
|
||||
'净配':'🔪','按单烹制':'🍳','上餐监控':'🍽️','余量处置':'♻️',
|
||||
'营养检验':'🧪','用户取餐':'👤','种植采收':'🌱','农残检测':'🔬',
|
||||
'干货入库':'📥','干货存储':'🏪','预处理':'🥬'
|
||||
};
|
||||
var NODE_ICON_BG = {
|
||||
'采供入库':'#F6FFED','卫生检测':'#E6F7FF','毛菜入库':'#E6FFFB','毛菜存储':'#FFF7E6',
|
||||
'净菜加工':'#F6FFED','净菜入库':'#F0F5FF','净菜内供':'#FFF2E8','净菜进柜':'#F9F0FF',
|
||||
'净菜组配':'#FFF0F6','按单烹制':'#FFF7E6','上餐监控':'#FFF1F0','余量处置':'#F5F5F5',
|
||||
'净配':'#FFF0F6','按单烹制':'#FFF7E6','上餐监控':'#FFF1F0','余量处置':'#F5F5F5',
|
||||
'营养检验':'#E6FFFB','用户取餐':'#E6F7FF','种植采收':'#F6FFED','农残检测':'#E6F7FF',
|
||||
'干货入库':'#E6FFFB','干货存储':'#FFF7E6','预处理':'#F6FFED'
|
||||
};
|
||||
@@ -240,7 +240,7 @@ var INGREDIENTS = [
|
||||
{stage:'净菜入库',time:'2026-04-22 08:30',title:'净菜冷柜暂存(3条分支)',trace:'NC-ST-20260422-001',detail:'分支A:切丁5kg → 净菜柜NC-01<br>分支B:切片3kg → 净菜柜NC-02<br>分支C:切条2kg → 净菜柜NC-03',badge:'x3',status:'ok',video:['净菜入库','2026-04-22 08:30','净菜柜-CAM07','00:01:50']},
|
||||
{stage:'净菜内供',time:'2026-04-22 09:30',title:'净菜领用出库(3条分支)',trace:'NC-OUT-20260422-001',detail:'分支A:切丁5kg → 后厨A组<br>分支B:切片3kg → 后厨B组<br>分支C:切条2kg → 后厨C组',badge:'x3',status:'ok',video:['净菜内供','2026-04-22 09:30','出库口-CAM08','00:02:00']},
|
||||
{stage:'净菜进柜',time:'2026-04-22 09:45',title:'后厨冷柜暂存(2条分支)',trace:'KC-IN-20260422-001',detail:'分支A:切丁5kg → 后厨冷柜KC-01<br>分支B:切片3kg → 后厨冷柜KC-02',badge:'x2',status:'ok',video:['净菜进柜','2026-04-22 09:45','后厨柜-CAM08','00:01:20']},
|
||||
{stage:'净菜组配',time:'2026-04-22 10:20',title:'菜品组配(2条分支)',trace:'CMB-20260422-001',detail:'分支A:CMB-001 宫保鸡丁 5.0kg<br>李组配<br>分支B:CMB-006 辣子鸡 3.5kg<br>张组配',badge:'x2',status:'ok',video:['净菜组配','2026-04-22 10:20','组配间-CAM09','00:02:40']},
|
||||
{stage:'净配',time:'2026-04-22 10:20',title:'菜品净配(2条分支)',trace:'CMB-20260422-001',detail:'分支A:CMB-001 宫保鸡丁 5.0kg<br>李净配<br>分支B:CMB-006 辣子鸡 3.5kg<br>张净配',badge:'x2',status:'ok',video:['净配','2026-04-22 10:20','净配间-CAM09','00:02:40']},
|
||||
{stage:'按单烹制',time:'2026-04-22 11:00 ~ 11:35',operator:'张厨师 等3人',title:'烹制任务 · 多锅次烹制(3条分支)',trace:'COOK-20260422-001',detail:'分支A:COOK-001(宫保鸡丁)张厨师 2锅<br>分支B:COOK-009(辣子鸡)王厨师 1锅<br>分支C:COOK-015(青椒鸡丁)李厨师 1锅',badge:'x3',status:'ok',video:['按单烹制','2026-04-22 11:00','后厨-CAM10','00:04:20']},
|
||||
{stage:'上餐监控',time:'2026-04-22 11:30 ~ 12:00',title:'上餐批次 · 多餐线分发(3条分支)',trace:'SERVE-20260422-001',detail:'分支A:SERVE-001 → A区中餐线 6.8kg<br>分支B:SERVE-007 → B区中餐线 1.4kg<br>分支C:SERVE-015 → C区中餐线 2.1kg',badge:'x3',status:'ok',video:['上餐监控','2026-04-22 11:30','餐线-CAM11','00:03:05']},
|
||||
{stage:'余量处置',time:'2026-04-22 13:00 ~ 13:20',title:'余量处置 · 多餐线余量登记(3条分支)',trace:'DISP-20260422-001',detail:'分支A:余量0.3kg → 冷藏留用<br>分支B:余量0.1kg → 废弃销毁<br>分支C:余量0.2kg → 废弃销毁<br>经办人:李处置',badge:'x3',status:'ok',video:['余量处置','2026-04-22 13:00','余量区-CAM12','00:01:30']},
|
||||
@@ -256,7 +256,7 @@ var INGREDIENTS = [
|
||||
{stage:'干货入库',time:'2026-04-14 15:30',title:'干货仓入库 · 花生仁',trace:'DG-IN-20260414-005',detail:'入库量:12kg<br>库位:DG-A03-02<br>保质期至:2026-10-14<br>存储温度:常温干燥<br>经办人:王仓管',badge:'',status:'ok',video:['干货入库','2026-04-14 15:30','干货仓-CAM03','00:01:20']},
|
||||
{stage:'干货存储',time:'2026-04-14 ~ 2026-04-22',title:'干货仓存储 · 温湿度监控',trace:'库位:DG-A03-02',detail:'存储天数:8天<br>温度范围:18~22℃<br>湿度范围:45~55%<br>无异常告警<br>剩余库存:9.6kg(已领用2.4kg)',badge:'',status:'ok',video:['干货存储','2026-04-20 08:00','干货仓-CAM03','00:01:00']},
|
||||
{stage:'预处理',time:'2026-04-22 08:30',title:'花生仁预处理 · 油炸',trace:'PP-HS-20260422-001',detail:'领用量:1.2kg<br>加工方式:油炸至金黄<br>油温:160℃<br>时长:3分钟<br>成品量:1.1kg<br>经办人:刘加工',badge:'',status:'ok',video:['预处理','2026-04-22 08:30','加工间-CAM05','00:02:00']},
|
||||
{stage:'净菜组配',time:'2026-04-22 10:15',title:'组配至宫保鸡丁',trace:'CMB-001-HS',detail:'配入菜品:宫保鸡丁<br>配比:1.1kg<br>经办人:李组配',badge:'',status:'ok',video:['净菜组配','2026-04-22 10:15','组配间-CAM09','00:01:30']},
|
||||
{stage:'净配',time:'2026-04-22 10:15',title:'净配至宫保鸡丁',trace:'CMB-001-HS',detail:'配入菜品:宫保鸡丁<br>净配:1.1kg<br>经办人:李净配',badge:'',status:'ok',video:['净配','2026-04-22 10:15','净配间-CAM09','00:01:30']},
|
||||
{stage:'按单烹制',time:'2026-04-22 11:00',title:'随宫保鸡丁烹制',trace:'COOK-20260422-001',detail:'烹制菜品:宫保鸡丁<br>厨师:张厨师<br>花生投入时机:出锅前30秒',badge:'',status:'ok',video:['按单烹制','2026-04-22 11:00','后厨-CAM10','00:04:20']},
|
||||
{stage:'上餐监控',time:'2026-04-22 11:30',title:'随宫保鸡丁上餐',trace:'SERVE-20260422-001',detail:'上餐线:A区中餐线<br>上餐温度:62℃<br>状态:正常',badge:'',status:'ok',video:['上餐监控','2026-04-22 11:30','餐线-CAM11','00:01:50']}
|
||||
]
|
||||
@@ -268,7 +268,7 @@ var INGREDIENTS = [
|
||||
{stage:'农残检测',time:'2026-04-20 08:00',title:'自种蔬菜农残快检',trace:'QC-SF-20260420-003',detail:'有机磷:未检出<br>氨基甲酸酯:未检出<br>抑制率:8.2%(合格线≤50%)<br>检测结论:<strong style="color:#52c41a">合格</strong>',badge:'',status:'ok',video:['农残检测','2026-04-20 08:00','检测室-CAM02','00:01:40']},
|
||||
{stage:'毛菜入库',time:'2026-04-20 09:00',title:'毛菜冷库入库',trace:'MC-IN-20260420-003',detail:'入库量:5kg<br>库位:MC-B02-01<br>存储温度:2~8℃<br>经办人:王仓管',badge:'',status:'ok',video:['毛菜入库','2026-04-20 09:00','冷库-CAM04','00:01:15']},
|
||||
{stage:'净菜加工',time:'2026-04-22 08:00',title:'辣椒清洗切段',trace:'NC-LJ-20260422-001',detail:'领用量:0.8kg<br>加工方式:去蒂→清洗→切段<br>成品量:0.75kg<br>经办人:刘加工',badge:'',status:'ok',video:['净菜加工','2026-04-22 08:00','净菜间-CAM06','00:01:50']},
|
||||
{stage:'净菜组配',time:'2026-04-22 10:15',title:'组配至宫保鸡丁',trace:'CMB-001-LJ',detail:'配入菜品:宫保鸡丁<br>配比:0.75kg<br>经办人:李组配',badge:'',status:'ok',video:['净菜组配','2026-04-22 10:15','组配间-CAM09','00:01:20']},
|
||||
{stage:'净配',time:'2026-04-22 10:15',title:'净配至宫保鸡丁',trace:'CMB-001-LJ',detail:'配入菜品:宫保鸡丁<br>净配:0.75kg<br>经办人:李净配',badge:'',status:'ok',video:['净配','2026-04-22 10:15','净配间-CAM09','00:01:20']},
|
||||
{stage:'按单烹制',time:'2026-04-22 11:00',title:'随宫保鸡丁烹制',trace:'COOK-20260422-001',detail:'烹制菜品:宫保鸡丁<br>厨师:张厨师<br>辣椒投入时机:爆香阶段',badge:'',status:'ok',video:['按单烹制','2026-04-22 11:00','后厨-CAM10','00:04:20']},
|
||||
{stage:'上餐监控',time:'2026-04-22 11:30',title:'随宫保鸡丁上餐',trace:'SERVE-20260422-001',detail:'上餐线:A区中餐线<br>上餐温度:62℃<br>状态:正常',badge:'',status:'ok',video:['上餐监控','2026-04-22 11:30','餐线-CAM11','00:01:50']}
|
||||
]
|
||||
@@ -279,7 +279,7 @@ var INGREDIENTS = [
|
||||
{stage:'种植采收',time:'2026-04-21 06:30',title:'自有农场采收 · 南郊基地',trace:'ST-SF-NJ01-SC-CJS-005K-20260421-001',detail:'品种:小葱/生姜/大蒜<br>地块:NJ-B01<br>采收量:葱0.2kg + 姜0.15kg + 蒜0.15kg<br>种植方式:有机种植<br>用药记录:无<br>采收人:孙农技',badge:'',status:'ok',video:['种植采收','2026-04-21 06:30','农场-CAM01','00:02:00']},
|
||||
{stage:'农残检测',time:'2026-04-21 07:30',title:'自种蔬菜农残快检',trace:'QC-SF-20260421-001',detail:'有机磷:未检出<br>氨基甲酸酯:未检出<br>抑制率:5.6%(合格线≤50%)<br>检测结论:<strong style="color:#52c41a">合格</strong>',badge:'',status:'ok',video:['农残检测','2026-04-21 07:30','检测室-CAM02','00:01:30']},
|
||||
{stage:'净菜加工',time:'2026-04-22 07:45',title:'葱姜蒜清洗切配',trace:'NC-CJS-20260422-001',detail:'葱:切葱花0.2kg<br>姜:切姜片0.15kg<br>蒜:切蒜片0.15kg<br>成品总量:0.48kg<br>经办人:刘加工',badge:'',status:'ok',video:['净菜加工','2026-04-22 07:45','净菜间-CAM06','00:01:40']},
|
||||
{stage:'净菜组配',time:'2026-04-22 10:15',title:'组配至宫保鸡丁',trace:'CMB-001-CJS',detail:'配入菜品:宫保鸡丁<br>配比:0.48kg<br>经办人:李组配',badge:'',status:'ok',video:['净菜组配','2026-04-22 10:15','组配间-CAM09','00:01:10']},
|
||||
{stage:'净配',time:'2026-04-22 10:15',title:'净配至宫保鸡丁',trace:'CMB-001-CJS',detail:'配入菜品:宫保鸡丁<br>净配:0.48kg<br>经办人:李净配',badge:'',status:'ok',video:['净配','2026-04-22 10:15','净配间-CAM09','00:01:10']},
|
||||
{stage:'按单烹制',time:'2026-04-22 11:00',title:'随宫保鸡丁烹制',trace:'COOK-20260422-001',detail:'烹制菜品:宫保鸡丁<br>厨师:张厨师<br>葱姜蒜投入时机:起锅爆香',badge:'',status:'ok',video:['按单烹制','2026-04-22 11:00','后厨-CAM10','00:04:20']},
|
||||
{stage:'上餐监控',time:'2026-04-22 11:30',title:'随宫保鸡丁上餐',trace:'SERVE-20260422-001',detail:'上餐线:A区中餐线<br>上餐温度:62℃<br>状态:正常',badge:'',status:'ok',video:['上餐监控','2026-04-22 11:30','餐线-CAM11','00:01:50']}
|
||||
]
|
||||
@@ -292,7 +292,7 @@ var INGREDIENT_EMOJIS = ['🥩','🥜','🌶️','🧄'];
|
||||
var STAGE_OPERATORS = {
|
||||
'采供入库':'赵采购','卫生检测':'陈检测','毛菜入库':'王仓管','毛菜存储':'系统监控',
|
||||
'净菜加工':'刘加工','净菜入库':'王仓管','净菜内供':'王仓管','净菜进柜':'王仓管',
|
||||
'净菜组配':'李组配','按单烹制':'张厨师','上餐监控':'系统记录','余量处置':'李处置',
|
||||
'净配':'李净配','按单烹制':'张厨师','上餐监控':'系统记录','余量处置':'李处置',
|
||||
'营养检验':'周营养师','用户取餐':'系统记录','种植采收':'孙农技','农残检测':'陈检测',
|
||||
'干货入库':'王仓管','干货存储':'系统监控','预处理':'刘加工'
|
||||
};
|
||||
|
||||
@@ -186,21 +186,21 @@
|
||||
var NODE_COLORS = {
|
||||
'采供入库':'#52C41A','卫生检测':'#1890FF','毛菜入库':'#13C2C2','毛菜存储':'#FA8C16',
|
||||
'净菜加工':'#52C41A','净菜入库':'#2F54EB','净菜内供':'#FA541C','净菜进柜':'#722ED1',
|
||||
'净菜组配':'#EB2F96','按单烹制':'#FAAD14','上餐监控':'#F5222D','余量处置':'#8C8C8C',
|
||||
'净配':'#EB2F96','按单烹制':'#FAAD14','上餐监控':'#F5222D','余量处置':'#8C8C8C',
|
||||
'营养检验':'#13C2C2','用户取餐':'#1890FF','种植采收':'#52C41A','农残检测':'#1890FF',
|
||||
'干货入库':'#13C2C2','干货存储':'#FA8C16','预处理':'#52C41A'
|
||||
};
|
||||
var NODE_ICONS = {
|
||||
'采供入库':'🛒','卫生检测':'🔬','毛菜入库':'📥','毛菜存储':'🏪',
|
||||
'净菜加工':'🥬','净菜入库':'📦','净菜内供':'🚚','净菜进柜':'🗄️',
|
||||
'净菜组配':'🔪','按单烹制':'🍳','上餐监控':'🍽️','余量处置':'♻️',
|
||||
'净配':'🔪','按单烹制':'🍳','上餐监控':'🍽️','余量处置':'♻️',
|
||||
'营养检验':'🧪','用户取餐':'👤','种植采收':'🌱','农残检测':'🔬',
|
||||
'干货入库':'📥','干货存储':'🏪','预处理':'🥬'
|
||||
};
|
||||
var NODE_ICON_BG = {
|
||||
'采供入库':'#F6FFED','卫生检测':'#E6F7FF','毛菜入库':'#E6FFFB','毛菜存储':'#FFF7E6',
|
||||
'净菜加工':'#F6FFED','净菜入库':'#F0F5FF','净菜内供':'#FFF2E8','净菜进柜':'#F9F0FF',
|
||||
'净菜组配':'#FFF0F6','按单烹制':'#FFF7E6','上餐监控':'#FFF1F0','余量处置':'#F5F5F5',
|
||||
'净配':'#FFF0F6','按单烹制':'#FFF7E6','上餐监控':'#FFF1F0','余量处置':'#F5F5F5',
|
||||
'营养检验':'#E6FFFB','用户取餐':'#E6F7FF','种植采收':'#F6FFED','农残检测':'#E6F7FF',
|
||||
'干货入库':'#E6FFFB','干货存储':'#FFF7E6','预处理':'#F6FFED'
|
||||
};
|
||||
@@ -215,7 +215,7 @@ var NODES = [
|
||||
{stage:'净菜入库',time:'2026-04-22 08:30',title:'净菜冷柜暂存(3条分支)',trace:'NC-ST-20260422-001',detail:'分支A:切丁5kg → 净菜柜NC-01<br>分支B:切片3kg → 净菜柜NC-02<br>分支C:切条2kg → 净菜柜NC-03',badge:'x3',status:'ok',video:['净菜入库','2026-04-22 08:30','净菜柜-CAM07','00:01:50']},
|
||||
{stage:'净菜内供',time:'2026-04-22 09:30',title:'净菜领用出库(3条分支)',trace:'NC-OUT-20260422-001',detail:'分支A:切丁5kg → 后厨A组<br>分支B:切片3kg → 后厨B组<br>分支C:切条2kg → 后厨C组',badge:'x3',status:'ok',video:['净菜内供','2026-04-22 09:30','出库口-CAM08','00:02:00']},
|
||||
{stage:'净菜进柜',time:'2026-04-22 09:45',title:'后厨冷柜暂存(2条分支)',trace:'KC-IN-20260422-001',detail:'分支A:切丁5kg → 后厨冷柜KC-01<br>分支B:切片3kg → 后厨冷柜KC-02',badge:'x2',status:'ok',video:['净菜进柜','2026-04-22 09:45','后厨柜-CAM08','00:01:20']},
|
||||
{stage:'净菜组配',time:'2026-04-22 10:20',title:'菜品组配(2条分支)',trace:'CMB-20260422-001',detail:'分支A:CMB-001 宫保鸡丁 5.0kg<br>李组配<br>分支B:CMB-006 辣子鸡 3.5kg<br>张组配',badge:'x2',status:'ok',video:['净菜组配','2026-04-22 10:20','组配间-CAM09','00:02:40']},
|
||||
{stage:'净配',time:'2026-04-22 10:20',title:'菜品净配(2条分支)',trace:'CMB-20260422-001',detail:'分支A:CMB-001 宫保鸡丁 5.0kg<br>李净配<br>分支B:CMB-006 辣子鸡 3.5kg<br>张净配',badge:'x2',status:'ok',video:['净配','2026-04-22 10:20','净配间-CAM09','00:02:40']},
|
||||
{stage:'按单烹制',time:'2026-04-22 11:00 ~ 11:35',operator:'张厨师 等3人',title:'烹制任务 · 多锅次烹制(3条分支)',trace:'COOK-20260422-001',detail:'分支A:COOK-001(宫保鸡丁)张厨师 2锅<br>分支B:COOK-009(辣子鸡)王厨师 1锅<br>分支C:COOK-015(青椒鸡丁)李厨师 1锅',badge:'x3',status:'ok',video:['按单烹制','2026-04-22 11:00','后厨-CAM10','00:04:20']},
|
||||
{stage:'上餐监控',time:'2026-04-22 11:30 ~ 12:00',title:'上餐批次 · 多餐线分发(3条分支)',trace:'SERVE-20260422-001',detail:'分支A:SERVE-001 → A区中餐线 6.8kg<br>分支B:SERVE-007 → B区中餐线 1.4kg<br>分支C:SERVE-015 → C区中餐线 2.1kg',badge:'x3',status:'ok',video:['上餐监控','2026-04-22 11:30','餐线-CAM11','00:03:05']},
|
||||
{stage:'余量处置',time:'2026-04-22 13:00 ~ 13:20',title:'余量处置 · 多餐线余量登记(3条分支)',trace:'DISP-20260422-001',detail:'分支A:余量0.3kg → 冷藏留用<br>分支B:余量0.1kg → 废弃销毁<br>分支C:余量0.2kg → 废弃销毁<br>经办人:李处置',badge:'x3',status:'ok',video:['余量处置','2026-04-22 13:00','余量区-CAM12','00:01:30']},
|
||||
@@ -227,7 +227,7 @@ var NODES = [
|
||||
var STAGE_OPERATORS = {
|
||||
'采供入库':'赵采购','卫生检测':'陈检测','毛菜入库':'王仓管','毛菜存储':'系统监控',
|
||||
'净菜加工':'刘加工','净菜入库':'王仓管','净菜内供':'王仓管','净菜进柜':'王仓管',
|
||||
'净菜组配':'李组配','按单烹制':'张厨师','上餐监控':'系统记录','余量处置':'李处置',
|
||||
'净配':'李净配','按单烹制':'张厨师','上餐监控':'系统记录','余量处置':'李处置',
|
||||
'营养检验':'周营养师','用户取餐':'系统记录','种植采收':'孙农技','农残检测':'陈检测',
|
||||
'干货入库':'王仓管','干货存储':'系统监控','预处理':'刘加工'
|
||||
};
|
||||
|
||||
@@ -156,12 +156,12 @@ other-terminals/
|
||||
├── clean-veg-inventory-cabinet/ # 净菜库存柜
|
||||
├── clean-veg-kitchen-cabinet/ # 净菜厨房柜
|
||||
├── clean-veg-packaging-labeler/ # 净菜包装贴标机
|
||||
├── clean-veg-ratio-scale/ # 净菜配比秤
|
||||
├── clean-veg-ratio-scale/ # 厨配秤
|
||||
├── food-safety-monitor/ # 食安监控设备
|
||||
├── nutrition-score-screen/ # 营养得分屏
|
||||
├── plate-dispenser/ # 吐盘机
|
||||
├── processing-prompt-screen/ # 加工提示屏
|
||||
├── ratio-scale/ # 配比秤
|
||||
├── ratio-scale/ # 净配秤
|
||||
├── raw-veg-hygiene-inspection/ # 毛菜卫检设备
|
||||
├── raw-veg-inspection-scale/ # 毛菜验收秤
|
||||
├── raw-veg-inventory-cabinet/ # 毛菜库存柜
|
||||
@@ -181,12 +181,12 @@ other-terminals/
|
||||
| `clean-veg-inventory-cabinet` | 净菜库存柜 | 净菜库存管理与盘点 |
|
||||
| `clean-veg-kitchen-cabinet` | 净菜厨房柜 | 净菜暂存与保鲜存储 |
|
||||
| `clean-veg-packaging-labeler` | 净菜包装贴标机 | 净菜自动包装与贴标打印 |
|
||||
| `clean-veg-ratio-scale` | 净菜配比秤 | 净菜搭配称重与营养配比 |
|
||||
| `clean-veg-ratio-scale` | 厨配秤 | 净菜搭配称重与营养配比 |
|
||||
| `food-safety-monitor` | 食安监控设备 | 食品安全监控与预警 |
|
||||
| `nutrition-score-screen` | 营养得分屏 | 个人营养评分与健康建议 |
|
||||
| `plate-dispenser` | 吐盘机 | 自动出盘与回盘管理 |
|
||||
| `processing-prompt-screen` | 加工提示屏 | 加工流程提示与操作指引 |
|
||||
| `ratio-scale` | 配比秤 | 食材精准配比称重 |
|
||||
| `ratio-scale` | 净配秤 | 食材精准配比称重 |
|
||||
| `raw-veg-hygiene-inspection` | 毛菜卫检设备 | 毛菜卫生检测与质量判定 |
|
||||
| `raw-veg-inspection-scale` | 毛菜验收秤 | 毛菜到货验收与质检 |
|
||||
| `raw-veg-inventory-cabinet` | 毛菜库存柜 | 毛菜库存管理与出入库 |
|
||||
|
||||
Reference in New Issue
Block a user