feat: 任务中心页重构(统计卡片/分段Tab/动态筛选) + 仓库页新增净菜包柜
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
d13eb783ad
commit
564dd1c1c5
@@ -1,5 +1,16 @@
|
||||
# 营养用餐管理端 CHANGELOG
|
||||
|
||||
## [2026-08-24] feat: 任务中心页重构(统计卡片/分段Tab/动态筛选) + 仓库页新增净菜包柜
|
||||
|
||||
- `sup-task-center.html` 任务中心重构:
|
||||
- 顶部新增统计卡片(今日任务总数/待办任务/已完成任务/任务完成率)
|
||||
- Tab 改为分段式样式,筛选栏与表头随 Tab 动态渲染
|
||||
- 毛菜加工任务改为按食材明细模式(需求数量/已加工/进度/截止时间),状态新增「进行中」
|
||||
- 净菜加工任务改为按食材+净菜类型的单行任务模式
|
||||
- `sup-warehouse.html` 仓库管理:
|
||||
- 物品类型新增「净菜包」「净菜餐品包」,存储方式/货柜类型新增「净菜包柜」
|
||||
- 新增 1号净菜包柜格口数据与存取记录,货柜/物品类型标签样式统一映射(tag-purple 新增)
|
||||
|
||||
## [2026-08-21] feat: 溯源链路详情卡片优化(去副标题/属性独占一行/时间并入详情/去重复溯源码)
|
||||
|
||||
- `trace-timeline-ingredient.html` / `trace-timeline-dish.html` 溯源节点详情卡片:
|
||||
|
||||
@@ -41,11 +41,22 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
|
||||
/* 主内容 */
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
/* Tab 切换栏 */
|
||||
.tabs{display:flex;gap:8px;margin-bottom:16px;background:#fff;border-radius:var(--radius-md);padding:8px 12px;box-shadow:var(--shadow-sm);}
|
||||
.tab{padding:8px 20px;border-radius:var(--radius-sm);font-size:var(--font-sm);font-weight:500;cursor:pointer;color:var(--text-secondary);transition:all .2s;border:1px solid transparent;}
|
||||
.tab:hover{color:var(--primary);}
|
||||
.tab.active{color:var(--primary);background:var(--primary-light);border-color:var(--primary);}
|
||||
/* 统计卡片 */
|
||||
.stats-row{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:16px;}
|
||||
.stat-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;}
|
||||
.stat-card__icon{width:48px;height:48px;border-radius:var(--radius-md);display:flex;align-items:center;justify-content:center;font-size:24px;}
|
||||
.stat-card__icon.blue{background:var(--primary-light);}
|
||||
.stat-card__icon.green{background:#F6FFED;}
|
||||
.stat-card__icon.orange{background:#FFF7E6;}
|
||||
.stat-card__icon.red{background:#FFF1F0;}
|
||||
.stat-card__info{display:flex;flex-direction:column;gap:4px;}
|
||||
.stat-card__value{font-size:var(--font-xl);font-weight:700;color:var(--text-primary);}
|
||||
.stat-card__label{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
/* Tab 切换栏(分段式) */
|
||||
.tabs{display:flex;gap:0;margin-bottom:16px;background:#F0F2F5;border-radius:var(--radius-md);padding:4px;box-shadow:var(--shadow-sm);}
|
||||
.tab{padding:8px 22px;border-radius:6px;font-size:var(--font-sm);font-weight:500;cursor:pointer;color:var(--text-secondary);transition:all .2s;white-space:nowrap;}
|
||||
.tab:hover{color:var(--text-primary);}
|
||||
.tab.active{color:var(--primary);background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12);font-weight:600;}
|
||||
.tab__count{margin-left:6px;font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.tab.active .tab__count{color:var(--primary);}
|
||||
/* 筛选栏 */
|
||||
@@ -117,6 +128,19 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
.detail-table th{background:#FAFAFA;padding:8px 12px;text-align:left;font-size:var(--font-xs);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
|
||||
.detail-table td{padding:10px 12px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);}
|
||||
.detail-table tr:last-child td{border-bottom:none;}
|
||||
.detail-table tfoot td{background:#FAFAFA;font-weight:600;border-top:1px solid var(--border);border-bottom:none;}
|
||||
/* 模态框 */
|
||||
.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center;animation:modalFadeIn .2s ease;}
|
||||
.modal-overlay.closing{animation:modalFadeOut .2s ease forwards;}
|
||||
.modal{background:#fff;border-radius:var(--radius-lg);box-shadow:var(--shadow-md);width:860px;max-width:92vw;max-height:86vh;display:flex;flex-direction:column;animation:modalZoomIn .2s ease;}
|
||||
.modal-overlay.closing .modal{animation:modalZoomOut .2s ease forwards;}
|
||||
@keyframes modalZoomIn{from{transform:scale(.96);opacity:0}to{transform:scale(1);opacity:1}}
|
||||
@keyframes modalZoomOut{from{transform:scale(1);opacity:1}to{transform:scale(.96);opacity:0}}
|
||||
.modal__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);flex-shrink:0;}
|
||||
.modal__title{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);}
|
||||
.modal__close{width:32px;height:32px;border:none;background:none;font-size:20px;cursor:pointer;border-radius:var(--radius-sm);color:var(--text-secondary);display:flex;align-items:center;justify-content:center;}
|
||||
.modal__close:hover{background:#f5f5f5;color:var(--text-primary);}
|
||||
.modal__body{padding:20px 24px 24px;overflow-y:auto;flex:1;}
|
||||
/* Toast */
|
||||
.toast{position:fixed;top:80px;left:50%;transform:translateX(-50%) translateY(-20px);background:rgba(0,0,0,0.8);color:#fff;padding:12px 28px;border-radius:8px;font-size:13px;z-index:1200;opacity:0;visibility:hidden;transition:opacity .3s,transform .3s,visibility .3s;white-space:nowrap;pointer-events:none;}
|
||||
.toast.show{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);}
|
||||
@@ -172,6 +196,14 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="main">
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-row">
|
||||
<div class="stat-card"><div class="stat-card__icon blue">📋</div><div class="stat-card__info"><div class="stat-card__value" id="statTotal">0</div><div class="stat-card__label">今日任务总数</div></div></div>
|
||||
<div class="stat-card"><div class="stat-card__icon orange">⏳</div><div class="stat-card__info"><div class="stat-card__value" id="statPending">0</div><div class="stat-card__label">待办任务</div></div></div>
|
||||
<div class="stat-card"><div class="stat-card__icon green">✅</div><div class="stat-card__info"><div class="stat-card__value" id="statDone">0</div><div class="stat-card__label">已完成任务</div></div></div>
|
||||
<div class="stat-card"><div class="stat-card__icon red">📈</div><div class="stat-card__info"><div class="stat-card__value" id="statRate">0%</div><div class="stat-card__label">任务完成率</div></div></div>
|
||||
</div>
|
||||
|
||||
<!-- Tab 切换 -->
|
||||
<div class="tabs" id="tabs">
|
||||
<div class="tab active" data-idx="0" onclick="switchTab(0, this)">毛菜加工任务 <span class="tab__count" id="tabCount-0"></span></div>
|
||||
@@ -180,16 +212,8 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<div class="tab" data-idx="3" onclick="switchTab(3, this)">净菜包装任务 <span class="tab__count" id="tabCount-3"></span></div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>任务编号</label><input class="filter-input" type="text" id="fTaskNo" placeholder="请输入任务编号"></div>
|
||||
<div class="filter-item"><label>任务状态</label><select class="filter-select" id="fStatus"></select></div>
|
||||
<div class="filter-item"><label>执行人</label><input class="filter-input" type="text" id="fExecutor" placeholder="请输入执行人"></div>
|
||||
<div class="filter-item"><label>生成时间</label><input class="filter-input" type="date" id="fCreatedStart"></div>
|
||||
<div class="filter-item"><label>至</label><input class="filter-input" type="date" id="fCreatedEnd"></div>
|
||||
<button class="btn btn-primary" onclick="doQuery()">查询</button>
|
||||
<button class="btn btn-default" onclick="doReset()">重置</button>
|
||||
</div>
|
||||
<!-- 筛选栏(随 Tab 动态渲染) -->
|
||||
<div class="filter-bar" id="filterBar"></div>
|
||||
|
||||
<!-- 操作栏 -->
|
||||
<div class="action-bar">
|
||||
@@ -204,9 +228,7 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
</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>
|
||||
</tr></thead>
|
||||
<thead><tr id="taskThead"></tr></thead>
|
||||
<tbody id="taskTbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -241,48 +263,47 @@ var TASK_TABS=[
|
||||
{
|
||||
label:'毛菜加工任务',
|
||||
device:'毛菜加工秤',
|
||||
statusOptions:['全部','待处理','已完成'],
|
||||
mode:'raw',
|
||||
statusOptions:['全部','待处理','进行中','已完成'],
|
||||
tasks:[
|
||||
{ taskNo:'RW-20260613-001', refNo:'—', content:'黄瓜、土豆、西红柿 等3种食材', status:'待处理', statusType:'pending', executor:'—', createdAt:'2026-06-13 08:00', deadline:'2026-06-13 11:00', finishedAt:'—',
|
||||
{ taskNo:'RW-20260613-001', status:'已完成', statusType:'done', operator:'张三丰', device:'毛菜加工秤-01', taskDate:'2026-06-13', createdAt:'2026-06-13 08:00', finishedAt:'2026-06-13 09:45',
|
||||
detail:[
|
||||
{ name:'黄瓜', spec:'需 2000g', state:'已录 2000g · 截止 10:00' },
|
||||
{ name:'土豆', spec:'需 2500g', state:'已录 2500g · 截止 10:30' },
|
||||
{ name:'西红柿', spec:'需 1750g', state:'已录 1750g · 截止 11:00' }
|
||||
{ itemNo:'MC-001', name:'黄瓜', required:20, actual:20, progress:'100%', deadline:'2026-06-13 10:00', finishedAt:'2026-06-13 09:30' },
|
||||
{ itemNo:'MC-002', name:'土豆', required:25, actual:25, progress:'100%', deadline:'2026-06-13 10:30', finishedAt:'2026-06-13 09:45' },
|
||||
{ itemNo:'MC-003', name:'西红柿', required:17.5, actual:17.5, progress:'100%', deadline:'2026-06-13 11:00', finishedAt:'2026-06-13 09:45' }
|
||||
] },
|
||||
{ taskNo:'RW-20260613-002', refNo:'—', content:'胡萝卜、小番茄 等2种食材', status:'待处理', statusType:'pending', executor:'—', createdAt:'2026-06-13 08:30', deadline:'2026-06-13 14:00', finishedAt:'—',
|
||||
{ taskNo:'RW-20260613-002', status:'进行中', statusType:'processing', operator:'李四', device:'毛菜加工秤-02', taskDate:'2026-06-13', createdAt:'2026-06-13 08:30', finishedAt:'—',
|
||||
detail:[
|
||||
{ name:'胡萝卜', spec:'需 1000g', state:'已录 1000g · 截止 11:30' },
|
||||
{ name:'小番茄', spec:'需 750g', state:'已录 750g · 截止 14:00' }
|
||||
{ itemNo:'MC-004', name:'胡萝卜', required:15, actual:12, progress:'80%', deadline:'2026-06-13 11:30', finishedAt:'—' },
|
||||
{ itemNo:'MC-005', name:'小番茄', required:8, actual:8, progress:'100%', deadline:'2026-06-13 14:00', finishedAt:'2026-06-13 10:15' }
|
||||
] },
|
||||
{ taskNo:'RW-20260613-003', refNo:'—', content:'大白菜、娃娃菜、土豆 等3种食材', status:'已完成', statusType:'done', executor:'张三丰', createdAt:'2026-06-13 07:30', deadline:'2026-06-13 09:30', finishedAt:'2026-06-13 09:45',
|
||||
{ taskNo:'RW-20260613-003', status:'待处理', statusType:'pending', operator:'—', device:'—', taskDate:'2026-06-13', createdAt:'2026-06-13 07:30', finishedAt:'—',
|
||||
detail:[
|
||||
{ name:'大白菜', spec:'需 4000g', state:'已录 4000g' },
|
||||
{ name:'娃娃菜', spec:'需 1250g', state:'已录 1250g' },
|
||||
{ name:'土豆', spec:'需 1500g', state:'已录 1500g' }
|
||||
{ itemNo:'MC-006', name:'大白菜', required:40, actual:0, progress:'0%', deadline:'2026-06-13 09:30', finishedAt:'—' },
|
||||
{ itemNo:'MC-007', name:'娃娃菜', required:12.5, actual:0, progress:'0%', deadline:'2026-06-13 10:00', finishedAt:'—' },
|
||||
{ itemNo:'MC-008', name:'土豆', required:15, actual:0, progress:'0%', deadline:'2026-06-13 10:30', finishedAt:'—' }
|
||||
] },
|
||||
{ taskNo:'RW-20260613-004', status:'待处理', statusType:'pending', operator:'—', device:'—', taskDate:'2026-06-13', createdAt:'2026-06-13 09:00', finishedAt:'—',
|
||||
detail:[
|
||||
{ itemNo:'MC-009', name:'西兰花', required:20, actual:0, progress:'0%', deadline:'2026-06-13 15:00', finishedAt:'—' },
|
||||
{ itemNo:'MC-010', name:'荷兰豆', required:6, actual:0, progress:'0%', deadline:'2026-06-13 15:30', finishedAt:'—' }
|
||||
] }
|
||||
]
|
||||
},
|
||||
{
|
||||
label:'净菜加工任务',
|
||||
device:'净菜加工秤',
|
||||
mode:'clean',
|
||||
statusOptions:['全部','待加工','进行中','已完成'],
|
||||
tasks:[
|
||||
{ taskNo:'CW-20260617-001', refNo:'—', content:'土豆、胡萝卜、黄瓜 等3种食材', status:'进行中', statusType:'processing', executor:'赵六', createdAt:'2026-06-17 08:00', deadline:'2026-06-17 11:30', finishedAt:'—',
|
||||
detail:[
|
||||
{ name:'土豆', spec:'35×1×1mm · 长条 · 平刀', state:'已加工 3.2/5.0斤' },
|
||||
{ name:'胡萝卜', spec:'20×1×1mm · 细丝 · 平刀', state:'已加工 1.5/2.0斤' },
|
||||
{ name:'黄瓜', spec:'30×30×3mm · 圆片 · 平刀', state:'已加工 2.8/4.0斤' }
|
||||
] },
|
||||
{ taskNo:'CW-20260617-002', refNo:'—', content:'大白菜、西红柿 等2种食材', status:'待加工', statusType:'pending', executor:'—', createdAt:'2026-06-17 10:00', deadline:'2026-06-17 14:00', finishedAt:'—',
|
||||
detail:[
|
||||
{ name:'大白菜', spec:'50×30×30mm · 宽段 · 平刀', state:'未加工' },
|
||||
{ name:'西红柿', spec:'25×25×20mm · 楔形块 · 平刀', state:'未加工' }
|
||||
] },
|
||||
{ taskNo:'CW-20260617-003', refNo:'—', content:'芹菜、洋葱 等2种食材', status:'已完成', statusType:'done', executor:'赵六', createdAt:'2026-06-17 07:30', deadline:'2026-06-17 09:00', finishedAt:'2026-06-17 09:15',
|
||||
detail:[
|
||||
{ name:'芹菜', spec:'40×10×10mm · 宽段 · 平刀', state:'已加工 3.0/3.0斤' },
|
||||
{ name:'洋葱', spec:'30×2×2mm · 细丝 · 平刀', state:'已加工 2.0/2.0斤' }
|
||||
] }
|
||||
{ taskNo:'CW-20260617-001', status:'进行中', statusType:'processing', name:'土豆', cleanType:'土豆丝.细丝', processDate:'2026-06-17', required:5, actual:3.2, progress:'64%', device:'净菜加工秤-01', updatedAt:'2026-06-17 10:30', createdAt:'2026-06-17 08:00' },
|
||||
{ taskNo:'CW-20260617-002', status:'进行中', statusType:'processing', name:'胡萝卜', cleanType:'胡萝卜丝.细丝', processDate:'2026-06-17', required:2, actual:1.5, progress:'75%', device:'净菜加工秤-01', updatedAt:'2026-06-17 10:45', createdAt:'2026-06-17 08:00' },
|
||||
{ taskNo:'CW-20260617-003', status:'已完成', statusType:'done', name:'黄瓜', cleanType:'黄瓜片.圆片', processDate:'2026-06-17', required:4, actual:4, progress:'100%', device:'净菜加工秤-01', updatedAt:'2026-06-17 09:30', createdAt:'2026-06-17 07:30' },
|
||||
{ taskNo:'CW-20260617-004', status:'待加工', statusType:'pending', name:'大白菜', cleanType:'白菜段.宽段', processDate:'2026-06-17', required:6, actual:0, progress:'0%', device:'—', updatedAt:'—', createdAt:'2026-06-17 10:00' },
|
||||
{ taskNo:'CW-20260617-005', status:'待加工', statusType:'pending', name:'西红柿', cleanType:'番茄块.楔形块', processDate:'2026-06-17', required:3, actual:0, progress:'0%', device:'—', updatedAt:'—', createdAt:'2026-06-17 10:00' },
|
||||
{ taskNo:'CW-20260617-006', status:'已完成', statusType:'done', name:'芹菜', cleanType:'芹菜段.宽段', processDate:'2026-06-17', required:3, actual:3, progress:'100%', device:'净菜加工秤-01', updatedAt:'2026-06-17 09:15', createdAt:'2026-06-17 07:30' },
|
||||
{ taskNo:'CW-20260617-007', status:'已完成', statusType:'done', name:'洋葱', cleanType:'洋葱丝.细丝', processDate:'2026-06-17', required:2, actual:2, progress:'100%', device:'净菜加工秤-01', updatedAt:'2026-06-17 09:15', createdAt:'2026-06-17 07:30' },
|
||||
{ taskNo:'CW-20260617-008', status:'待加工', statusType:'pending', name:'土豆', cleanType:'土豆片.圆片', processDate:'2026-06-17', required:3, actual:0, progress:'0%', device:'—', updatedAt:'—', createdAt:'2026-06-17 10:30' }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -353,36 +374,188 @@ var currentTabIdx=0;
|
||||
/* ===== 渲染状态下拉选项(随 Tab 切换) ===== */
|
||||
function renderStatusOptions(){
|
||||
var sel=document.getElementById('fStatus');
|
||||
if(!sel) return;
|
||||
var opts=TASK_TABS[currentTabIdx].statusOptions;
|
||||
sel.innerHTML=opts.map(function(o){return '<option>'+o+'</option>';}).join('');
|
||||
}
|
||||
|
||||
/* ===== 各 Tab 搜索字段配置 ===== */
|
||||
var FILTER_DEFS={
|
||||
raw:['no','status','device','operator','date'],
|
||||
clean:['no','status','date','device','name','cleanType'],
|
||||
common:['no','status','operator','date']
|
||||
};
|
||||
|
||||
/* ===== 单个搜索项 HTML ===== */
|
||||
function filterFieldHtml(key){
|
||||
switch(key){
|
||||
case 'no': return '<div class="filter-item"><label>任务编号</label><input class="filter-input" type="text" id="fTaskNo" placeholder="请输入任务编号"></div>';
|
||||
case 'status': return '<div class="filter-item"><label>任务状态</label><select class="filter-select" id="fStatus"></select></div>';
|
||||
case 'device': return '<div class="filter-item"><label>加工设备</label><select class="filter-select" id="fDevice"></select></div>';
|
||||
case 'date': return '<div class="filter-item"><label>任务日期</label><input class="filter-input" type="date" id="fDateStart"></div><div class="filter-item"><label>至</label><input class="filter-input" type="date" id="fDateEnd"></div>';
|
||||
case 'name': return '<div class="filter-item"><label>食材名称</label><select class="filter-select" id="fName" onchange="onNameChange()"></select></div>';
|
||||
case 'cleanType': return '<div class="filter-item"><label>净菜类型</label><select class="filter-select" id="fCleanType"></select></div>';
|
||||
case 'operator': return '<div class="filter-item"><label>操作人</label><input class="filter-input" type="text" id="fOperator" placeholder="请输入操作人"></div>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/* ===== 渲染搜索区(随 Tab 切换) ===== */
|
||||
function renderFilterBar(){
|
||||
var tab=TASK_TABS[currentTabIdx];
|
||||
var mode = tab.mode==='raw' ? 'raw' : (tab.mode==='clean' ? 'clean' : 'common');
|
||||
var keys=FILTER_DEFS[mode];
|
||||
var html=keys.map(filterFieldHtml).join('')
|
||||
+'<button class="btn btn-primary" onclick="doQuery()">查询</button>'
|
||||
+'<button class="btn btn-default" onclick="doReset()">重置</button>';
|
||||
document.getElementById('filterBar').innerHTML=html;
|
||||
renderStatusOptions();
|
||||
renderFilterSelects();
|
||||
}
|
||||
|
||||
/* ===== 从当前 Tab 数据提取去重值 ===== */
|
||||
function distinctValues(field){
|
||||
var tab=TASK_TABS[currentTabIdx];
|
||||
var arr=[];
|
||||
tab.tasks.forEach(function(t){
|
||||
if(t[field] && t[field]!=='—' && arr.indexOf(t[field])===-1) arr.push(t[field]);
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
/* ===== 填充下拉框选项(首项「全部」) ===== */
|
||||
function fillSelect(id, options){
|
||||
var sel=document.getElementById(id);
|
||||
if(!sel) return;
|
||||
sel.innerHTML=['全部'].concat(options).map(function(o){return '<option>'+o+'</option>';}).join('');
|
||||
}
|
||||
|
||||
/* ===== 净菜类型下拉框联动:按选中食材过滤 ===== */
|
||||
function updateCleanTypeOptions(){
|
||||
var nameSel=document.getElementById('fName');
|
||||
var typeSel=document.getElementById('fCleanType');
|
||||
if(!typeSel) return;
|
||||
var tab=TASK_TABS[currentTabIdx];
|
||||
var selectedName = nameSel ? nameSel.value : '全部';
|
||||
var opts=['全部'];
|
||||
tab.tasks.forEach(function(t){
|
||||
if(selectedName==='全部' || t.name===selectedName){
|
||||
if(opts.indexOf(t.cleanType)===-1) opts.push(t.cleanType);
|
||||
}
|
||||
});
|
||||
typeSel.innerHTML=opts.map(function(o){return '<option>'+o+'</option>';}).join('');
|
||||
}
|
||||
|
||||
/* ===== 食材名称变化 → 联动净菜类型 ===== */
|
||||
function onNameChange(){
|
||||
updateCleanTypeOptions();
|
||||
}
|
||||
|
||||
/* ===== 渲染搜索下拉框(设备/食材/净菜类型) ===== */
|
||||
function renderFilterSelects(){
|
||||
fillSelect('fDevice', distinctValues('device'));
|
||||
fillSelect('fName', distinctValues('name'));
|
||||
updateCleanTypeOptions();
|
||||
}
|
||||
|
||||
/* ===== 安全读取输入值 ===== */
|
||||
function val(id){
|
||||
var el=document.getElementById(id);
|
||||
return el ? el.value.trim() : '';
|
||||
}
|
||||
|
||||
/* ===== 状态标签 HTML ===== */
|
||||
function statusTag(statusType,text){
|
||||
var cls=STATUS_TAG[statusType]||'tag-gray';
|
||||
return '<span class="tag '+cls+'">'+text+'</span>';
|
||||
}
|
||||
|
||||
/* ===== 重量格式化 ===== */
|
||||
function fmtKg(kg){ return kg+'kg'; }
|
||||
/* ===== 时间格式化(仅时分) ===== */
|
||||
function fmtTime(s){ return (s && s!=='—') ? s.split(' ')[1] : '—'; }
|
||||
|
||||
/* ===== 渲染表头(随 Tab 切换) ===== */
|
||||
function renderThead(){
|
||||
var tab=TASK_TABS[currentTabIdx];
|
||||
var th;
|
||||
if(tab.mode==='raw'){
|
||||
th=['序号','任务编号','任务状态','加工总量(kg)','任务明细','操作人','加工设备','任务日期','完成时间','创建时间','操作'];
|
||||
}else if(tab.mode==='clean'){
|
||||
th=['序号','任务编号','任务状态','食材名称','净菜类型','加工日期','需求量','已加工重量','进度','加工设备','最后更新时间','创建时间','操作'];
|
||||
}else{
|
||||
th=['任务编号','关联单号','任务内容摘要','任务状态','操作人','生成时间','截止时间','完成时间','操作'];
|
||||
}
|
||||
document.getElementById('taskThead').innerHTML=th.map(function(h){return '<th>'+h+'</th>';}).join('');
|
||||
}
|
||||
|
||||
/* ===== 毛菜加工任务行 ===== */
|
||||
function rawRow(t,i){
|
||||
var vegCount=t.detail.length;
|
||||
var totalKg=t.detail.reduce(function(s,d){return s+d.required;},0);
|
||||
return '<tr>'
|
||||
+'<td>'+(i+1)+'</td>'
|
||||
+'<td>'+t.taskNo+'</td>'
|
||||
+'<td>'+statusTag(t.statusType,t.status)+'</td>'
|
||||
+'<td>'+fmtKg(totalKg)+'</td>'
|
||||
+'<td><span class="link" onclick="showDetailList(\''+t.taskNo+'\')">'+vegCount+'项</span></td>'
|
||||
+'<td>'+t.operator+'</td>'
|
||||
+'<td>'+t.device+'</td>'
|
||||
+'<td>'+t.taskDate+'</td>'
|
||||
+'<td>'+t.finishedAt+'</td>'
|
||||
+'<td>'+t.createdAt+'</td>'
|
||||
+'<td class="td-actions"><span class="link" onclick="showDetail(\''+t.taskNo+'\')">详情</span></td>'
|
||||
+'</tr>';
|
||||
}
|
||||
|
||||
/* ===== 净菜加工任务行 ===== */
|
||||
function cleanRow(t,i){
|
||||
return '<tr>'
|
||||
+'<td>'+(i+1)+'</td>'
|
||||
+'<td>'+t.taskNo+'</td>'
|
||||
+'<td>'+statusTag(t.statusType,t.status)+'</td>'
|
||||
+'<td>'+t.name+'</td>'
|
||||
+'<td>'+t.cleanType+'</td>'
|
||||
+'<td>'+t.processDate+'</td>'
|
||||
+'<td>'+fmtKg(t.required)+'</td>'
|
||||
+'<td>'+(t.actual>0?fmtKg(t.actual):'—')+'</td>'
|
||||
+'<td>'+t.progress+'</td>'
|
||||
+'<td>'+t.device+'</td>'
|
||||
+'<td>'+(t.updatedAt||'—')+'</td>'
|
||||
+'<td>'+t.createdAt+'</td>'
|
||||
+'<td class="td-actions"><span class="link" onclick="showDetail(\''+t.taskNo+'\')">详情</span></td>'
|
||||
+'</tr>';
|
||||
}
|
||||
|
||||
/* ===== 通用任务行 ===== */
|
||||
function commonRow(t){
|
||||
return '<tr>'
|
||||
+'<td>'+t.taskNo+'</td>'
|
||||
+'<td>'+t.refNo+'</td>'
|
||||
+'<td>'+t.content+'</td>'
|
||||
+'<td>'+statusTag(t.statusType,t.status)+'</td>'
|
||||
+'<td>'+t.executor+'</td>'
|
||||
+'<td>'+t.createdAt+'</td>'
|
||||
+'<td>'+t.deadline+'</td>'
|
||||
+'<td>'+t.finishedAt+'</td>'
|
||||
+'<td class="td-actions"><span class="link" onclick="showDetail(\''+t.taskNo+'\')">详情</span></td>'
|
||||
+'</tr>';
|
||||
}
|
||||
|
||||
/* ===== 渲染行(按模式分发) ===== */
|
||||
function renderRow(t,i){
|
||||
var tab=TASK_TABS[currentTabIdx];
|
||||
if(tab.mode==='raw') return rawRow(t,i);
|
||||
if(tab.mode==='clean') return cleanRow(t,i);
|
||||
return commonRow(t);
|
||||
}
|
||||
|
||||
/* ===== 渲染表格 ===== */
|
||||
function renderTable(){
|
||||
renderThead();
|
||||
var tab=TASK_TABS[currentTabIdx];
|
||||
var tbody=document.getElementById('taskTbody');
|
||||
var html='';
|
||||
tab.tasks.forEach(function(t){
|
||||
html+='<tr>'
|
||||
+'<td>'+t.taskNo+'</td>'
|
||||
+'<td>'+t.refNo+'</td>'
|
||||
+'<td>'+t.content+'</td>'
|
||||
+'<td>'+statusTag(t.statusType,t.status)+'</td>'
|
||||
+'<td>'+t.executor+'</td>'
|
||||
+'<td>'+t.createdAt+'</td>'
|
||||
+'<td>'+t.deadline+'</td>'
|
||||
+'<td>'+t.finishedAt+'</td>'
|
||||
+'<td class="td-actions"><span class="link" onclick="showDetail(\''+t.taskNo+'\')">详情</span></td>'
|
||||
+'</tr>';
|
||||
});
|
||||
tbody.innerHTML=html;
|
||||
|
||||
tbody.innerHTML=tab.tasks.map(renderRow).join('');
|
||||
document.getElementById('tableTitle').textContent=tab.label;
|
||||
document.getElementById('pageInfo').textContent='显示 '+tab.tasks.length+' 条';
|
||||
}
|
||||
@@ -394,57 +567,83 @@ function renderTabCounts(){
|
||||
});
|
||||
}
|
||||
|
||||
/* ===== 渲染顶部统计卡片(全局汇总,不随 Tab 变化) ===== */
|
||||
function renderStats(){
|
||||
var total=0, done=0;
|
||||
TASK_TABS.forEach(function(tab){
|
||||
total+=tab.tasks.length;
|
||||
tab.tasks.forEach(function(t){
|
||||
if(t.statusType==='done') done++;
|
||||
});
|
||||
});
|
||||
var pending=total-done;
|
||||
var rate=total ? Math.round(done/total*100) : 0;
|
||||
document.getElementById('statTotal').textContent=total;
|
||||
document.getElementById('statPending').textContent=pending;
|
||||
document.getElementById('statDone').textContent=done;
|
||||
document.getElementById('statRate').textContent=rate+'%';
|
||||
}
|
||||
|
||||
/* ===== 切换 Tab ===== */
|
||||
function switchTab(idx,el){
|
||||
currentTabIdx=idx;
|
||||
document.querySelectorAll('.tab').forEach(function(t){t.classList.remove('active');});
|
||||
el.classList.add('active');
|
||||
renderStatusOptions();
|
||||
renderFilterBar();
|
||||
doReset();
|
||||
renderTable();
|
||||
}
|
||||
|
||||
/* ===== 查询(简单前端过滤:任务编号/执行人关键字 + 状态精确匹配) ===== */
|
||||
/* ===== 查询(各 Tab 独立过滤字段) ===== */
|
||||
function doQuery(){
|
||||
var tab=TASK_TABS[currentTabIdx];
|
||||
var kwNo=document.getElementById('fTaskNo').value.trim();
|
||||
var kwExe=document.getElementById('fExecutor').value.trim();
|
||||
var status=document.getElementById('fStatus').value;
|
||||
var mode = tab.mode==='raw' ? 'raw' : (tab.mode==='clean' ? 'clean' : 'common');
|
||||
var kwNo=val('fTaskNo');
|
||||
var status=val('fStatus');
|
||||
var kwDevice=val('fDevice');
|
||||
var kwOperator=val('fOperator');
|
||||
var kwName=val('fName');
|
||||
var kwCleanType=val('fCleanType');
|
||||
var dateStart=val('fDateStart');
|
||||
var dateEnd=val('fDateEnd');
|
||||
|
||||
var list=tab.tasks.filter(function(t){
|
||||
if(kwNo && t.taskNo.indexOf(kwNo)===-1) return false;
|
||||
if(kwExe && t.executor.indexOf(kwExe)===-1) return false;
|
||||
if(status && status!=='全部' && t.status!==status) return false;
|
||||
if(kwDevice && kwDevice!=='全部' && t.device!==kwDevice) return false;
|
||||
|
||||
if(mode==='raw'){
|
||||
if(kwOperator && (t.operator||'').indexOf(kwOperator)===-1) return false;
|
||||
if(dateStart && t.taskDate < dateStart) return false;
|
||||
if(dateEnd && t.taskDate > dateEnd) return false;
|
||||
}else if(mode==='clean'){
|
||||
if(kwName && kwName!=='全部' && t.name!==kwName) return false;
|
||||
if(kwCleanType && kwCleanType!=='全部' && t.cleanType!==kwCleanType) return false;
|
||||
if(dateStart && t.processDate < dateStart) return false;
|
||||
if(dateEnd && t.processDate > dateEnd) return false;
|
||||
}else{
|
||||
if(kwOperator && (t.executor||'').indexOf(kwOperator)===-1) return false;
|
||||
if(dateStart && t.createdAt.slice(0,10) < dateStart) return false;
|
||||
if(dateEnd && t.createdAt.slice(0,10) > dateEnd) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
var tbody=document.getElementById('taskTbody');
|
||||
var html='';
|
||||
list.forEach(function(t){
|
||||
html+='<tr>'
|
||||
+'<td>'+t.taskNo+'</td>'
|
||||
+'<td>'+t.refNo+'</td>'
|
||||
+'<td>'+t.content+'</td>'
|
||||
+'<td>'+statusTag(t.statusType,t.status)+'</td>'
|
||||
+'<td>'+t.executor+'</td>'
|
||||
+'<td>'+t.createdAt+'</td>'
|
||||
+'<td>'+t.deadline+'</td>'
|
||||
+'<td>'+t.finishedAt+'</td>'
|
||||
+'<td class="td-actions"><span class="link" onclick="showDetail(\''+t.taskNo+'\')">详情</span></td>'
|
||||
+'</tr>';
|
||||
});
|
||||
tbody.innerHTML=html;
|
||||
tbody.innerHTML=list.map(renderRow).join('');
|
||||
document.getElementById('pageInfo').textContent='显示 '+list.length+' 条';
|
||||
}
|
||||
|
||||
/* ===== 重置 ===== */
|
||||
function doReset(){
|
||||
document.getElementById('fTaskNo').value='';
|
||||
document.getElementById('fExecutor').value='';
|
||||
document.getElementById('fCreatedStart').value='';
|
||||
document.getElementById('fCreatedEnd').value='';
|
||||
var sel=document.getElementById('fStatus');
|
||||
sel.selectedIndex=0;
|
||||
['fTaskNo','fOperator','fDateStart','fDateEnd'].forEach(function(id){
|
||||
var el=document.getElementById(id);
|
||||
if(el) el.value='';
|
||||
});
|
||||
['fStatus','fDevice','fName','fCleanType'].forEach(function(id){
|
||||
var el=document.getElementById(id);
|
||||
if(el) el.selectedIndex=0;
|
||||
});
|
||||
updateCleanTypeOptions();
|
||||
renderTable();
|
||||
}
|
||||
|
||||
@@ -485,18 +684,77 @@ function findTask(taskNo){
|
||||
return null;
|
||||
}
|
||||
|
||||
/* ===== 毛菜加工食材明细表(任务明细弹窗与详情抽屉共用) ===== */
|
||||
function renderRawDetailTable(t){
|
||||
var rows='';var totalReq=0,totalActual=0;
|
||||
t.detail.forEach(function(d){
|
||||
totalReq+=d.required; totalActual+=d.actual||0;
|
||||
rows+='<tr>'
|
||||
+'<td>'+d.itemNo+'</td>'
|
||||
+'<td>'+d.name+'</td>'
|
||||
+'<td>'+fmtKg(d.required)+'</td>'
|
||||
+'<td>'+(d.actual>0?fmtKg(d.actual):'—')+'</td>'
|
||||
+'<td>'+d.progress+'</td>'
|
||||
+'<td>'+fmtTime(d.deadline)+'</td>'
|
||||
+'<td>'+fmtTime(d.finishedAt)+'</td>'
|
||||
+'</tr>';
|
||||
});
|
||||
return '<table class="detail-table">'
|
||||
+'<thead><tr><th>食材编号</th><th>食材名称</th><th>需求量</th><th>实际加工量</th><th>进度</th><th>截止时间</th><th>完成时间</th></tr></thead>'
|
||||
+'<tbody>'+rows+'</tbody>'
|
||||
+'<tfoot><tr><td>总计</td><td>'+t.detail.length+'种</td><td>'+fmtKg(totalReq)+'</td><td>'+(totalActual>0?fmtKg(totalActual):'—')+'</td><td>—</td><td>—</td><td>—</td></tr></tfoot>'
|
||||
+'</table>';
|
||||
}
|
||||
|
||||
function showDetail(taskNo){
|
||||
var r=findTask(taskNo);
|
||||
if(!r) return;
|
||||
var tab=r.tab, t=r.task;
|
||||
|
||||
/* 毛菜加工任务:任务信息 + 食材列表 */
|
||||
if(tab.mode==='raw'){
|
||||
var rawFields=
|
||||
'<div class="drawer-field"><div class="drawer-field__label">任务编号</div><div class="drawer-field__value">'+t.taskNo+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">任务状态</div><div class="drawer-field__value">'+statusTag(t.statusType,t.status)+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">操作人</div><div class="drawer-field__value">'+t.operator+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">加工设备</div><div class="drawer-field__value">'+t.device+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">任务日期</div><div class="drawer-field__value">'+t.taskDate+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">创建时间</div><div class="drawer-field__value">'+t.createdAt+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">完成时间</div><div class="drawer-field__value">'+t.finishedAt+'</div></div>';
|
||||
|
||||
var rawHtml='<div class="drawer-section"><div class="drawer-section__title">任务信息</div><div class="drawer-fields">'+rawFields+'</div></div>'
|
||||
+'<div class="drawer-section"><div class="drawer-section__title">食材列表</div>'+renderRawDetailTable(t)+'</div>';
|
||||
|
||||
Drawer.open('任务详情 — '+t.taskNo, rawHtml);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 净菜加工任务:任务信息 */
|
||||
if(tab.mode==='clean'){
|
||||
var cleanFields=
|
||||
'<div class="drawer-field"><div class="drawer-field__label">任务编号</div><div class="drawer-field__value">'+t.taskNo+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">任务状态</div><div class="drawer-field__value">'+statusTag(t.statusType,t.status)+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">食材名称</div><div class="drawer-field__value">'+t.name+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">净菜类型</div><div class="drawer-field__value">'+t.cleanType+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">加工日期</div><div class="drawer-field__value">'+t.processDate+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">需求量</div><div class="drawer-field__value">'+fmtKg(t.required)+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">已加工重量</div><div class="drawer-field__value">'+(t.actual>0?fmtKg(t.actual):'—')+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">进度</div><div class="drawer-field__value">'+t.progress+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">加工设备</div><div class="drawer-field__value">'+t.device+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">最后更新时间</div><div class="drawer-field__value">'+(t.updatedAt||'—')+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">创建时间</div><div class="drawer-field__value">'+t.createdAt+'</div></div>';
|
||||
|
||||
Drawer.open('任务详情 — '+t.taskNo, '<div class="drawer-section"><div class="drawer-section__title">任务信息</div><div class="drawer-fields">'+cleanFields+'</div></div>');
|
||||
return;
|
||||
}
|
||||
|
||||
/* 任务信息区 */
|
||||
var fields=
|
||||
'<div class="drawer-field"><div class="drawer-field__label">任务编号</div><div class="drawer-field__value">'+t.taskNo+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">终端设备</div><div class="drawer-field__value">'+tab.device+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">关联单号</div><div class="drawer-field__value">'+t.refNo+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">任务状态</div><div class="drawer-field__value">'+statusTag(t.statusType,t.status)+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">执行人</div><div class="drawer-field__value">'+t.executor+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">操作人</div><div class="drawer-field__value">'+t.executor+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">生成时间</div><div class="drawer-field__value">'+t.createdAt+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">截止时间</div><div class="drawer-field__value">'+t.deadline+'</div></div>'
|
||||
+'<div class="drawer-field"><div class="drawer-field__label">完成时间</div><div class="drawer-field__value">'+t.finishedAt+'</div></div>';
|
||||
@@ -514,6 +772,32 @@ function showDetail(taskNo){
|
||||
Drawer.open('任务详情 — '+t.taskNo, html);
|
||||
}
|
||||
|
||||
/* ===== 任务明细弹窗(毛菜加工) ===== */
|
||||
function showDetailList(taskNo){
|
||||
var r=findTask(taskNo);
|
||||
if(!r) return;
|
||||
var t=r.task;
|
||||
Modal.open('任务明细 — '+t.taskNo, renderRawDetailTable(t));
|
||||
}
|
||||
|
||||
/* ===== 模态框 ===== */
|
||||
var Modal={
|
||||
open:function(title,bodyHtml){
|
||||
this.close();
|
||||
var overlay=document.createElement('div');
|
||||
overlay.className='modal-overlay';
|
||||
overlay.innerHTML='<div class="modal"><div class="modal__header"><div class="modal__title">'+title+'</div><button class="modal__close" onclick="Modal.close()">×</button></div><div class="modal__body">'+bodyHtml+'</div></div>';
|
||||
overlay.addEventListener('click',function(e){if(e.target===overlay)Modal.close();});
|
||||
document.body.appendChild(overlay);
|
||||
},
|
||||
close:function(){
|
||||
var o=document.querySelector('.modal-overlay');
|
||||
if(!o)return;
|
||||
o.classList.add('closing');
|
||||
setTimeout(function(){o.remove();},200);
|
||||
}
|
||||
};
|
||||
|
||||
/* ===== Toast ===== */
|
||||
function showToast(msg){
|
||||
var el=document.getElementById('toast');
|
||||
@@ -524,7 +808,8 @@ function showToast(msg){
|
||||
}
|
||||
|
||||
/* ===== 初始化 ===== */
|
||||
renderStatusOptions();
|
||||
renderStats();
|
||||
renderFilterBar();
|
||||
renderTabCounts();
|
||||
renderTable();
|
||||
</script>
|
||||
|
||||
@@ -79,6 +79,7 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
.tag-orange{background:#FFF7E6;color:var(--warning);}
|
||||
.tag-red{background:#FFF1F0;color:var(--danger);}
|
||||
.tag-gray{background:#F5F5F5;color:#999;}
|
||||
.tag-purple{background:#F9F0FF;color:#722ED1;}
|
||||
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
|
||||
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);}
|
||||
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
@@ -268,10 +269,10 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>食材名称</label><input class="filter-input" placeholder="请输入食材名称"></div>
|
||||
<div class="filter-item"><label>物品类型</label>
|
||||
<select class="filter-select"><option>全部</option><option>毛菜</option><option>净菜</option></select>
|
||||
<select class="filter-select"><option>全部</option><option>毛菜</option><option>净菜</option><option>净菜包</option><option>净菜餐品包</option></select>
|
||||
</div>
|
||||
<div class="filter-item"><label>存储方式</label>
|
||||
<select class="filter-select" id="storageTypeFilter" onchange="updateAreaOptions()"><option>全部</option><option>毛菜柜</option><option>净菜柜</option><option>仓库</option></select>
|
||||
<select class="filter-select" id="storageTypeFilter" onchange="updateAreaOptions()"><option>全部</option><option>毛菜柜</option><option>净菜柜</option><option>净菜包柜</option><option>仓库</option></select>
|
||||
</div>
|
||||
<div class="filter-item"><label>所在区域</label>
|
||||
<select class="filter-select" id="areaFilter"></select>
|
||||
@@ -302,11 +303,13 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<tr><td>猪里脊</td><td><span class="tag tag-blue">净菜</span></td><td>IN-20260415-003</td><td><span class="tag tag-blue">净菜柜</span></td><td>3号净菜柜-C02格口</td><td>80</td><td>40</td><td>40</td><td>2026-04-15 09:15</td><td>2026-04-19</td><td><span class="tag tag-orange">临期</span></td><td><span class="tag tag-orange">不足</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" onclick="showStockDetail(this)">详情</span></td></tr>
|
||||
<tr><td>草鱼</td><td><span class="tag tag-green">毛菜</span></td><td>IN-20260415-004</td><td><span class="tag tag-gray">仓库</span></td><td>C仓-水产区-01</td><td>60</td><td>0</td><td>60</td><td>2026-04-15 09:45</td><td>2026-04-15</td><td><span class="tag tag-red">已过期</span></td><td><span class="tag tag-orange">不足</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" onclick="showStockDetail(this)">详情</span></td></tr>
|
||||
<tr><td>胡萝卜</td><td><span class="tag tag-green">毛菜</span></td><td>IN-20260416-005</td><td><span class="tag tag-gray">仓库</span></td><td>A仓-蔬菜区-03</td><td>420</td><td>0</td><td>420</td><td>2026-04-16 11:00</td><td>2026-04-23</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-green">充足</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" onclick="showStockDetail(this)">详情</span></td></tr>
|
||||
<tr><td>净土豆丝净菜包</td><td><span class="tag tag-orange">净菜包</span></td><td>IN-20260422-031</td><td><span class="tag tag-orange">净菜包柜</span></td><td>1号净菜包柜-A02格口</td><td>25</td><td>10</td><td>15</td><td>2026-04-22 09:15</td><td>2026-04-25</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-green">充足</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" onclick="showStockDetail(this)">详情</span></td></tr>
|
||||
<tr><td>土豆烧肉餐品包</td><td><span class="tag tag-purple">净菜餐品包</span></td><td>IN-20260422-032</td><td><span class="tag tag-orange">净菜包柜</span></td><td>1号净菜包柜-B01格口</td><td>18</td><td>6</td><td>12</td><td>2026-04-22 10:30</td><td>2026-04-24</td><td><span class="tag tag-orange">临期</span></td><td><span class="tag tag-green">充足</span></td><td class="td-actions"><span class="link">编辑</span> <span class="link" onclick="showStockDetail(this)">详情</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
<span>显示 5 条,共 18 条</span>
|
||||
<span>显示 7 条,共 20 条</span>
|
||||
<button class="page-btn">‹</button>
|
||||
<button class="page-btn active">1</button>
|
||||
<button class="page-btn">2</button>
|
||||
@@ -414,7 +417,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<div class="filter-bar">
|
||||
<div class="filter-item"><label>设备编号/名称</label><input class="filter-input" placeholder="请输入设备编号或名称"></div>
|
||||
<div class="filter-item"><label>货柜类型</label>
|
||||
<select class="filter-select"><option>全部</option><option>净菜柜</option><option>毛菜柜</option></select>
|
||||
<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>
|
||||
@@ -489,6 +492,7 @@ function switchTab(idx){
|
||||
var storageAreaOptions={
|
||||
'毛菜柜':['8号柜','9号柜','10号柜'],
|
||||
'净菜柜':['3号净菜柜','5号净菜柜','6号净菜柜'],
|
||||
'净菜包柜':['1号净菜包柜'],
|
||||
'仓库':['A仓-蔬菜区-01','A仓-蔬菜区-02','A仓-蔬菜区-03','A仓-蔬菜区-04','B仓-肉类区-01','B仓-肉类区-02','B仓-肉类区-03','C仓-水产区-01','C仓-水产区-02']
|
||||
};
|
||||
function updateAreaOptions(){
|
||||
@@ -497,7 +501,7 @@ function updateAreaOptions(){
|
||||
if(!storageSelect||!areaSelect)return;
|
||||
var storageType=storageSelect.value;
|
||||
var options=storageType==='全部'
|
||||
? storageAreaOptions['毛菜柜'].concat(storageAreaOptions['净菜柜'],storageAreaOptions['仓库'])
|
||||
? storageAreaOptions['毛菜柜'].concat(storageAreaOptions['净菜柜'],storageAreaOptions['净菜包柜'],storageAreaOptions['仓库'])
|
||||
: storageAreaOptions[storageType]||[];
|
||||
areaSelect.innerHTML='<option>全部</option>'+options.map(function(item){return '<option>'+item+'</option>';}).join('');
|
||||
}
|
||||
@@ -662,21 +666,6 @@ var CABINETS=[
|
||||
{id:'NC3-10',status:'idle'}
|
||||
]
|
||||
},
|
||||
{
|
||||
code:'CAB-NC-002',name:'5号净菜柜',cabinetType:'净菜柜',status:'在线',totalSlots:10,
|
||||
slots:[
|
||||
{id:'NC5-01',status:'occupied',itemName:'净黄瓜片',itemType:'净菜',qty:7.4,op:'李主管',inTime:'2026-04-22 10:05'},
|
||||
{id:'NC5-02',status:'occupied',itemName:'净西红柿',itemType:'净菜',qty:12.0,op:'张厨师',inTime:'2026-04-22 10:15'},
|
||||
{id:'NC5-03',status:'occupied',itemName:'净洋葱',itemType:'净菜',qty:3.2,op:'王厨师',inTime:'2026-04-22 08:40'},
|
||||
{id:'NC5-04',status:'occupied',itemName:'净青椒丝',itemType:'净菜',qty:6.6,op:'张厨师',inTime:'2026-04-22 11:00'},
|
||||
{id:'NC5-05',status:'occupied',itemName:'净豆角',itemType:'净菜',qty:9.0,op:'李主管',inTime:'2026-04-22 11:10'},
|
||||
{id:'NC5-06',status:'occupied',itemName:'净藕片',itemType:'净菜',qty:4.8,op:'王厨师',inTime:'2026-04-22 11:20'},
|
||||
{id:'NC5-07',status:'idle'},
|
||||
{id:'NC5-08',status:'idle'},
|
||||
{id:'NC5-09',status:'idle'},
|
||||
{id:'NC5-10',status:'idle'}
|
||||
]
|
||||
},
|
||||
{
|
||||
code:'CAB-RW-008',name:'8号毛菜柜',cabinetType:'毛菜柜',status:'在线',totalSlots:8,
|
||||
slots:[
|
||||
@@ -691,16 +680,16 @@ var CABINETS=[
|
||||
]
|
||||
},
|
||||
{
|
||||
code:'CAB-RW-009',name:'9号毛菜柜',cabinetType:'毛菜柜',status:'离线',totalSlots:8,
|
||||
code:'CAB-NP-001',name:'1号净菜包柜',cabinetType:'净菜包柜',status:'在线',totalSlots:8,
|
||||
slots:[
|
||||
{id:'RW9-A1',status:'offline',itemName:'蒜薹',itemType:'毛菜',qty:55.0,op:'张仓管',inTime:'2026-04-15 14:00'},
|
||||
{id:'RW9-A2',status:'offline',itemName:'韭菜',itemType:'毛菜',qty:38.0,op:'张仓管',inTime:'2026-04-15 14:20'},
|
||||
{id:'RW9-B1',status:'offline'},
|
||||
{id:'RW9-B2',status:'offline'},
|
||||
{id:'RW9-C1',status:'offline'},
|
||||
{id:'RW9-C2',status:'offline'},
|
||||
{id:'RW9-C3',status:'offline'},
|
||||
{id:'RW9-D1',status:'offline'}
|
||||
{id:'NP1-A1',status:'occupied',itemName:'净土豆丝净菜包',itemType:'净菜包',qty:15.0,op:'张厨师',inTime:'2026-04-22 09:15'},
|
||||
{id:'NP1-A2',status:'occupied',itemName:'净青椒丝净菜包',itemType:'净菜包',qty:12.5,op:'李主管',inTime:'2026-04-22 09:30'},
|
||||
{id:'NP1-B1',status:'occupied',itemName:'土豆烧肉餐品包',itemType:'净菜餐品包',qty:12.0,op:'王厨师',inTime:'2026-04-22 10:30'},
|
||||
{id:'NP1-B2',status:'occupied',itemName:'香菇滑鸡餐品包',itemType:'净菜餐品包',qty:9.6,op:'张厨师',inTime:'2026-04-22 10:45'},
|
||||
{id:'NP1-C1',status:'occupied',itemName:'净西兰花净菜包',itemType:'净菜包',qty:8.0,op:'王厨师',inTime:'2026-04-22 11:00'},
|
||||
{id:'NP1-C2',status:'idle'},
|
||||
{id:'NP1-C3',status:'idle'},
|
||||
{id:'NP1-D1',status:'idle'}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -709,6 +698,15 @@ var CABINETS=[
|
||||
function slotStatusText(s){
|
||||
return {occupied:'储物中',idle:'空闲',offline:'离线'}[s]||s;
|
||||
}
|
||||
/* 货柜类型 / 物品类型 → 标签样式映射 */
|
||||
function cabinetTypeTag(t){
|
||||
var map={'净菜柜':'tag-blue','毛菜柜':'tag-green','净菜包柜':'tag-orange'};
|
||||
return '<span class="tag '+(map[t]||'tag-gray')+'">'+t+'</span>';
|
||||
}
|
||||
function itemTypeTag(t){
|
||||
var map={'净菜':'tag-blue','毛菜':'tag-green','净菜包':'tag-orange','净菜餐品包':'tag-purple'};
|
||||
return t?'<span class="tag '+(map[t]||'tag-gray')+'">'+t+'</span>':'—';
|
||||
}
|
||||
function slotStatusTag(s){
|
||||
var map={occupied:'tag-green',idle:'tag-gray',offline:'tag-gray'};
|
||||
return '<span class="tag '+(map[s]||'tag-gray')+'">'+slotStatusText(s)+'</span>';
|
||||
@@ -723,7 +721,7 @@ function renderCabinets(){
|
||||
var statusTag=cab.status==='在线'?'<span class="tag tag-green">● 在线</span>'
|
||||
:cab.status==='离线'?'<span class="tag tag-gray">● 离线</span>'
|
||||
:'<span class="tag tag-red">● 故障</span>';
|
||||
var typeTag=cab.cabinetType==='净菜柜'?'<span class="tag tag-blue">净菜柜</span>':'<span class="tag tag-green">毛菜柜</span>';
|
||||
var typeTag=cabinetTypeTag(cab.cabinetType);
|
||||
/* 统计:空闲 / 储物中 / 离线 格口数 */
|
||||
var idleCount=cab.slots.filter(function(s){return s.status==='idle';}).length;
|
||||
var occupiedCount=cab.slots.filter(function(s){return s.status==='occupied';}).length;
|
||||
@@ -803,24 +801,32 @@ var SLOT_HISTORY={
|
||||
{time:'2026-04-20 09:30',itemType:'净菜',itemName:'净白菜',type:'取出',before:22.5,after:20.5,op:'张厨师',inOrder:'IN-20260415-006',remark:'午餐备菜'},
|
||||
{time:'2026-04-18 14:30',itemType:'净菜',itemName:'净白菜',type:'存入',before:0.0,after:23.0,op:'王厨师',inOrder:'IN-20260415-006',remark:'首次入柜'}
|
||||
],
|
||||
'CAB-NC-002|NC5-01':[
|
||||
{time:'2026-04-22 13:20',itemType:'净菜',itemName:'净黄瓜片',type:'取出',before:8.0,after:7.4,op:'李主管',inOrder:'IN-20260416-031',remark:'凉拌黄瓜'},
|
||||
{time:'2026-04-22 10:05',itemType:'净菜',itemName:'净黄瓜片',type:'存入',before:0.0,after:9.5,op:'李主管',inOrder:'IN-20260416-031',remark:'首次入柜'}
|
||||
],
|
||||
'CAB-NC-002|NC5-02':[
|
||||
{time:'2026-04-22 12:00',itemType:'净菜',itemName:'净西红柿',type:'取出',before:13.5,after:12.0,op:'张厨师',inOrder:'IN-20260416-032',remark:'西红柿炒蛋'},
|
||||
{time:'2026-04-22 10:15',itemType:'净菜',itemName:'净西红柿',type:'存入',before:0.0,after:15.0,op:'张厨师',inOrder:'IN-20260416-032',remark:'首次入柜'}
|
||||
],
|
||||
'CAB-NC-002|NC5-03':[
|
||||
{time:'2026-04-22 15:00',itemType:'净菜',itemName:'净洋葱',type:'取出',before:3.8,after:3.2,op:'王厨师',inOrder:'IN-20260416-033',remark:'洋葱炒蛋'},
|
||||
{time:'2026-04-22 08:40',itemType:'净菜',itemName:'净洋葱',type:'存入',before:0.0,after:5.0,op:'王厨师',inOrder:'IN-20260416-033',remark:'首次入柜'}
|
||||
],
|
||||
'CAB-RW-008|RW8-A1':[
|
||||
{time:'2026-04-16 09:00',itemType:'毛菜',itemName:'菠菜',type:'取出',before:248.0,after:225.0,op:'张仓管',inOrder:'IN-20260415-001',remark:'净菜加工领用'},
|
||||
{time:'2026-04-15 10:30',itemType:'毛菜',itemName:'菠菜',type:'存入',before:0.0,after:286.0,op:'张仓管',inOrder:'IN-20260415-001',remark:'毛菜入库'}
|
||||
],
|
||||
'CAB-RW-008|RW8-C1':[
|
||||
{time:'2026-04-16 08:00',itemType:'毛菜',itemName:'芹菜',type:'取出',before:18.0,after:12.0,op:'张仓管',inOrder:'IN-20260414-009',remark:'领用加工'}
|
||||
],
|
||||
'CAB-NP-001|NP1-A1':[
|
||||
{time:'2026-04-22 14:00',itemType:'净菜包',itemName:'净土豆丝净菜包',type:'取出',before:17.5,after:15.0,op:'王厨师',inOrder:'IN-20260422-031',remark:'净菜包领用'},
|
||||
{time:'2026-04-22 09:15',itemType:'净菜包',itemName:'净土豆丝净菜包',type:'存入',before:0.0,after:25.0,op:'张厨师',inOrder:'IN-20260422-031',remark:'包装完成入柜'}
|
||||
],
|
||||
'CAB-NP-001|NP1-A2':[
|
||||
{time:'2026-04-22 15:20',itemType:'净菜包',itemName:'净青椒丝净菜包',type:'取出',before:14.0,after:12.5,op:'李主管',inOrder:'IN-20260422-033',remark:'青椒肉丝备菜'},
|
||||
{time:'2026-04-22 09:30',itemType:'净菜包',itemName:'净青椒丝净菜包',type:'存入',before:0.0,after:18.0,op:'李主管',inOrder:'IN-20260422-033',remark:'包装完成入柜'}
|
||||
],
|
||||
'CAB-NP-001|NP1-B1':[
|
||||
{time:'2026-04-22 12:30',itemType:'净菜餐品包',itemName:'土豆烧肉餐品包',type:'取出',before:14.0,after:12.0,op:'张厨师',inOrder:'IN-20260422-032',remark:'午餐档口领用'},
|
||||
{time:'2026-04-22 10:30',itemType:'净菜餐品包',itemName:'土豆烧肉餐品包',type:'存入',before:0.0,after:18.0,op:'王厨师',inOrder:'IN-20260422-032',remark:'餐品包装完成入柜'}
|
||||
],
|
||||
'CAB-NP-001|NP1-B2':[
|
||||
{time:'2026-04-22 13:10',itemType:'净菜餐品包',itemName:'香菇滑鸡餐品包',type:'取出',before:10.8,after:9.6,op:'王厨师',inOrder:'IN-20260422-034',remark:'晚餐备餐'},
|
||||
{time:'2026-04-22 10:45',itemType:'净菜餐品包',itemName:'香菇滑鸡餐品包',type:'存入',before:0.0,after:12.0,op:'张厨师',inOrder:'IN-20260422-034',remark:'餐品包装完成入柜'}
|
||||
],
|
||||
'CAB-NP-001|NP1-C1':[
|
||||
{time:'2026-04-22 11:40',itemType:'净菜包',itemName:'净西兰花净菜包',type:'取出',before:8.8,after:8.0,op:'李主管',inOrder:'IN-20260422-035',remark:'取样检测'},
|
||||
{time:'2026-04-22 11:00',itemType:'净菜包',itemName:'净西兰花净菜包',type:'存入',before:0.0,after:10.0,op:'王厨师',inOrder:'IN-20260422-035',remark:'包装完成入柜'}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -866,9 +872,9 @@ function showSlotHistory(cabIdx,slotIdx){
|
||||
}
|
||||
SLOT_HIST_CURRENT={cabIdx:cabIdx,slotIdx:slotIdx,key:cab.code+'|'+slot.id,page:1};
|
||||
/* 上半区:格口信息 + 当前存储详情 */
|
||||
var typeTag=cab.cabinetType==='净菜柜'?'<span class="tag tag-blue">净菜柜</span>':'<span class="tag tag-green">毛菜柜</span>';
|
||||
var typeTag=cabinetTypeTag(cab.cabinetType);
|
||||
var statusTag=cab.status==='在线'?'<span class="tag tag-green">在线</span>':cab.status==='离线'?'<span class="tag tag-gray">离线</span>':'<span class="tag tag-red">故障</span>';
|
||||
var slotTypeTag=slot.itemType?(slot.itemType==='净菜'?'<span class="tag tag-blue">净菜</span>':'<span class="tag tag-green">毛菜</span>'):'—';
|
||||
var slotTypeTag=itemTypeTag(slot.itemType);
|
||||
var infoHtml='<div class="history-summary" style="grid-template-columns:none;">'
|
||||
+'<div class="history-summary__item"><span class="label">所属柜:</span><strong>'+cab.name+'</strong></div>'
|
||||
+'<div class="history-summary__item"><span class="label">设备编号:</span><strong>'+cab.code+'</strong></div>'
|
||||
@@ -919,7 +925,7 @@ function renderSlotHistoryPage(){
|
||||
var isPos=r.type==='存入';
|
||||
var deltaHtml='<span class="history-delta '+(isPos?'pos':'neg')+'">'+(isPos?'+':'-')+delta+' '+(isPos?'↑':'↓')+'</span>';
|
||||
var typeHtml=r.type==='存入'?'<span class="tag tag-blue">存入</span>':'<span class="tag tag-orange">取出</span>';
|
||||
var itemTypeHtml=r.itemType==='净菜'?'<span class="tag tag-blue">净菜</span>':'<span class="tag tag-green">毛菜</span>';
|
||||
var itemTypeHtml=itemTypeTag(r.itemType);
|
||||
html+='<tr><td>'+r.time+'</td><td>'+itemTypeHtml+'</td><td>'+r.itemName+'</td><td>'+typeHtml+'</td><td>'+r.before.toFixed(1)+'</td><td>'+r.after.toFixed(1)+'</td><td>'+deltaHtml+'</td><td>'+r.op+'</td><td style="color:var(--primary);">'+r.inOrder+'</td><td>'+(r.remark||'—')+'</td></tr>';
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user