feat: 餐品定价规格拆分为餐品规格与餐品净菜包规格
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
bf08a9614b
commit
39547a8bb6
@@ -607,9 +607,16 @@ var STEP_HTML = [
|
||||
</div>
|
||||
<div class="form-subtitle">📐 餐品规格</div>
|
||||
<table class="spec-table" id="specTable">
|
||||
<thead><tr><th style="width:200px;">规格(克重/份型)</th><th>餐品售价(元/份)</th><th>净菜包毛利率(%)</th><th>净菜包售价(元/份)</th><th>外售净菜包</th><th>默认</th><th style="width:60px;">操作</th></tr></thead>
|
||||
<thead><tr><th style="width:200px;">规格(克重/份型)</th><th>餐品售价(元/份)</th><th>上架状态</th><th>默认</th><th style="width:60px;">操作</th></tr></thead>
|
||||
<tbody id="specBody">
|
||||
<tr class="spec-add-row" onclick="addSpecRow(this)"><td colspan="7" style="text-align:center;">+ 添加规格</td></tr>
|
||||
<tr class="spec-add-row" onclick="addSpecRow(this)"><td colspan="5" style="text-align:center;">+ 添加规格</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="form-subtitle">🥬 餐品净菜包规格</div>
|
||||
<table class="spec-table" id="cleanSpecTable">
|
||||
<thead><tr><th style="width:200px;">规格(克重/份型)</th><th>净菜包毛利率(%)</th><th>净菜包售价(元/份)</th><th>上架状态</th><th>默认</th><th style="width:60px;">操作</th></tr></thead>
|
||||
<tbody id="cleanSpecBody">
|
||||
<tr class="spec-add-row" onclick="addCleanSpecRow(this)"><td colspan="6" style="text-align:center;">+ 添加净菜包规格</td></tr>
|
||||
</tbody>
|
||||
</table>`,
|
||||
|
||||
@@ -733,10 +740,17 @@ function buildPortionOptions(selected){
|
||||
return html;
|
||||
}
|
||||
function addSpecRow(addRow){
|
||||
if(document.querySelector('.spec-edit-row')) return;
|
||||
if(document.querySelector('#specBody .spec-edit-row')) return;
|
||||
var tr=document.createElement('tr');
|
||||
tr.className='spec-edit-row';
|
||||
tr.innerHTML='<td><div class="spec-input-group"><input class="form-input" type="number" placeholder="克重" min="0"><select class="form-select">'+buildPortionOptions()+'</select></div></td><td><input class="form-input" type="number" placeholder="餐品售价"></td><td><input class="form-input" type="number" placeholder="净菜包毛利率" min="0" max="100" step="0.1"></td><td><input class="form-input" type="number" placeholder="净菜包售价"></td><td><div class="sale-toggle" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">下架</span></div></td><td></td><td><button class="btn-text btn-text--primary" onclick="confirmSpec(this)">确定</button> <button class="btn-text btn-text--danger" onclick="cancelSpec(this)">取消</button></td>';
|
||||
tr.innerHTML='<td><div class="spec-input-group"><input class="form-input" type="number" placeholder="克重" min="0"><select class="form-select">'+buildPortionOptions()+'</select></div></td><td><input class="form-input" type="number" placeholder="餐品售价"></td><td><div class="sale-toggle" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">下架</span></div></td><td></td><td><button class="btn-text btn-text--primary" onclick="confirmSpec(this)">确定</button> <button class="btn-text btn-text--danger" onclick="cancelSpec(this)">取消</button></td>';
|
||||
addRow.parentNode.insertBefore(tr,addRow);
|
||||
}
|
||||
function addCleanSpecRow(addRow){
|
||||
if(document.querySelector('#cleanSpecBody .spec-edit-row')) return;
|
||||
var tr=document.createElement('tr');
|
||||
tr.className='spec-edit-row';
|
||||
tr.innerHTML='<td><div class="spec-input-group"><input class="form-input" type="number" placeholder="克重" min="0"><select class="form-select">'+buildPortionOptions()+'</select></div></td><td><input class="form-input" type="number" placeholder="净菜包毛利率" min="0" max="100" step="0.1"></td><td><input class="form-input" type="number" placeholder="净菜包售价"></td><td><div class="sale-toggle" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">下架</span></div></td><td></td><td><button class="btn-text btn-text--primary" onclick="confirmCleanSpec(this)">确定</button> <button class="btn-text btn-text--danger" onclick="cancelSpec(this)">取消</button></td>';
|
||||
addRow.parentNode.insertBefore(tr,addRow);
|
||||
}
|
||||
function confirmSpec(btn){
|
||||
@@ -745,21 +759,35 @@ function confirmSpec(btn){
|
||||
var gramInput=group.querySelector('input[type="number"]');
|
||||
var sel=group.querySelector('select');
|
||||
var priceInput=row.querySelectorAll('input[type="number"]')[1];
|
||||
var cleanMarginInput=row.querySelectorAll('input[type="number"]')[2];
|
||||
var cleanPriceInput=row.querySelectorAll('input[type="number"]')[3];
|
||||
var portion=sel?sel.value:'';
|
||||
var gram=gramInput?gramInput.value.trim():'';
|
||||
var price=priceInput?priceInput.value.trim():'';
|
||||
var cleanMargin=cleanMarginInput?cleanMarginInput.value.trim():'';
|
||||
var cleanPrice=cleanPriceInput?cleanPriceInput.value.trim():'';
|
||||
if(!portion||portion==='请选择'||!gram||!price){alert('请填写完整信息');return;}
|
||||
var cleanMarginText = cleanMargin ? parseFloat(cleanMargin).toFixed(1)+'%' : '—';
|
||||
var cleanPriceText = cleanPrice ? parseFloat(cleanPrice).toFixed(2) : '—';
|
||||
var saleActive = !!row.querySelector('.sale-toggle.active');
|
||||
var saleHtml = '<div class="sale-toggle'+(saleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(saleActive?'上架':'下架')+'</span></div>';
|
||||
var specViewHtml='<div class="spec-cell-view"><span class="spec-cell-view__gram">'+parseFloat(gram)+'g</span><span class="spec-cell-view__sep">/</span><span class="spec-cell-view__name">'+portion+'</span></div>';
|
||||
row.className='';
|
||||
row.innerHTML='<td>'+specViewHtml+'</td><td>'+parseFloat(price).toFixed(2)+'</td><td>'+cleanMarginText+'</td><td>'+cleanPriceText+'</td><td>'+saleHtml+'</td><td><div class="spec-toggle" onclick="toggleDefault(this)"><span class="spec-toggle__switch"></span><span class="spec-toggle__label">默认</span></div></td><td><button class="btn-text btn-text--danger" onclick="removeSpec(this)">删除</button></td>';
|
||||
row.innerHTML='<td>'+specViewHtml+'</td><td>'+parseFloat(price).toFixed(2)+'</td><td>'+saleHtml+'</td><td><div class="spec-toggle" onclick="toggleDefault(this)"><span class="spec-toggle__switch"></span><span class="spec-toggle__label">默认</span></div></td><td><button class="btn-text btn-text--danger" onclick="removeSpec(this)">删除</button></td>';
|
||||
}
|
||||
function confirmCleanSpec(btn){
|
||||
var row=btn.closest('tr');
|
||||
var group=row.querySelector('.spec-input-group');
|
||||
var gramInput=group.querySelector('input[type="number"]');
|
||||
var sel=group.querySelector('select');
|
||||
var cleanMarginInput=row.querySelectorAll('input[type="number"]')[1];
|
||||
var cleanPriceInput=row.querySelectorAll('input[type="number"]')[2];
|
||||
var portion=sel?sel.value:'';
|
||||
var gram=gramInput?gramInput.value.trim():'';
|
||||
var cleanMargin=cleanMarginInput?cleanMarginInput.value.trim():'';
|
||||
var cleanPrice=cleanPriceInput?cleanPriceInput.value.trim():'';
|
||||
if(!portion||portion==='请选择'||!gram||!cleanMargin||!cleanPrice){alert('请填写完整信息');return;}
|
||||
var cleanMarginText = parseFloat(cleanMargin).toFixed(1)+'%';
|
||||
var cleanPriceText = parseFloat(cleanPrice).toFixed(2);
|
||||
var saleActive = !!row.querySelector('.sale-toggle.active');
|
||||
var saleHtml = '<div class="sale-toggle'+(saleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(saleActive?'上架':'下架')+'</span></div>';
|
||||
var specViewHtml='<div class="spec-cell-view"><span class="spec-cell-view__gram">'+parseFloat(gram)+'g</span><span class="spec-cell-view__sep">/</span><span class="spec-cell-view__name">'+portion+'</span></div>';
|
||||
row.className='';
|
||||
row.innerHTML='<td>'+specViewHtml+'</td><td>'+cleanMarginText+'</td><td>'+cleanPriceText+'</td><td>'+saleHtml+'</td><td><div class="spec-toggle" onclick="toggleDefault(this)"><span class="spec-toggle__switch"></span><span class="spec-toggle__label">默认</span></div></td><td><button class="btn-text btn-text--danger" onclick="removeSpec(this)">删除</button></td>';
|
||||
}
|
||||
function toggleCleanSale(el){
|
||||
var active = el.classList.toggle('active');
|
||||
@@ -1011,13 +1039,13 @@ function recalcAfterIngredientChange(){
|
||||
}
|
||||
function cancelSpec(btn){btn.closest('tr').remove();}
|
||||
function toggleDefault(el){
|
||||
var tbody=document.getElementById('specBody');
|
||||
var tbody=el.closest('tbody');
|
||||
if(tbody){tbody.querySelectorAll('.spec-toggle').forEach(function(t){t.classList.remove('active');});}
|
||||
el.classList.add('active');
|
||||
}
|
||||
function removeSpec(btn){
|
||||
var row=btn.closest('tr');var wasDefault=row.querySelector('.spec-toggle.active');row.remove();
|
||||
if(wasDefault){var first=document.querySelector('#specBody tr:not(.spec-add-row):not(.spec-edit-row) .spec-toggle');if(first)first.classList.add('active');}
|
||||
var row=btn.closest('tr');var tbody=row.closest('tbody');var wasDefault=row.querySelector('.spec-toggle.active');row.remove();
|
||||
if(wasDefault){var first=tbody.querySelector('tr:not(.spec-add-row):not(.spec-edit-row) .spec-toggle');if(first)first.classList.add('active');}
|
||||
}
|
||||
|
||||
/* 食材清单数据:每个食材含 code + 多个净菜(netName 净菜形式,本身可含-分隔符) + 净菜规格别名(aliases 多规格时需先选净菜再选规格) */
|
||||
@@ -1251,7 +1279,8 @@ var EDIT_DATA = {
|
||||
pricing:{basePrice:'6.00',ingredientCost:4.80,per100gCost:2.40,profitRate:150,suggestedPrice:'6.00'},
|
||||
promoVideos:[{title:'红烧肉宣传片',duration:'1分30秒',size:'48MB',enabled:true},{title:'制作工艺展示',duration:'2分15秒',size:'72MB',enabled:false}],
|
||||
crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],
|
||||
specs:[{portion:'标准份',baseGram:200,unit:'份',price:12,cleanMargin:55.0,cleanPrice:9,cleanSale:false,isDefault:true},{portion:'小份',baseGram:120,unit:'份',price:8,cleanMargin:50.0,cleanPrice:6,cleanSale:false,isDefault:false},{portion:'大份',baseGram:300,unit:'份',price:18,cleanMargin:58.0,cleanPrice:13,cleanSale:false,isDefault:false}],
|
||||
specs:[{portion:'标准份',baseGram:200,unit:'份',price:12,sale:true,isDefault:true},{portion:'小份',baseGram:120,unit:'份',price:8,sale:true,isDefault:false},{portion:'大份',baseGram:300,unit:'份',price:18,sale:false,isDefault:false}],
|
||||
cleanSpecs:[{portion:'标准份',baseGram:200,unit:'份',cleanMargin:55.0,cleanPrice:9,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:120,unit:'份',cleanMargin:50.0,cleanPrice:6,cleanSale:false,cleanDefault:false},{portion:'大份',baseGram:300,unit:'份',cleanMargin:58.0,cleanPrice:13,cleanSale:false,cleanDefault:false}],
|
||||
cookedWeight:200,
|
||||
composition:[{code:'YQ-001',name:'猪五花肉',netName:'红烧块',amount:150,type:'主材'},{code:'YQ-023',name:'冰糖',netName:'糖浆',amount:20,type:'调料'},{code:'YQ-017',name:'生抽',netName:'调味汁',amount:15,type:'调料'},{code:'YQ-020',name:'料酒',netName:'料酒汁',amount:10,type:'调料'}],
|
||||
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,temp:100,steps:6,desc:'1.五花肉切块焯水;2.锅中煎至金黄;3.加冰糖炒色;4.加酱油料酒;5.加水小火炖40分钟;6.大火收汁'},
|
||||
@@ -1294,7 +1323,8 @@ if(pageMode==='convert' && INNOV){
|
||||
EDIT_DATA.tag = INNOV.tag;
|
||||
EDIT_DATA.status = '上架';
|
||||
EDIT_DATA.crowd = [];
|
||||
EDIT_DATA.specs = [{portion:'标准份',baseGram:350,unit:'份',price:15,cleanMargin:52.0,cleanPrice:10,cleanSale:false,isDefault:true},{portion:'小份',baseGram:200,unit:'份',price:10,cleanMargin:48.0,cleanPrice:7,cleanSale:false,isDefault:false}];
|
||||
EDIT_DATA.specs = [{portion:'标准份',baseGram:350,unit:'份',price:15,sale:true,isDefault:true},{portion:'小份',baseGram:200,unit:'份',price:10,sale:false,isDefault:false}];
|
||||
EDIT_DATA.cleanSpecs = [{portion:'标准份',baseGram:350,unit:'份',cleanMargin:52.0,cleanPrice:10,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:200,unit:'份',cleanMargin:48.0,cleanPrice:7,cleanSale:false,cleanDefault:false}];
|
||||
EDIT_DATA.cookedWeight = INNOV.cookedWeight || 200;
|
||||
EDIT_DATA.composition = INNOV.composition;
|
||||
EDIT_DATA.cook = INNOV.cook;
|
||||
@@ -1422,19 +1452,33 @@ function fillEditData(){
|
||||
if(inputs[1]) inputs[1].value = d.pricing.profitRate||'';
|
||||
if(inputs[2]) inputs[2].value = (d.pricing.ingredientCost||0).toFixed(2);
|
||||
}
|
||||
// 预填规格表
|
||||
// 预填餐品规格表
|
||||
var specBody = container.querySelector('#specTable tbody');
|
||||
if(specBody){
|
||||
specBody.innerHTML = '';
|
||||
d.specs.forEach(function(s){
|
||||
(d.specs||[]).forEach(function(s){
|
||||
var tr = document.createElement('tr');
|
||||
var saleActive = s.sale === true;
|
||||
var saleHtml = '<div class="sale-toggle'+(saleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(saleActive?'上架':'下架')+'</span></div>';
|
||||
var portionVal = s.portion || s.spec || '';
|
||||
var gramVal = s.baseGram!==undefined?s.baseGram:(s.weight!==undefined?s.weight:'');
|
||||
tr.innerHTML = '<td><div class="spec-input-group"><input class="form-input form-input--sm" type="number" value="'+gramVal+'"><select class="form-select form-select--sm">'+buildPortionOptions(portionVal)+'</select></div></td><td><input class="form-input form-input--sm" value="'+s.price+'"></td><td>'+saleHtml+'</td><td><input type="checkbox"'+(s.isDefault?' checked':'')+'></td><td><span class="link" onclick="this.closest(\'tr\').remove()">删除</span></td>';
|
||||
specBody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
// 预填餐品净菜包规格表
|
||||
var cleanSpecBody = container.querySelector('#cleanSpecTable tbody');
|
||||
if(cleanSpecBody){
|
||||
cleanSpecBody.innerHTML = '';
|
||||
(d.cleanSpecs||[]).forEach(function(s){
|
||||
var tr = document.createElement('tr');
|
||||
var saleActive = s.cleanSale === true;
|
||||
var saleHtml = '<div class="sale-toggle'+(saleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(saleActive?'上架':'下架')+'</span></div>';
|
||||
var portionVal = s.portion || s.spec || '';
|
||||
var gramVal = s.baseGram!==undefined?s.baseGram:(s.weight!==undefined?s.weight:'');
|
||||
var cleanMarginVal = s.cleanMargin!==undefined?s.cleanMargin:'';
|
||||
tr.innerHTML = '<td><div class="spec-input-group"><input class="form-input form-input--sm" type="number" value="'+gramVal+'"><select class="form-select form-select--sm">'+buildPortionOptions(portionVal)+'</select></div></td><td><input class="form-input form-input--sm" value="'+s.price+'"></td><td><input class="form-input form-input--sm" type="number" step="0.1" min="0" max="100" value="'+cleanMarginVal+'" placeholder="净菜包毛利率"></td><td><input class="form-input form-input--sm" value="'+(s.cleanPrice!==undefined?s.cleanPrice:'')+'"></td><td>'+saleHtml+'</td><td><input type="checkbox"'+(s.isDefault?' checked':'')+'></td><td><span class="link" onclick="this.closest(\'tr\').remove()">删除</span></td>';
|
||||
specBody.appendChild(tr);
|
||||
tr.innerHTML = '<td><div class="spec-input-group"><input class="form-input form-input--sm" type="number" value="'+gramVal+'"><select class="form-select form-select--sm">'+buildPortionOptions(portionVal)+'</select></div></td><td><input class="form-input form-input--sm" type="number" step="0.1" min="0" max="100" value="'+cleanMarginVal+'" placeholder="净菜包毛利率"></td><td><input class="form-input form-input--sm" value="'+(s.cleanPrice!==undefined?s.cleanPrice:'')+'"></td><td>'+saleHtml+'</td><td><input type="checkbox"'+(s.cleanDefault?' checked':'')+'></td><td><span class="link" onclick="this.closest(\'tr\').remove()">删除</span></td>';
|
||||
cleanSpecBody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
} else if(currentStep===3){
|
||||
|
||||
@@ -454,7 +454,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<script>
|
||||
// 餐品数据
|
||||
const DISHES = [
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:12,cleanMargin:55.0,cleanPrice:9,cleanSale:false,isDefault:true},{portion:'小份',baseGram:120,unit:'份',price:8,cleanMargin:50.0,cleanPrice:6,cleanSale:false},{portion:'大份',baseGram:300,unit:'份',price:18,cleanMargin:58.0,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',updateTime:'2026-08-10',
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:12,sale:true,isDefault:true},{portion:'小份',baseGram:120,unit:'份',price:8,sale:true,isDefault:false},{portion:'大份',baseGram:300,unit:'份',price:18,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:200,unit:'份',cleanMargin:55.0,cleanPrice:9,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:120,unit:'份',cleanMargin:50.0,cleanPrice:6,cleanSale:false,cleanDefault:false},{portion:'大份',baseGram:300,unit:'份',cleanMargin:58.0,cleanPrice:13,cleanSale:false,cleanDefault:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',updateTime:'2026-08-10',
|
||||
pricing:{basePrice:'6.00',ingredientCost:4.80,per100gCost:2.40,profitRate:150,suggestedPrice:'6.00'},
|
||||
promoVideos:[{title:'红烧肉宣传片',duration:'1分30秒',size:'48MB',enabled:true},{title:'制作工艺展示',duration:'2分15秒',size:'72MB',enabled:false}],
|
||||
crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',cookedWeight:200,
|
||||
@@ -465,7 +465,7 @@ const DISHES = [
|
||||
color:{mainColor:'红褐色',uniformity:4,gloss:4,freshness:4,appearance:4,colorScore:4.0,colorGrade:'良好'},
|
||||
texture:{hardness:2,sticky:3,crispy:1,smooth:4,elastic:3,textureScore:3.6,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{portion:'标准份',baseGram:150,unit:'份',price:6,cleanMargin:60.0,cleanPrice:4.5,cleanSale:false,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:4,cleanMargin:55.0,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-08-09',
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{portion:'标准份',baseGram:150,unit:'份',price:6,sale:true,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:4,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:150,unit:'份',cleanMargin:60.0,cleanPrice:4.5,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:100,unit:'份',cleanMargin:55.0,cleanPrice:3,cleanSale:false,cleanDefault:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-08-09',
|
||||
pricing:{basePrice:'3.00',ingredientCost:1.80,per100gCost:1.20,profitRate:150,suggestedPrice:'3.00'},
|
||||
promoVideos:[{title:'清炒时蔬宣传片',duration:'45秒',size:'18MB',enabled:true}],
|
||||
crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',cookedWeight:140,
|
||||
@@ -476,7 +476,7 @@ const DISHES = [
|
||||
color:{mainColor:'翠绿色',uniformity:5,gloss:4,freshness:5,appearance:4,colorScore:4.5,colorGrade:'优秀'},
|
||||
texture:{hardness:3,sticky:1,crispy:4,smooth:3,elastic:2,textureScore:3.4,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{portion:'标准份',baseGram:300,unit:'份',price:5,cleanMargin:42.0,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',updateTime:'2026-08-08',
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{portion:'标准份',baseGram:300,unit:'份',price:5,sale:true,isDefault:true}],cleanSpecs:[{portion:'标准份',baseGram:300,unit:'份',cleanMargin:42.0,cleanPrice:3.5,cleanSale:false,cleanDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',updateTime:'2026-08-08',
|
||||
pricing:{basePrice:'1.67',ingredientCost:1.20,per100gCost:0.40,profitRate:150,suggestedPrice:'1.00'},
|
||||
promoVideos:[{title:'番茄鸡蛋汤宣传片',duration:'1分钟',size:'24MB',enabled:true},{title:'家常做法教程',duration:'3分钟',size:'80MB',enabled:true}],
|
||||
crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',cookedWeight:300,
|
||||
@@ -487,7 +487,7 @@ const DISHES = [
|
||||
color:{mainColor:'橙红色',uniformity:4,gloss:3,freshness:5,appearance:4,colorScore:4.0,colorGrade:'良好'},
|
||||
texture:{hardness:1,sticky:2,crispy:1,smooth:5,elastic:2,textureScore:3.8,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:3,cleanMargin:38.0,cleanPrice:2,cleanSale:false,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:2,cleanMargin:35.0,cleanPrice:1.5,cleanSale:false},{portion:'大份',baseGram:300,unit:'份',price:4,cleanMargin:40.0,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-07-22',
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:3,sale:true,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:2,sale:true,isDefault:false},{portion:'大份',baseGram:300,unit:'份',price:4,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:200,unit:'份',cleanMargin:38.0,cleanPrice:2,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:100,unit:'份',cleanMargin:35.0,cleanPrice:1.5,cleanSale:false,cleanDefault:false},{portion:'大份',baseGram:300,unit:'份',cleanMargin:40.0,cleanPrice:3,cleanSale:false,cleanDefault:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-07-22',
|
||||
pricing:{basePrice:'1.50',ingredientCost:1.00,per100gCost:0.50,profitRate:150,suggestedPrice:'1.25'},
|
||||
promoVideos:[],
|
||||
crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',cookedWeight:200,
|
||||
@@ -498,7 +498,7 @@ const DISHES = [
|
||||
color:{mainColor:'乳白色',uniformity:5,gloss:4,freshness:4,appearance:4,colorScore:4.2,colorGrade:'良好'},
|
||||
texture:{hardness:2,sticky:3,crispy:1,smooth:3,elastic:2,textureScore:3.2,textureGrade:'一般'}
|
||||
},
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{portion:'标准份',baseGram:180,unit:'份',price:14,cleanMargin:62.0,cleanPrice:10,cleanSale:false,isDefault:true},{portion:'大份',baseGram:280,unit:'份',price:20,cleanMargin:65.0,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',updateTime:'2026-08-05',
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{portion:'标准份',baseGram:180,unit:'份',price:14,sale:true,isDefault:true},{portion:'大份',baseGram:280,unit:'份',price:20,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:180,unit:'份',cleanMargin:62.0,cleanPrice:10,cleanSale:false,cleanDefault:true},{portion:'大份',baseGram:280,unit:'份',cleanMargin:65.0,cleanPrice:14,cleanSale:false,cleanDefault:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',updateTime:'2026-08-05',
|
||||
pricing:{basePrice:'7.78',ingredientCost:5.20,per100gCost:2.89,profitRate:170,suggestedPrice:'7.80'},
|
||||
promoVideos:[{title:'糖醋里脊宣传片',duration:'2分钟',size:'58MB',enabled:true},{title:'正宗做法揭秘',duration:'5分钟',size:'150MB',enabled:true},{title:'出品摆盘展示',duration:'30秒',size:'12MB',enabled:false}],
|
||||
crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',cookedWeight:180,
|
||||
@@ -595,17 +595,27 @@ function renderBasic(d){
|
||||
function renderPricing(d){
|
||||
var specRows = (d.specs||[]).map(function(s){
|
||||
var statusHtml = s.isDefault ? '<span class="tag tag-blue" style="font-size:11px;">默认</span>' : '';
|
||||
var saleActive = s.sale === true;
|
||||
var saleHtml = '<div class="sale-toggle'+(saleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(saleActive?'上架':'下架')+'</span></div>';
|
||||
var gramText=s.baseGram?(parseFloat(s.baseGram)+'g'):'';
|
||||
var specViewHtml='<div class="spec-cell-view"><span class="spec-cell-view__gram">'+gramText+'</span><span class="spec-cell-view__sep">/</span><span class="spec-cell-view__name">'+(s.portion||'—')+'</span></div>';
|
||||
return '<tr><td>'+specViewHtml+'</td><td>'+(s.price!==undefined?parseFloat(s.price).toFixed(2):'—')+'</td><td>'+saleHtml+'</td><td>'+statusHtml+'</td></tr>';
|
||||
}).join('');
|
||||
var cleanSpecRows = (d.cleanSpecs||[]).map(function(s){
|
||||
var statusHtml = s.cleanDefault ? '<span class="tag tag-blue" style="font-size:11px;">默认</span>' : '';
|
||||
var cleanMargin = (s.cleanMargin!==undefined && s.cleanMargin!==null) ? parseFloat(s.cleanMargin).toFixed(1)+'%' : '—';
|
||||
var cleanPrice = (s.cleanPrice!==undefined && s.cleanPrice!==null) ? parseFloat(s.cleanPrice).toFixed(2) : '—';
|
||||
var cleanSaleActive = s.cleanSale === true;
|
||||
var cleanSaleHtml = '<div class="sale-toggle'+(cleanSaleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(cleanSaleActive?'上架':'下架')+'</span></div>';
|
||||
var gramText=s.baseGram?(parseFloat(s.baseGram)+'g'):'';
|
||||
var specViewHtml='<div class="spec-cell-view"><span class="spec-cell-view__gram">'+gramText+'</span><span class="spec-cell-view__sep">/</span><span class="spec-cell-view__name">'+(s.portion||'—')+'</span></div>';
|
||||
return '<tr><td>'+specViewHtml+'</td><td>'+s.price.toFixed(2)+'</td><td>'+cleanMargin+'</td><td>'+cleanPrice+'</td><td>'+cleanSaleHtml+'</td><td>'+statusHtml+'</td></tr>';
|
||||
return '<tr><td>'+specViewHtml+'</td><td>'+cleanMargin+'</td><td>'+cleanPrice+'</td><td>'+cleanSaleHtml+'</td><td>'+statusHtml+'</td></tr>';
|
||||
}).join('');
|
||||
return renderPricingSection(d)
|
||||
+ '<div class="info-section"><div class="info-section__title">📐 餐品规格(共 '+(d.specs||[]).length+' 项)</div>'
|
||||
+ '<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>餐品售价(元/份)</th><th>净菜包毛利率</th><th>净菜包售价(元/份)</th><th>外售净菜包</th><th>状态</th></tr></thead><tbody>'+specRows+'</tbody></table></div>';
|
||||
+ '<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>餐品售价(元/份)</th><th>上架状态</th><th>是否默认</th></tr></thead><tbody>'+specRows+'</tbody></table></div>'
|
||||
+ '<div class="info-section"><div class="info-section__title">🥬 餐品净菜包规格(共 '+(d.cleanSpecs||[]).length+' 项)</div>'
|
||||
+ '<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>净菜包毛利率</th><th>净菜包售价(元/份)</th><th>上架状态</th><th>是否默认</th></tr></thead><tbody>'+cleanSpecRows+'</tbody></table></div>';
|
||||
}
|
||||
|
||||
/* 餐品定价分区(普通 info-section 样式,详情页只展示字段,成本核算仅在编辑/新增页) */
|
||||
@@ -995,7 +1005,11 @@ function persistDishSpecs(){
|
||||
yieldRate: d.yieldRate,
|
||||
specs: (d.specs||[]).map(function(s){
|
||||
return {portion:s.portion, baseGram:s.baseGram, unit:s.unit||'份',
|
||||
price:s.price, cleanPrice:s.cleanPrice, cleanSale:s.cleanSale===true, isDefault:s.isDefault===true};
|
||||
price:s.price, sale:s.sale===true, isDefault:s.isDefault===true};
|
||||
}),
|
||||
cleanSpecs: (d.cleanSpecs||[]).map(function(s){
|
||||
return {portion:s.portion, baseGram:s.baseGram, unit:s.unit||'份',
|
||||
cleanMargin:s.cleanMargin, cleanPrice:s.cleanPrice, cleanSale:s.cleanSale===true, cleanDefault:s.cleanDefault===true};
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
@@ -551,7 +551,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<script>
|
||||
// 餐品数据
|
||||
const DISHES = [
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:12,cleanMargin:55.0,cleanPrice:9,cleanSale:false,isDefault:true},{portion:'小份',baseGram:120,unit:'份',price:8,cleanMargin:50.0,cleanPrice:6,cleanSale:false},{portion:'大份',baseGram:300,unit:'份',price:18,cleanMargin:58.0,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',updateTime:'2026-08-10',
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:12,sale:true,isDefault:true},{portion:'小份',baseGram:120,unit:'份',price:8,sale:true,isDefault:false},{portion:'大份',baseGram:300,unit:'份',price:18,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:200,unit:'份',cleanMargin:55.0,cleanPrice:9,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:120,unit:'份',cleanMargin:50.0,cleanPrice:6,cleanSale:false,cleanDefault:false},{portion:'大份',baseGram:300,unit:'份',cleanMargin:58.0,cleanPrice:13,cleanSale:false,cleanDefault:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',updateTime:'2026-08-10',
|
||||
pricing:{basePrice:'6.00',ingredientCost:4.80,per100gCost:2.40,profitRate:150,suggestedPrice:'6.00'},
|
||||
promoVideos:[{title:'红烧肉宣传片',duration:'1分30秒',size:'48MB',enabled:true},{title:'制作工艺展示',duration:'2分15秒',size:'72MB',enabled:false}],
|
||||
crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',cookedWeight:200,
|
||||
@@ -569,7 +569,7 @@ const DISHES = [
|
||||
{type:'编辑',title:'修订色泽档案',time:'2026-08-10 10:25',user:'李建国',role:'营养师',dept:'营养管理部',summary:'色泽评分根据试菜反馈微调,综合评级维持良好。',changes:[{field:'卖相评分',old:'3.5',new:'4'},{field:'综合色泽评分',old:'3.8',new:'4.0'}]}
|
||||
]
|
||||
},
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{portion:'标准份',baseGram:150,unit:'份',price:6,cleanMargin:60.0,cleanPrice:4.5,cleanSale:false,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:4,cleanMargin:55.0,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-08-09',
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{portion:'标准份',baseGram:150,unit:'份',price:6,sale:true,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:4,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:150,unit:'份',cleanMargin:60.0,cleanPrice:4.5,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:100,unit:'份',cleanMargin:55.0,cleanPrice:3,cleanSale:false,cleanDefault:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-08-09',
|
||||
pricing:{basePrice:'3.00',ingredientCost:1.80,per100gCost:1.20,profitRate:150,suggestedPrice:'3.00'},
|
||||
promoVideos:[{title:'清炒时蔬宣传片',duration:'45秒',size:'18MB',enabled:true}],
|
||||
crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',cookedWeight:140,
|
||||
@@ -585,7 +585,7 @@ const DISHES = [
|
||||
{type:'价格调整',title:'更新餐品定价',time:'2026-07-12 09:45',user:'张明',role:'成本核算员',dept:'财务部',summary:'夏季时蔬成本下降,标准份售价下调。',changes:[{field:'标准份售价',old:'8 元',new:'6 元'}]}
|
||||
]
|
||||
},
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{portion:'标准份',baseGram:300,unit:'份',price:5,cleanMargin:42.0,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',updateTime:'2026-08-08',
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{portion:'标准份',baseGram:300,unit:'份',price:5,sale:true,isDefault:true}],cleanSpecs:[{portion:'标准份',baseGram:300,unit:'份',cleanMargin:42.0,cleanPrice:3.5,cleanSale:false,cleanDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',updateTime:'2026-08-08',
|
||||
pricing:{basePrice:'1.67',ingredientCost:1.20,per100gCost:0.40,profitRate:150,suggestedPrice:'1.00'},
|
||||
promoVideos:[{title:'番茄鸡蛋汤宣传片',duration:'1分钟',size:'24MB',enabled:true},{title:'家常做法教程',duration:'3分钟',size:'80MB',enabled:true}],
|
||||
crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',cookedWeight:300,
|
||||
@@ -601,7 +601,7 @@ const DISHES = [
|
||||
{type:'编辑',title:'修订味道档案',time:'2026-07-20 11:10',user:'李建国',role:'营养师',dept:'营养管理部',summary:'根据试菜反馈调整酸味评分。',changes:[{field:'酸味(1-5)',old:'2',new:'3'},{field:'味道评分',old:'3.2',new:'3.5'}]}
|
||||
]
|
||||
},
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:3,cleanMargin:38.0,cleanPrice:2,cleanSale:false,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:2,cleanMargin:35.0,cleanPrice:1.5,cleanSale:false},{portion:'大份',baseGram:300,unit:'份',price:4,cleanMargin:40.0,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-07-22',
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{portion:'标准份',baseGram:200,unit:'份',price:3,sale:true,isDefault:true},{portion:'小份',baseGram:100,unit:'份',price:2,sale:true,isDefault:false},{portion:'大份',baseGram:300,unit:'份',price:4,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:200,unit:'份',cleanMargin:38.0,cleanPrice:2,cleanSale:false,cleanDefault:true},{portion:'小份',baseGram:100,unit:'份',cleanMargin:35.0,cleanPrice:1.5,cleanSale:false,cleanDefault:false},{portion:'大份',baseGram:300,unit:'份',cleanMargin:40.0,cleanPrice:3,cleanSale:false,cleanDefault:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',updateTime:'2026-07-22',
|
||||
pricing:{basePrice:'1.50',ingredientCost:1.00,per100gCost:0.50,profitRate:150,suggestedPrice:'1.25'},
|
||||
promoVideos:[],
|
||||
crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',cookedWeight:200,
|
||||
@@ -617,7 +617,7 @@ const DISHES = [
|
||||
{type:'价格调整',title:'更新餐品定价',time:'2026-07-22 10:30',user:'张明',role:'成本核算员',dept:'财务部',summary:'粳米定价微调,标准份售价更新。',changes:[{field:'标准份售价',old:'2 元',new:'3 元'}]}
|
||||
]
|
||||
},
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{portion:'标准份',baseGram:180,unit:'份',price:14,cleanMargin:62.0,cleanPrice:10,cleanSale:false,isDefault:true},{portion:'大份',baseGram:280,unit:'份',price:20,cleanMargin:65.0,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',updateTime:'2026-08-05',
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{portion:'标准份',baseGram:180,unit:'份',price:14,sale:true,isDefault:true},{portion:'大份',baseGram:280,unit:'份',price:20,sale:false,isDefault:false}],cleanSpecs:[{portion:'标准份',baseGram:180,unit:'份',cleanMargin:62.0,cleanPrice:10,cleanSale:false,cleanDefault:true},{portion:'大份',baseGram:280,unit:'份',cleanMargin:65.0,cleanPrice:14,cleanSale:false,cleanDefault:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',updateTime:'2026-08-05',
|
||||
pricing:{basePrice:'7.78',ingredientCost:5.20,per100gCost:2.89,profitRate:170,suggestedPrice:'7.80'},
|
||||
promoVideos:[{title:'糖醋里脊宣传片',duration:'2分钟',size:'58MB',enabled:true},{title:'正宗做法揭秘',duration:'5分钟',size:'150MB',enabled:true},{title:'出品摆盘展示',duration:'30秒',size:'12MB',enabled:false}],
|
||||
crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',cookedWeight:180,
|
||||
@@ -737,17 +737,27 @@ function renderBasic(d){
|
||||
function renderPricing(d){
|
||||
var specRows = (d.specs||[]).map(function(s){
|
||||
var statusHtml = s.isDefault ? '<span class="tag tag-blue" style="font-size:11px;">默认</span>' : '';
|
||||
var saleActive = s.sale === true;
|
||||
var saleHtml = '<div class="sale-toggle'+(saleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(saleActive?'上架':'下架')+'</span></div>';
|
||||
var gramText=s.baseGram?(parseFloat(s.baseGram)+'g'):'';
|
||||
var specViewHtml='<div class="spec-cell-view"><span class="spec-cell-view__gram">'+gramText+'</span><span class="spec-cell-view__sep">/</span><span class="spec-cell-view__name">'+(s.portion||'—')+'</span></div>';
|
||||
return '<tr><td>'+specViewHtml+'</td><td>'+(s.price!==undefined?parseFloat(s.price).toFixed(2):'—')+'</td><td>'+saleHtml+'</td><td>'+statusHtml+'</td></tr>';
|
||||
}).join('');
|
||||
var cleanSpecRows = (d.cleanSpecs||[]).map(function(s){
|
||||
var statusHtml = s.cleanDefault ? '<span class="tag tag-blue" style="font-size:11px;">默认</span>' : '';
|
||||
var cleanMargin = (s.cleanMargin!==undefined && s.cleanMargin!==null) ? parseFloat(s.cleanMargin).toFixed(1)+'%' : '—';
|
||||
var cleanPrice = (s.cleanPrice!==undefined && s.cleanPrice!==null) ? parseFloat(s.cleanPrice).toFixed(2) : '—';
|
||||
var cleanSaleActive = s.cleanSale === true;
|
||||
var cleanSaleHtml = '<div class="sale-toggle'+(cleanSaleActive?' active':'')+'" onclick="toggleCleanSale(this)"><span class="sale-toggle__switch"></span><span class="sale-toggle__label">'+(cleanSaleActive?'上架':'下架')+'</span></div>';
|
||||
var gramText=s.baseGram?(parseFloat(s.baseGram)+'g'):'';
|
||||
var specViewHtml='<div class="spec-cell-view"><span class="spec-cell-view__gram">'+gramText+'</span><span class="spec-cell-view__sep">/</span><span class="spec-cell-view__name">'+(s.portion||'—')+'</span></div>';
|
||||
return '<tr><td>'+specViewHtml+'</td><td>'+s.price.toFixed(2)+'</td><td>'+cleanMargin+'</td><td>'+cleanPrice+'</td><td>'+cleanSaleHtml+'</td><td>'+statusHtml+'</td></tr>';
|
||||
return '<tr><td>'+specViewHtml+'</td><td>'+cleanMargin+'</td><td>'+cleanPrice+'</td><td>'+cleanSaleHtml+'</td><td>'+statusHtml+'</td></tr>';
|
||||
}).join('');
|
||||
return renderPricingSection(d)
|
||||
+ '<div class="info-section"><div class="info-section__title">📐 餐品规格(共 '+(d.specs||[]).length+' 项)</div>'
|
||||
+ '<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>餐品售价(元/份)</th><th>净菜包毛利率</th><th>净菜包售价(元/份)</th><th>外售净菜包</th><th>状态</th></tr></thead><tbody>'+specRows+'</tbody></table></div>';
|
||||
+ '<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>餐品售价(元/份)</th><th>上架状态</th><th>是否默认</th></tr></thead><tbody>'+specRows+'</tbody></table></div>'
|
||||
+ '<div class="info-section"><div class="info-section__title">🥬 餐品净菜包规格(共 '+(d.cleanSpecs||[]).length+' 项)</div>'
|
||||
+ '<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>净菜包毛利率</th><th>净菜包售价(元/份)</th><th>上架状态</th><th>是否默认</th></tr></thead><tbody>'+cleanSpecRows+'</tbody></table></div>';
|
||||
}
|
||||
|
||||
/* 餐品定价分区(普通 info-section 样式,详情页只展示字段,成本核算仅在编辑/新增页) */
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# 营养用餐管理端 CHANGELOG
|
||||
|
||||
## [2026-09-14] feat: 餐品定价规格拆分为餐品规格与餐品净菜包规格
|
||||
|
||||
- `din-dish-add.html` 新增/编辑餐品:
|
||||
- Step 2 餐品定价下将原单一「餐品规格」表拆分为「餐品规格」「餐品净菜包规格」两个独立维护区域
|
||||
- 餐品规格列:规格(克重/份型)·餐品售价(元/份)·上架状态·默认;净菜包规格列:规格(克重/份型)·净菜包毛利率(%)·净菜包售价(元/份)·上架状态·默认
|
||||
- 规格管理 JS 拆分 addSpecRow/confirmSpec(餐品规格) 与 addCleanSpecRow/confirmCleanSpec(净菜包规格),toggleDefault/removeSpec 改为按所在 tbody 生效
|
||||
- 数据模型 specs 拆分为 specs(新增 sale 上架状态) 与 cleanSpecs(新增 cleanDefault 默认),fillEditData 分别预填两表
|
||||
- `din-dish-all.html` 餐品总库:
|
||||
- 详情「餐品定价」Tab 拆分为「餐品规格」「餐品净菜包规格」两个表格区域,DISHES 数据拆分 specs/cleanSpecs
|
||||
- persistDishSpecs 序列化同步输出 cleanSpecs(specs 去掉净菜包字段、新增 sale)
|
||||
- `din-dish-own.html` 自有餐品库:
|
||||
- 详情「餐品定价」Tab 拆分为「餐品规格」「餐品净菜包规格」两个表格区域,DISHES 数据拆分 specs/cleanSpecs
|
||||
|
||||
## [2026-09-07] feat: 净菜类型拆分独立管理(新增净菜管理页 + 规格行级可食部字段)
|
||||
|
||||
- `din-ingredient.html` 食物成分:
|
||||
|
||||
Reference in New Issue
Block a user