feat: 餐线营养秤字段与时间筛选调整 + 净配菜单名改净菜组配
- din-terminal.html: 重量变化数据提交重量改较上次变化(正数绿/负数红/0灰)、当前重量改当前余量并取消两列筛选;四维度时间列筛选改年月日/时间(到分钟)日期时间组件 - prod-qc-daily.html: 侧边栏菜单净配改净菜组配
This commit is contained in:
@@ -320,6 +320,14 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
.th-filter-pop__item{padding:6px 10px;font-size:var(--font-sm);color:var(--text-primary);border-radius:4px;cursor:pointer;white-space:nowrap;}
|
||||
.th-filter-pop__item:hover{background:#f5f8ff;}
|
||||
.th-filter-pop__item.active{color:var(--primary);background:#e8f1ff;font-weight:500;}
|
||||
.th-filter-pop--dt{min-width:240px;max-height:none;overflow:visible;padding:10px;}
|
||||
.th-filter-pop__dt-label{font-size:var(--font-xs);color:var(--text-secondary);margin-bottom:6px;}
|
||||
.th-filter-pop__dt-input{width:100%;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);box-sizing:border-box;color:var(--text-primary);}
|
||||
.th-filter-pop__dt-actions{display:flex;gap:6px;margin-top:10px;justify-content:flex-end;}
|
||||
.th-filter-pop__dt-actions .dt-btn{height:28px;padding:0 14px;border:1px solid var(--border);background:#fff;border-radius:var(--radius-sm);cursor:pointer;font-size:var(--font-xs);color:var(--text-secondary);}
|
||||
.th-filter-pop__dt-actions .dt-btn:hover{border-color:var(--primary);color:var(--primary);}
|
||||
.th-filter-pop__dt-actions .dt-btn--primary{background:var(--primary);border-color:var(--primary);color:#fff;}
|
||||
.th-filter-pop__dt-actions .dt-btn--primary:hover{color:#fff;opacity:.9;}
|
||||
/* ===== 餐线营养秤概览(子页切换) ===== */
|
||||
.scale-sub-tabs{display:flex;gap:8px;margin-bottom:16px;border-bottom:1px solid var(--border);}
|
||||
.scale-sub-tab{padding:8px 16px;font-size:var(--font-sm);color:var(--text-secondary);cursor:pointer;border-bottom:2px solid transparent;white-space:nowrap;user-select:none;transition:all .15s;}
|
||||
@@ -789,23 +797,23 @@ var SCALE_FILTERS={status:{},face:{},weight:{},log:{}};
|
||||
/* 每个 Tab 的列定义:key 列标识 / label 表头文字 / field 数据字段 */
|
||||
var SCALE_COLS={
|
||||
status:[
|
||||
{key:'time',label:'日期/时间',field:'time'},
|
||||
{key:'time',label:'日期/时间',field:'time',type:'datetime'},
|
||||
{key:'item',label:'监控项',field:'item'},
|
||||
{key:'event',label:'事件',field:'event'}
|
||||
],
|
||||
face:[
|
||||
{key:'start',label:'开始时间',field:'start'},
|
||||
{key:'end',label:'结束时间',field:'end'},
|
||||
{key:'start',label:'开始时间',field:'start',type:'datetime'},
|
||||
{key:'end',label:'结束时间',field:'end',type:'datetime'},
|
||||
{key:'status',label:'状态',field:'status'}
|
||||
],
|
||||
weight:[
|
||||
{key:'start',label:'日期/时间',field:'start'},
|
||||
{key:'start',label:'日期/时间',field:'start',type:'datetime'},
|
||||
{key:'target',label:'用户',field:'target'},
|
||||
{key:'submit',label:'提交重量',field:'submit'},
|
||||
{key:'total',label:'当前重量',field:'total'}
|
||||
{key:'delta',label:'较上次变化',field:'delta',filterable:false},
|
||||
{key:'total',label:'当前余量',field:'total',filterable:false}
|
||||
],
|
||||
log:[
|
||||
{key:'time',label:'日期/时间',field:'time'},
|
||||
{key:'time',label:'日期/时间',field:'time',type:'datetime'},
|
||||
{key:'type',label:'报错类型',field:'type'},
|
||||
{key:'content',label:'报错内容',field:'code'}
|
||||
]
|
||||
@@ -816,13 +824,14 @@ var SCALE_WEIGHT_RECORDS=[];
|
||||
var SCALE_LOG_RECORDS=[];
|
||||
|
||||
/* 生成带筛选图标的表头单元格 */
|
||||
function scaleTh(colKey,label,tabKey){
|
||||
function scaleTh(colKey,label,tabKey,filterable){
|
||||
if(filterable===false)return label;
|
||||
var active=SCALE_FILTERS[tabKey]&&SCALE_FILTERS[tabKey][colKey];
|
||||
return label+'<span class="th-filter'+(active?' th-filter--active':'')+'" data-tab="'+tabKey+'" data-col="'+colKey+'" onclick="toggleThFilter(event)">▾</span>';
|
||||
}
|
||||
/* 生成表头(可选追加操作列,操作列不可筛选) */
|
||||
function scaleThead(tabKey,actionLabel){
|
||||
var ths=SCALE_COLS[tabKey].map(function(c){return '<th>'+scaleTh(c.key,c.label,tabKey)+'</th>';}).join('');
|
||||
var ths=SCALE_COLS[tabKey].map(function(c){return '<th>'+scaleTh(c.key,c.label,tabKey,c.filterable)+'</th>';}).join('');
|
||||
if(actionLabel)ths+='<th>'+actionLabel+'</th>';
|
||||
return '<thead><tr>'+ths+'</tr></thead>';
|
||||
}
|
||||
@@ -841,12 +850,22 @@ function getFilteredScaleRows(tabKey){
|
||||
var cols=SCALE_COLS[tabKey]||[];
|
||||
return rows.filter(function(r){
|
||||
for(var i=0;i<cols.length;i++){
|
||||
var v=f[cols[i].key];
|
||||
if(v && r[cols[i].field]!==v)return false;
|
||||
var col=cols[i];
|
||||
var v=f[col.key];
|
||||
if(!v)continue;
|
||||
if(col.type==='datetime'){
|
||||
if(normalizeMinute(r[col.field])!==normalizeMinute(v))return false;
|
||||
}else if(r[col.field]!==v){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
/* 时间戳截断到分钟并统一为 datetime-local 格式(YYYY-MM-DDTHH:mm) */
|
||||
function normalizeMinute(ts){
|
||||
return String(ts).slice(0,16).replace(' ','T');
|
||||
}
|
||||
/* 取某列去重值(用于筛选下拉) */
|
||||
function getScaleColValues(tabKey,colKey){
|
||||
var col=(SCALE_COLS[tabKey]||[]).filter(function(c){return c.key===colKey;})[0];
|
||||
@@ -865,21 +884,45 @@ function toggleThFilter(e){
|
||||
closeThFilter();
|
||||
var icon=e.currentTarget;
|
||||
var tabKey=icon.dataset.tab,colKey=icon.dataset.col;
|
||||
var col=(SCALE_COLS[tabKey]||[]).filter(function(c){return c.key===colKey;})[0];
|
||||
var isDt=col&&col.type==='datetime';
|
||||
var pop=document.createElement('div');
|
||||
pop.className='th-filter-pop'+(isDt?' th-filter-pop--dt':'');
|
||||
pop.id='thFilterPop';
|
||||
pop.innerHTML=isDt?buildDatetimePopHtml(tabKey,colKey):buildValuePopHtml(tabKey,colKey);
|
||||
document.body.appendChild(pop);
|
||||
var rect=icon.getBoundingClientRect();
|
||||
pop.style.left=Math.min(rect.left,window.innerWidth-(isDt?250:170))+'px';
|
||||
pop.style.top=(rect.bottom+4)+'px';
|
||||
document.addEventListener('click',onDocClickForFilter,{once:true});
|
||||
}
|
||||
/* 普通列:去重取值下拉 */
|
||||
function buildValuePopHtml(tabKey,colKey){
|
||||
var values=getScaleColValues(tabKey,colKey);
|
||||
var cur=(SCALE_FILTERS[tabKey]&&SCALE_FILTERS[tabKey][colKey])||'';
|
||||
var pop=document.createElement('div');
|
||||
pop.className='th-filter-pop';
|
||||
pop.id='thFilterPop';
|
||||
var html='<div class="th-filter-pop__item'+(cur===''?' active':'')+'" onclick="pickThFilter(\''+tabKey+'\',\''+colKey+'\',\'\')">全部</div>';
|
||||
values.forEach(function(v){
|
||||
html+='<div class="th-filter-pop__item'+(cur===v?' active':'')+'" onclick="pickThFilter(\''+tabKey+'\',\''+colKey+'\',\''+escapeAttr(v)+'\')">'+escapeHtml(v)+'</div>';
|
||||
});
|
||||
pop.innerHTML=html;
|
||||
document.body.appendChild(pop);
|
||||
var rect=icon.getBoundingClientRect();
|
||||
pop.style.left=Math.min(rect.left,window.innerWidth-170)+'px';
|
||||
pop.style.top=(rect.bottom+4)+'px';
|
||||
document.addEventListener('click',onDocClickForFilter,{once:true});
|
||||
return html;
|
||||
}
|
||||
/* 时间列:年月日/时间(到分钟)选择组件 */
|
||||
function buildDatetimePopHtml(tabKey,colKey){
|
||||
var cur=(SCALE_FILTERS[tabKey]&&SCALE_FILTERS[tabKey][colKey])||'';
|
||||
return '<div class="th-filter-pop__dt-label">选择日期时间(到分钟)</div>'
|
||||
+'<input type="datetime-local" id="dtFilterInput" class="th-filter-pop__dt-input" step="60" value="'+escapeAttr(cur)+'">'
|
||||
+'<div class="th-filter-pop__dt-actions">'
|
||||
+'<button type="button" class="dt-btn" onclick="applyDatetimeFilter(\''+tabKey+'\',\''+colKey+'\',true)">清除</button>'
|
||||
+'<button type="button" class="dt-btn dt-btn--primary" onclick="applyDatetimeFilter(\''+tabKey+'\',\''+colKey+'\',false)">确定</button>'
|
||||
+'</div>';
|
||||
}
|
||||
function applyDatetimeFilter(tabKey,colKey,clear){
|
||||
var val='';
|
||||
if(!clear){
|
||||
var inp=document.getElementById('dtFilterInput');
|
||||
val=inp?inp.value:'';
|
||||
}
|
||||
pickThFilter(tabKey,colKey,val);
|
||||
}
|
||||
function onDocClickForFilter(e){
|
||||
if(!e.target.closest('.th-filter-pop'))closeThFilter();
|
||||
@@ -1070,15 +1113,19 @@ function formatFaceVideoTime(t){
|
||||
return (m<10?'0'+m:m)+':'+(s<10?'0'+s:s);
|
||||
}
|
||||
|
||||
/* 维度3:重量变化数据 — 日期/时间 / 用户 / 提交重量 / 当前重量 */
|
||||
/* 维度3:重量变化数据 — 日期/时间 / 用户 / 较上次变化 / 当前余量 */
|
||||
SCALE_WEIGHT_RECORDS=[
|
||||
{start:'2026-08-25 08:15:22.701',target:'EMP0234 · 张伟',submit:'436.5',total:'486.5'},
|
||||
{start:'2026-08-25 08:14:50.512',target:'无用户',submit:'412.0',total:'462.0'},
|
||||
{start:'2026-08-25 08:13:46.098',target:'EMP0357 · 王强',submit:'—',total:'501.2'},
|
||||
{start:'2026-08-25 08:12:08.876',target:'无用户',submit:'0.0',total:'0.0'}
|
||||
{start:'2026-08-25 08:15:22.701',target:'EMP0234 · 张伟',delta:'+50.0',total:'486.5'},
|
||||
{start:'2026-08-25 08:14:50.512',target:'无用户',delta:'+412.0',total:'462.0'},
|
||||
{start:'2026-08-25 08:13:46.098',target:'EMP0357 · 王强',delta:'-62.0',total:'501.2'},
|
||||
{start:'2026-08-25 08:12:08.876',target:'无用户',delta:'0.0',total:'0.0'}
|
||||
];
|
||||
function renderScaleWeight(){
|
||||
const rows=getFilteredScaleRows('weight').map(w=>`<tr><td>${w.start}</td><td>${w.target}</td><td>${w.submit}</td><td>${w.total}</td></tr>`).join('');
|
||||
const rows=getFilteredScaleRows('weight').map(w=>{
|
||||
const num=parseFloat(w.delta);
|
||||
const color=num>0?'var(--success)':(num<0?'var(--danger)':'var(--text-secondary)');
|
||||
return `<tr><td>${w.start}</td><td>${w.target}</td><td><span style="color:${color}">${w.delta}</span></td><td>${w.total}</td></tr>`;
|
||||
}).join('');
|
||||
return `<div class="ov-section-title">重量变化数据</div>
|
||||
<div style="overflow-x:auto"><table class="ov-daily-table">${scaleThead('weight')}<tbody>${rows}</tbody></table></div>`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# 营养用餐管理端 CHANGELOG
|
||||
|
||||
## [2026-08-27] feat: 餐线营养秤字段与时间筛选调整 + 净配菜单名改净菜组配
|
||||
|
||||
- `din-terminal.html` 设备概览「餐线营养秤」:
|
||||
- 重量变化数据 Tab:「提交重量」→「较上次变化」(正数绿/负数红/0 灰),「当前重量」→「当前余量」;「较上次变化」「当前余量」两列取消筛选
|
||||
- 四个维度时间类列(日期/时间、开始时间、结束时间)筛选由去重下拉改为年月日/时间(到分钟)日期时间选择组件
|
||||
- `prod-qc-daily.html` 侧边栏菜单:「净配」→「净菜组配」
|
||||
|
||||
## [2026-08-27] refactor: 餐线营养秤概览字段调整
|
||||
|
||||
- `din-terminal.html` 设备概览「餐线营养秤」:
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user