feat: 营养用餐侧边栏新增「自由餐品库」菜单项

在「餐品总库」与「算法标准」之间新增自由餐品库入口,页面内容暂复制自餐品总库,后续将做差异化。同步更新全部 26 个营养用餐分组页面侧边栏及 SITE_MAP。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
fengpu
2026-08-03 14:06:19 +08:00
co-authored by Claude Opus 4.7
parent de20b8693c
commit aa3d439471
29 changed files with 769 additions and 0 deletions
@@ -151,6 +151,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -152,6 +152,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup open">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -174,6 +174,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -116,6 +116,7 @@ tr:hover td{background:#FAFAFA;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -134,6 +134,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -186,6 +186,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -177,6 +177,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -413,6 +413,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -122,6 +122,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -126,6 +126,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -197,6 +197,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item active"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -206,6 +206,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item active"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+736
View File
@@ -0,0 +1,736 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>营养管理 - 自由餐品库 | 健康CQ原型</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei',sans-serif;background:#F5F7FA;color:#333;min-width:1280px;}
:root{
--primary:#1890FF;--primary-light:#E6F7FF;--success:#52C41A;--warning:#FAAD14;--danger:#FF4D4F;
--text-primary:#333;--text-secondary:#666;--text-placeholder:#999;--border:#E8E8E8;--background:#F5F7FA;
--font-xs:11px;--font-sm:13px;--font-base:15px;--font-lg:17px;--font-xl:20px;--font-xxl:24px;
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
}
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
.top-bar__logo svg{width:28px;height:28px;}
.top-bar__nav{display:flex;align-items:center;gap:4px;flex:1;}
.top-bar__nav a{color:rgba(255,255,255,0.65);text-decoration:none;padding:6px 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);transition:all .2s;}
.top-bar__nav a:hover{color:#fff;background:rgba(255,255,255,0.08);}
.top-bar__nav a.active{color:#fff;background:var(--primary);}
.top-bar__right{display:flex;align-items:center;gap:16px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);}
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
.top-bar__right .btn-back:hover{color:#fff;}
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
.sidebar-item:hover{color:#fff;background:rgba(255,255,255,0.06);}
.sidebar-item.active{color:#fff;background:var(--primary);}
.mi{display:inline-block;width:20px;text-align:center;filter:grayscale(1);opacity:0.65;margin-right:2px;}
.sidebar-item.active .mi{opacity:0.9;}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;user-select:none;}
.sidebar-subgroup__header:hover{color:rgba(255,255,255,0.85);background:rgba(255,255,255,0.04);}
.sidebar-subgroup__header .arrow{width:10px;height:10px;transition:transform .2s;flex-shrink:0;}
.sidebar-subgroup__header .arrow path{stroke:rgba(255,255,255,0.45);}
.sidebar-subgroup.open>.sidebar-subgroup__header{color:rgba(255,255,255,0.85);}
.sidebar-subgroup.open>.sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:16px;}
.sidebar-subgroup.open>.sidebar-subgroup__body{display:block;}
.sidebar-subgroup__body .sidebar-item{padding-left:36px;font-size:12px;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.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);}
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;text-decoration:none;}
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);margin-right:4px;}
.tag-blue{background:var(--primary-light);color:var(--primary);}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-orange{background:#FFF7E6;color:var(--warning);}
.tag-red{background:#FFF1F0;color:var(--danger);}
.tag-gray{background:#F5F5F5;color:#999;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}.link:hover{color:#40a9ff;}
/* 左右分栏布局 */
.master-detail{display:flex;gap:16px;height:calc(100vh - 56px - 24px - 24px - 100px);}
.panel-left{width:380px;flex-shrink:0;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);display:flex;flex-direction:column;overflow:hidden;}
.panel-right{flex:1;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);display:flex;flex-direction:column;overflow:hidden;}
.panel-left__header{padding:16px 20px 12px;border-bottom:1px solid var(--border);}
.panel-left__title{font-size:var(--font-base);font-weight:600;margin-bottom:12px;display:flex;align-items:center;justify-content:space-between;}
.panel-left__search{display:flex;gap:8px;margin-bottom:10px;}
.panel-left__search input{flex:1;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);outline:none;}
.panel-left__search input:focus{border-color:var(--primary);}
.panel-left__filter{display:flex;gap:8px;}
.panel-left__filter select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 10px;font-size:var(--font-xs);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 8px center;appearance:none;cursor:pointer;flex:1;}
.panel-left__actions{display:flex;gap:8px;padding:10px 20px;border-bottom:1px solid var(--border);flex-wrap:wrap;}
.panel-left__actions .btn{height:30px;font-size:var(--font-xs);padding:0 10px;}
.food-list{flex:1;overflow-y:auto;padding:4px 0;}
.food-item{display:flex;align-items:center;gap:12px;padding:12px 20px;cursor:pointer;transition:background .15s;border-left:3px solid transparent;}
.food-item:hover{background:#F8FBFF;}
.food-item.active{background:var(--primary-light);border-left-color:var(--primary);}
.food-item__icon{width:36px;height:36px;border-radius:var(--radius-sm);background:#F5F7FA;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;}
.food-item__info{flex:1;min-width:0;}
.food-item__name{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.food-item__meta{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;display:flex;gap:8px;}
.panel-left__footer{padding:10px 20px;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;}
.panel-left__footer span{font-size:var(--font-xs);color:var(--text-placeholder);}
.page-btn{width:26px;height:26px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-xs);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
/* 右侧详情面板 */
.panel-right__header{padding:16px 24px 0;border-bottom:1px solid var(--border);}
.panel-right__food-title{display:flex;align-items:center;gap:12px;margin-bottom:14px;}
.panel-right__food-title h2{font-size:var(--font-lg);font-weight:600;}
.panel-right__food-title .code{font-size:var(--font-sm);color:var(--text-placeholder);}
.detail-tabs{display:flex;gap:0;flex-wrap:wrap;}
.detail-tab{padding:10px 16px;font-size:var(--font-sm);cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .2s;display:flex;align-items:center;gap:5px;}
.detail-tab:hover{color:var(--primary);}
.detail-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:500;}
.panel-right__body{flex:1;overflow-y:auto;padding:20px 24px;}
.detail-panel{display:none;}
.detail-panel.active{display:block;}
/* 详情网格 */
.info-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px;}
.info-cell{background:#FAFBFC;border-radius:var(--radius-sm);padding:14px 16px;border:1px solid #F0F0F0;}
.info-cell__label{font-size:var(--font-xs);color:var(--text-placeholder);margin-bottom:4px;}
.info-cell__value{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
.info-section{margin-bottom:20px;}
.info-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid #F0F0F0;display:flex;align-items:center;justify-content:space-between;}
.info-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
.info-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.info-table td{padding:10px 12px;border-bottom:1px solid var(--border);color:var(--text-primary);}
.info-table tr:last-child td{border-bottom:none;}
.info-table tr:hover{background:#F8FBFF;}
.spec-detail-table th{padding:6px 12px;font-size:12px;}
.spec-detail-table td{padding:6px 12px;font-size:12px;}
/* 外售净菜包胶囊开关 */
.sale-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
.sale-toggle__switch{width:32px;height:18px;background:#D9D9D9;border-radius:9px;position:relative;transition:background .2s;flex-shrink:0;}
.sale-toggle__switch::after{content:'';position:absolute;left:2px;top:2px;width:14px;height:14px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 2px rgba(0,0,0,0.15);}
.sale-toggle.active .sale-toggle__switch{background:var(--success);}
.sale-toggle.active .sale-toggle__switch::after{transform:translateX(14px);}
.sale-toggle__label{font-size:11px;color:var(--text-placeholder);}
.sale-toggle.active .sale-toggle__label{color:var(--success);font-weight:500;}
/* 图片/视频网格 */
.media-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;}
.media-card{background:#FAFBFC;border-radius:var(--radius-md);border:1px solid #F0F0F0;overflow:hidden;}
.media-card__thumb{height:120px;background:#E8E8E8;display:flex;align-items:center;justify-content:center;font-size:36px;color:#999;}
.media-card__info{padding:10px 12px;}
.media-card__title{font-size:var(--font-sm);font-weight:500;margin-bottom:4px;}
.media-card__meta{font-size:var(--font-xs);color:var(--text-placeholder);}
/* 弹窗 */
.dialog-mask{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.45);z-index:200;align-items:center;justify-content:center;}
.dialog-mask.open{display:flex;}
.dialog{background:#fff;border-radius:var(--radius-lg);width:560px;max-width:90vw;box-shadow:var(--shadow-md);}
.dialog__header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);}
.dialog__title{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);}
.dialog__close{width:32px;height:32px;border:none;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);color:var(--text-placeholder);}
.dialog__close:hover{background:#F5F5F5;color:var(--text-primary);}
.dialog__body{padding:24px;}
.dialog__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
.form-group{margin-bottom:16px;}
.form-group label{display:block;font-size:var(--font-sm);color:var(--text-primary);margin-bottom:8px;font-weight:500;}
.form-group label .required{color:var(--danger);margin-left:2px;}
.form-input,.form-select{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
.form-input:focus,.form-select:focus{border-color:var(--primary);}
.form-select{appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;padding-right:28px;}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
/* 操作列固定右侧 */
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;white-space:nowrap;}
thead th:last-child{background:#FAFAFA;}
tbody tr:hover td.td-actions{background:#F8FBFF;}
/* 分类管理表格 */
.cat-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
.cat-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
.cat-table td{padding:8px 12px;border-bottom:1px solid #F5F5F5;color:var(--text-primary);}
.cat-table tbody tr:hover{background:#F8FBFF;}
.cat-toggle{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:1px solid var(--border);border-radius:3px;cursor:pointer;margin-right:6px;font-size:12px;line-height:1;background:#fff;color:var(--text-secondary);flex-shrink:0;vertical-align:middle;}
.cat-toggle:hover{border-color:var(--primary);color:var(--primary);}
.cat-toggle.leaf{visibility:hidden;}
.cat-table td.td-actions{white-space:nowrap;}
.cat-table td.td-actions .link{margin:0 4px;}
/* 状态胶囊开关 */
.status-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
.status-toggle__switch{width:36px;height:20px;background:#D9D9D9;border-radius:10px;position:relative;transition:background .2s;flex-shrink:0;}
.status-toggle__switch::after{content:'';position:absolute;left:2px;top:2px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,0.15);}
.status-toggle.active .status-toggle__switch{background:var(--success);}
.status-toggle.active .status-toggle__switch::after{transform:translateX(16px);}
.status-toggle__label{font-size:var(--font-xs);color:var(--text-placeholder);}
.status-toggle.active .status-toggle__label{color:var(--success);}
</style>
</head>
<body>
<!-- 顶部导航栏 -->
<header class="top-bar">
<div class="top-bar__logo">
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
营养管理
</div>
<nav class="top-bar__nav">
<a href="trace-query.html">溯源总览</a>
<a href="arc-employee.html">档案</a>
<a href="farm-land.html">绿色种采</a>
<a href="sup-hygiene-check.html">卫生供应</a>
<a href="prod-qc-daily.html">健康生产</a>
<a href="din-dict-mgmt.html" class="active">营养用餐</a>
<a href="ano-dirty.html">异常事件</a>
</nav>
<div class="top-bar__right">
<a href="../../index.html" class="btn-back">← 返回首页</a>
<div class="avatar"></div>
<span>系统管理员 ▾</span>
</div>
</header>
<!-- 侧边栏 -->
<aside class="sidebar">
<div class="sidebar-menu__title">营养用餐</div>
<a href="din-canteen.html" class="sidebar-item"><span class="mi">🏢</span>场所管理</a>
<a href="din-dict-mgmt.html" class="sidebar-item"><span class="mi">📖</span>字典管理</a>
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item active"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
📐 算法标准
</div>
<div class="sidebar-subgroup__body">
<a href="din-algo-population.html" class="sidebar-item"><span class="mi">👥</span>人群基础标准</a>
<a href="din-algo-vip.html" class="sidebar-item"><span class="mi">👑</span>VIP定制标准</a>
</div>
</div>
<a href="din-recipe-mgmt.html" class="sidebar-item"><span class="mi">📋</span>食谱管理</a>
<a href="din-order-meal.html" class="sidebar-item"><span class="mi">🍱</span>营养点餐</a>
<a href="din-user-mgmt.html" class="sidebar-item"><span class="mi">👤</span>用户管理</a>
<a href="din-order-line.html" class="sidebar-item"><span class="mi">🍳</span>餐线管理</a>
<a href="din-order-pickup.html" class="sidebar-item"><span class="mi">🎫</span>取餐管理</a>
<a href="din-data-consume.html" class="sidebar-item"><span class="mi">💰</span>消费管理</a>
<a href="din-data-nutri.html" class="sidebar-item"><span class="mi">📊</span>营养管理</a>
<a href="din-data-benefit.html" class="sidebar-item"><span class="mi">📈</span>餐材效益</a>
<a href="din-terminal.html" class="sidebar-item"><span class="mi">🖥️</span>终端管理</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
🤖 模型管理
</div>
<div class="sidebar-subgroup__body">
<a href="din-model-llm.html" class="sidebar-item"><span class="mi">🧠</span>大模型管理</a>
<a href="din-model-small.html" class="sidebar-item"><span class="mi">⚙️</span>小模型管理</a>
</div>
</div>
<a href="din-innovation.html" class="sidebar-item"><span class="mi">💡</span>味养创新</a>
</aside>
<!-- 主内容 -->
<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">486</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">312</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">312道</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">18</div><div class="stat-card__label">本月新增</div></div></div>
</div>
<!-- 左右分栏 -->
<div class="master-detail">
<!-- 左侧:餐品列表 -->
<div class="panel-left">
<div class="panel-left__header">
<div class="panel-left__title">餐品列表</div>
<div class="panel-left__search">
<input type="text" placeholder="搜索餐品名称/编号...">
<button class="btn btn-primary" style="height:32px;padding:0 12px;font-size:var(--font-xs);">搜索</button>
</div>
<div class="panel-left__filter">
<select><option>全部分类</option><option>主食</option><option>荤菜</option><option>素菜</option><option>汤品</option><option>小吃</option></select>
<select><option>全部状态</option><option>启用</option><option>停用</option></select>
</div>
</div>
<div class="panel-left__actions">
<a href="din-dish-add.html" class="btn btn-primary"> 新增餐品</a>
<button class="btn btn-default">📤 批量导入</button>
<button class="btn btn-default" onclick="openDishCategoryDialog()">📂 餐品分类管理</button>
</div>
<div class="food-list" id="dishList"></div>
<div class="panel-left__footer">
<span>共 486 条</span>
<div style="display:flex;gap:4px;">
<button class="page-btn"></button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn">3</button><button class="page-btn"></button>
</div>
</div>
</div>
<!-- 右侧:餐品详情 -->
<div class="panel-right">
<div class="panel-right__header">
<div class="panel-right__food-title">
<h2 id="detailName">红烧肉</h2>
<span class="code" id="detailCode">DISH001</span>
<span class="tag tag-green" id="detailStatus">上架</span>
</div>
<div class="detail-tabs">
<div class="detail-tab active" onclick="switchDetailTab(0)">🍽️ 基本档案</div>
<div class="detail-tab" onclick="switchDetailTab(1)">🧩 餐品构成</div>
<div class="detail-tab" onclick="switchDetailTab(2)">🍳 烹饪工艺</div>
<div class="detail-tab" onclick="switchDetailTab(3)">📊 营养指标</div>
<div class="detail-tab" onclick="switchDetailTab(4)">👅 味道档案</div>
<div class="detail-tab" onclick="switchDetailTab(5)">🫦 口感档案</div>
<div class="detail-tab" onclick="switchDetailTab(6)">🎨 色泽档案</div>
</div>
</div>
<div class="panel-right__body" id="detailBody"></div>
</div>
</div>
</main>
<script>
// 餐品数据
const DISHES = [
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,cleanPrice:9,cleanSale:false,isDefault:true},{name:'小份',weight:120,price:8,cleanPrice:6,cleanSale:false},{name:'大份',weight:300,price:18,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',weightBasePrice:'6.00',weightVipPrice:'5.40',crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',
composition:[{name:'猪五花肉',netType:'红烧块',amount:160,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'生姜',netType:'姜片',amount:5,pct:'2.5%',state:'净菜',type:'辅材',allergen:'无',note:'—'},{name:'生抽',netType:'调味汁',amount:15,pct:'7.5%',state:'原料',type:'调料',allergen:'含麸质',note:'酱油'},{name:'冰糖',netType:'糖浆',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'上色提味'},{name:'料酒',netType:'料酒汁',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'去腥'}],
nutri:{calorie:385,protein:18.2,carb:12.4,fat:28.6,vitC:0.8,mineral:42,fiber:0.3,sodium:486,nutriGrade:'偏差',note:'铁2.4mg、锌1.8mg'},
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,oil:15,salt:3,temp:'100→80',output:200,steps:6},
taste:{sour:1,sweet:3,bitter:1,spicy:1,salty:4,smell:4.2,flavorScore:3.8,flavorGrade:'良好'},
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:[{name:'标准份',weight:150,price:6,cleanPrice:4.5,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:4,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'3.00',weightVipPrice:'2.70',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',
composition:[{name:'时令青菜',netType:'青菜段',amount:120,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'蒜末',netType:'蒜末',amount:5,pct:'3.3%',state:'净菜',type:'调料',allergen:'无',note:'—'},{name:'食用油',netType:'净油',amount:8,pct:'5.3%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'1.3%',state:'原料',type:'调料',allergen:'无',note:'—'}],
nutri:{calorie:68,protein:2.1,carb:8.6,fat:3.2,vitC:28.5,mineral:86,fiber:2.4,sodium:186,nutriGrade:'达标',note:'铁1.2mg、钙45mg'},
cook:{mainMethod:'炒',subMethod:'—',heat:'大火',duration:5,oil:8,salt:2,temp:'200',output:150,steps:4},
taste:{sour:1,sweet:2,bitter:1,spicy:1,salty:2,smell:3.5,flavorScore:3.2,flavorGrade:'一般'},
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:[{name:'标准份',weight:300,price:5,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',weightBasePrice:'1.67',weightVipPrice:'1.50',crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',
composition:[{name:'番茄',netType:'番茄块',amount:150,pct:'50%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'鸡蛋',netType:'鸡蛋液',amount:60,pct:'20%',state:'原料',type:'主材',allergen:'含蛋类',note:'—'},{name:'食用油',netType:'净油',amount:5,pct:'1.7%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'0.7%',state:'原料',type:'调料',allergen:'无',note:'—'}],
nutri:{calorie:45,protein:3.8,carb:4.2,fat:2.1,vitC:18.2,mineral:32,fiber:1.1,sodium:280,nutriGrade:'达标',note:'铁0.8mg、钾210mg'},
cook:{mainMethod:'煮',subMethod:'炒',heat:'中火',duration:12,oil:5,salt:2,temp:'100',output:300,steps:5},
taste:{sour:3,sweet:2,bitter:1,spicy:1,salty:2,smell:3.8,flavorScore:3.5,flavorGrade:'良好'},
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:[{name:'标准份',weight:200,price:3,cleanPrice:2,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:2,cleanPrice:1.5,cleanSale:false},{name:'大份',weight:300,price:4,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'1.50',weightVipPrice:'1.35',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',
composition:[{name:'大米',netType:'熟米饭',amount:100,pct:'100%',state:'原料',type:'主材',allergen:'无',note:'—'}],
nutri:{calorie:232,protein:4.8,carb:51.2,fat:0.6,vitC:0,mineral:12,fiber:0.4,sodium:3,nutriGrade:'达标',note:'—'},
cook:{mainMethod:'蒸',subMethod:'—',heat:'大火→中火',duration:30,oil:0,salt:0,temp:'100',output:200,steps:2},
taste:{sour:1,sweet:2,bitter:1,spicy:1,salty:1,smell:3.0,flavorScore:2.8,flavorGrade:'一般'},
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:[{name:'标准份',weight:180,price:14,cleanPrice:10,cleanSale:false,isDefault:true},{name:'大份',weight:280,price:20,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',weightBasePrice:'7.78',weightVipPrice:'7.00',crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',
composition:[{name:'猪里脊肉',netType:'里脊丝',amount:120,pct:'66.7%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'淀粉',netType:'淀粉糊',amount:20,pct:'11.1%',state:'原料',type:'辅材',allergen:'无',note:'挂糊'},{name:'白糖',netType:'糖浆',amount:25,pct:'13.9%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'},{name:'食醋',netType:'调味汁',amount:15,pct:'8.3%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'}],
nutri:{calorie:420,protein:22.4,carb:38.2,fat:18.6,vitC:1.2,mineral:28,fiber:0.6,sodium:620,nutriGrade:'未达标',note:'铁1.8mg'},
cook:{mainMethod:'炸',subMethod:'炒',heat:'大火',duration:15,oil:50,salt:1,temp:'180',output:180,steps:7},
taste:{sour:4,sweet:4,bitter:1,spicy:1,salty:3,smell:4.0,flavorScore:4.2,flavorGrade:'良好'},
color:{mainColor:'金黄色',uniformity:4,gloss:5,freshness:4,appearance:5,colorScore:4.5,colorGrade:'优秀'},
texture:{hardness:3,sticky:1,crispy:5,smooth:4,elastic:3,textureScore:4.0,textureGrade:'良好'}
}
];
let currentDish = 0;
let currentTab = 0;
function renderDishList(){
document.getElementById('dishList').innerHTML = DISHES.map((d,i)=>`
<div class="food-item${i===currentDish?' active':''}" onclick="selectDish(${i})">
<div class="food-item__icon">${d.icon}</div>
<div class="food-item__info">
<div class="food-item__name">${d.name}</div>
<div class="food-item__meta"><span>${d.id}</span><span>${d.category}</span><span class="tag tag-${d.status==='启用'?'green':'gray'}" style="margin:0;padding:1px 6px;">${d.status}</span></div>
</div>
</div>`).join('');
}
function selectDish(idx){ currentDish=idx; renderDishList(); renderDetail(); }
function levelTag(level){
const map = {'推荐':'tag-green','适量':'tag-orange','慎食':'tag-red'};
return '<span class="tag '+(map[level]||'tag-gray')+'">'+level+'</span>';
}
function switchDetailTab(idx){ currentTab=idx; document.querySelectorAll('.detail-tab').forEach((t,i)=>t.classList.toggle('active',i===idx)); renderDetail(); }
function renderDetail(){
const d = DISHES[currentDish];
document.getElementById('detailName').textContent = d.name;
document.getElementById('detailCode').textContent = d.id;
const s = document.getElementById('detailStatus');
s.textContent = d.status; s.className='tag tag-'+(d.status==='启用'?'green':'gray');
const body = document.getElementById('detailBody');
const renderers = [renderBasic,renderComposition,renderCook,renderNutri,renderTaste,renderTexture,renderColor];
body.innerHTML = renderers[currentTab](d);
}
function renderBasic(d){
var specRows = (d.specs||[]).map(function(s){
var statusHtml = s.isDefault ? '<span class="tag tag-blue" style="font-size:11px;">默认</span>' : '';
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>';
return '<tr><td>'+s.name+'</td><td>'+s.weight+'</td><td>'+s.price.toFixed(2)+'</td><td>'+cleanPrice+'</td><td>'+cleanSaleHtml+'</td><td>'+statusHtml+'</td></tr>';
}).join('');
return `<div class="info-section"><div class="info-section__title">基本信息 <span><a class="link" href="din-dish-add.html?mode=edit&tab=0">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">餐品编号</div><div class="info-cell__value">${d.id}</div></div>
<div class="info-cell"><div class="info-cell__label">餐品分类</div><div class="info-cell__value">${d.category}</div></div>
<div class="info-cell"><div class="info-cell__label">适用餐次</div><div class="info-cell__value">${d.meal}</div></div>
<div class="info-cell"><div class="info-cell__label">过敏原</div><div class="info-cell__value">${d.allergen}</div></div>
<div class="info-cell"><div class="info-cell__label">餐品标签</div><div class="info-cell__value">${d.tags.map(t=>'<span class="tag tag-blue">'+t+'</span>').join('')}</div></div>
<div class="info-cell"><div class="info-cell__label">称重基础价(元/100g)</div><div class="info-cell__value">${d.weightBasePrice||'—'}</div></div>
<div class="info-cell"><div class="info-cell__label">称重会员价(元/100g)</div><div class="info-cell__value">${d.weightVipPrice||'—'}</div></div>
<div class="info-cell"><div class="info-cell__label">来源</div><div class="info-cell__value"><span class="tag ${d.source==='系统内置'?'tag-blue':d.source==='味养创新'?'tag-orange':'tag-gray'}">${d.source||'—'}</span></div></div>
<div class="info-cell"><div class="info-cell__label">启用状态</div><div class="info-cell__value"><span class="tag tag-${d.status==='启用'?'green':'gray'}">${d.status}</span></div></div>
</div></div>
<div class="info-section"><div class="info-section__title">👥 适配人群</div>
<div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;">${(d.crowd||[]).map(function(c){return '<div style="display:flex;align-items:center;gap:4px;"><span style="font-size:var(--font-sm);">'+c.name+'</span>'+levelTag(c.level)+'</div>';}).join('')}</div>
${d.crowdNote?'<div style="font-size:var(--font-sm);color:var(--text-secondary);background:#FFFBE6;padding:8px 12px;border-radius:var(--radius-sm);border:1px solid #FFE58F;">⚠️ '+d.crowdNote+'</div>':''}</div>
<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>单位重量(g)</th><th>餐品售价(元/份)</th><th>净菜包售价(元/份)</th><th>外售净菜包</th><th>状态</th></tr></thead><tbody>${specRows}</tbody></table></div>
<div class="info-section"><div class="info-section__title">📷 菜品展示</div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">图片</div></div>
<div class="media-card"><div class="media-card__thumb">图片</div></div>
<div class="media-card"><div class="media-card__thumb">图片</div></div>
</div></div>
<div class="info-section"><div class="info-section__title">🎬 制作流程</div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">🎬</div></div>
</div></div>`;
}
function renderComposition(d){
const rows = (d.composition||[]).map(c=>`<tr><td>${c.name}</td><td>${c.netType}</td><td>${c.amount}</td><td>${c.pct}</td><td><span class="tag tag-${c.type==='主材'?'blue':c.type==='调料'?'orange':'gray'}">${c.type||'辅材'}</span></td><td>${c.allergen}</td><td>${c.note}</td></tr>`).join('');
return `<div class="info-section"><div class="info-section__title">食材构成(共 ${(d.composition||[]).length} 项) <span><a class="link" href="din-dish-add.html?mode=edit&tab=1">编辑</a></span></div>
<table class="info-table"><thead><tr><th>食材名称</th><th>净菜类型</th><th>用量(g/份)</th><th>占比(%)</th><th>食材类型</th><th>过敏原</th><th>备注</th></tr></thead><tbody>${rows}</tbody></table></div>`;
}
function renderCook(d){
const c = d.cook;
return `<div class="info-section"><div class="info-section__title">烹饪工艺 <span><a class="link" href="din-dish-add.html?mode=edit&tab=2">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">主烹饪法</div><div class="info-cell__value">${c.mainMethod}</div></div>
<div class="info-cell"><div class="info-cell__label">辅烹饪法</div><div class="info-cell__value">${c.subMethod}</div></div>
<div class="info-cell"><div class="info-cell__label">火候</div><div class="info-cell__value">${c.heat}</div></div>
<div class="info-cell"><div class="info-cell__label">烹饪时长</div><div class="info-cell__value">${c.duration}min</div></div>
<div class="info-cell"><div class="info-cell__label">油用量</div><div class="info-cell__value">${c.oil}g</div></div>
<div class="info-cell"><div class="info-cell__label">盐用量</div><div class="info-cell__value">${c.salt}g</div></div>
<div class="info-cell"><div class="info-cell__label">烹饪温度</div><div class="info-cell__value">${c.temp}℃</div></div>
<div class="info-cell"><div class="info-cell__label">出品量</div><div class="info-cell__value">${c.output}g</div></div>
<div class="info-cell"><div class="info-cell__label">工艺步骤数</div><div class="info-cell__value">${c.steps}步</div></div>
</div></div>`;
}
function renderNutri(d){
const n = d.nutri;
return `<div class="info-section"><div class="info-section__title">营养指标 <span><a class="link" href="din-dish-add.html?mode=edit&tab=3">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">热量 (kcal/100g)</div><div class="info-cell__value">${n.calorie}</div></div>
<div class="info-cell"><div class="info-cell__label">蛋白质 (g)</div><div class="info-cell__value">${n.protein}</div></div>
<div class="info-cell"><div class="info-cell__label">碳水化合物 (g)</div><div class="info-cell__value">${n.carb}</div></div>
<div class="info-cell"><div class="info-cell__label">脂肪 (g)</div><div class="info-cell__value">${n.fat}</div></div>
<div class="info-cell"><div class="info-cell__label">维生素C (mg)</div><div class="info-cell__value">${n.vitC}</div></div>
<div class="info-cell"><div class="info-cell__label">矿物质 (mg)</div><div class="info-cell__value">${n.mineral}</div></div>
<div class="info-cell"><div class="info-cell__label">膳食纤维 (g)</div><div class="info-cell__value">${n.fiber}</div></div>
<div class="info-cell"><div class="info-cell__label">钠 (mg)</div><div class="info-cell__value">${n.sodium}</div></div>
<div class="info-cell"><div class="info-cell__label">营养等级</div><div class="info-cell__value">${n.nutriGrade==='达标'?'<span class="tag tag-green">达标</span>':n.nutriGrade==='偏差'?'<span class="tag tag-orange">偏差</span>':'<span class="tag tag-red">未达标</span>'}</div></div>
</div></div>`;
}
function renderTaste(d){
const t = d.taste;
const gradeTag = t.flavorGrade==='优秀'?'green':t.flavorGrade==='良好'?'blue':t.flavorGrade==='一般'?'orange':'red';
return `<div class="info-section"><div class="info-section__title">味道档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=4">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">酸度 (1-5)</div><div class="info-cell__value">${t.sour}</div></div>
<div class="info-cell"><div class="info-cell__label">甜度 (1-5)</div><div class="info-cell__value">${t.sweet}</div></div>
<div class="info-cell"><div class="info-cell__label">苦度 (1-5)</div><div class="info-cell__value">${t.bitter}</div></div>
<div class="info-cell"><div class="info-cell__label">辣度 (1-5)</div><div class="info-cell__value">${t.spicy}</div></div>
<div class="info-cell"><div class="info-cell__label">咸度 (1-5)</div><div class="info-cell__value">${t.salty}</div></div>
<div class="info-cell"><div class="info-cell__label">气味评分 (1-5)</div><div class="info-cell__value">${t.smell}</div></div>
<div class="info-cell"><div class="info-cell__label">综合风味评分 (1-5)</div><div class="info-cell__value">${t.flavorScore}</div></div>
<div class="info-cell"><div class="info-cell__label">综合评级</div><div class="info-cell__value"><span class="tag tag-${gradeTag}">${t.flavorGrade}</span></div></div>
</div></div>`;
}
function renderColor(d){
const c = d.color;
const gradeTag = c.colorGrade==='优秀'?'green':c.colorGrade==='良好'?'blue':c.colorGrade==='一般'?'orange':'red';
return `<div class="info-section"><div class="info-section__title">色泽档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=5">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">主色调</div><div class="info-cell__value">${c.mainColor}</div></div>
<div class="info-cell"><div class="info-cell__label">色泽均匀度 (1-5)</div><div class="info-cell__value">${c.uniformity}</div></div>
<div class="info-cell"><div class="info-cell__label">光泽度 (1-5)</div><div class="info-cell__value">${c.gloss}</div></div>
<div class="info-cell"><div class="info-cell__label">新鲜度 (1-5)</div><div class="info-cell__value">${c.freshness}</div></div>
<div class="info-cell"><div class="info-cell__label">卖相评分 (1-5)</div><div class="info-cell__value">${c.appearance}</div></div>
<div class="info-cell"><div class="info-cell__label">综合色泽评分 (1-5)</div><div class="info-cell__value">${c.colorScore}</div></div>
<div class="info-cell"><div class="info-cell__label">综合评级</div><div class="info-cell__value"><span class="tag tag-${gradeTag}">${c.colorGrade}</span></div></div>
</div></div>`;
}
function renderTexture(d){
const t = d.texture;
const gradeTag = t.textureGrade==='优秀'?'green':t.textureGrade==='良好'?'blue':t.textureGrade==='一般'?'orange':'red';
return `<div class="info-section"><div class="info-section__title">口感档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=6">编辑</a></span></div>
<div class="info-grid">
<div class="info-cell"><div class="info-cell__label">软硬度 (1-5)</div><div class="info-cell__value">${t.hardness}</div></div>
<div class="info-cell"><div class="info-cell__label">黏糯度 (1-5)</div><div class="info-cell__value">${t.sticky}</div></div>
<div class="info-cell"><div class="info-cell__label">酥脆度 (1-5)</div><div class="info-cell__value">${t.crispy}</div></div>
<div class="info-cell"><div class="info-cell__label">嫩滑度 (1-5)</div><div class="info-cell__value">${t.smooth}</div></div>
<div class="info-cell"><div class="info-cell__label">弹性 (1-5)</div><div class="info-cell__value">${t.elastic}</div></div>
<div class="info-cell"><div class="info-cell__label">综合口感评分 (1-5)</div><div class="info-cell__value">${t.textureScore}</div></div>
<div class="info-cell"><div class="info-cell__label">综合评级</div><div class="info-cell__value"><span class="tag tag-${gradeTag}">${t.textureGrade}</span></div></div>
</div></div>`;
}
function renderImages(d){
return `<div class="info-section"><div class="info-section__title">图片库 <span><button class="btn btn-primary" style="height:28px;font-size:var(--font-xs);padding:0 10px;">📤 上传图片</button></span></div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">📷</div><div class="media-card__info"><div class="media-card__meta">800×600 · 128KB · <span class="link">预览</span></div></div></div>
<div class="media-card"><div class="media-card__thumb">📷</div><div class="media-card__info"><div class="media-card__meta">1200×900 · 256KB · <span class="link">预览</span></div></div></div>
<div class="media-card"><div class="media-card__thumb">📷</div><div class="media-card__info"><div class="media-card__meta">800×600 · 96KB · <span class="link">预览</span></div></div></div>
</div></div>`;
}
function renderVideos(d){
return `<div class="info-section"><div class="info-section__title">视频库 <span><button class="btn btn-primary" style="height:28px;font-size:var(--font-xs);padding:0 10px;">📤 上传视频</button></span></div>
<div class="media-grid">
<div class="media-card"><div class="media-card__thumb">🎬</div><div class="media-card__info"><div class="media-card__title">标准制作流程</div><div class="media-card__meta">8分32秒 · 256MB · <span class="link">播放</span></div></div></div>
<div class="media-card"><div class="media-card__thumb">🎬</div><div class="media-card__info"><div class="media-card__title">食材溯源故事</div><div class="media-card__meta">2分15秒 · 86MB · <span class="link">播放</span></div></div></div>
</div></div>`;
}
// 侧边栏折叠
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 切换外售净菜包上架/下架状态
function toggleCleanSale(el){
var active = el.classList.toggle('active');
var label = el.querySelector('.sale-toggle__label');
if(label) label.textContent = active ? '上架' : '下架';
}
(function(){var a=document.querySelector('.sidebar-item.active');if(!a)return;var s=a.closest('.sidebar-subgroup');if(s)s.classList.add('open');})();
// 初始化
renderDishList();
renderDetail();
/* ========== 餐品分类管理 ========== */
var nextDishCatId = 100;
var dishCategories = [
{id:1, name:'主食', code:'01', level:1, sort:1, status:'启用', parentId:null},
{id:2, name:'荤菜', code:'02', level:1, sort:2, status:'启用', parentId:null},
{id:3, name:'素菜', code:'03', level:1, sort:3, status:'启用', parentId:null},
{id:4, name:'汤品', code:'04', level:1, sort:4, status:'启用', parentId:null},
{id:5, name:'小吃', code:'05', level:1, sort:5, status:'启用', parentId:null},
{id:6, name:'饮品', code:'06', level:1, sort:6, status:'启用', parentId:null},
{id:7, name:'甜品', code:'07', level:1, sort:7, status:'启用', parentId:null},
{id:8, name:'套餐', code:'08', level:1, sort:8, status:'禁用', parentId:null}
];
var expandedDishCats = {};
var editingDishCatId = null;
var editingDishParentId = null;
var MAX_DISH_LEVEL = 2;
function openDishCategoryDialog(){
document.getElementById('dishCategoryDialog').classList.add('open');
renderDishCatTable();
}
function closeDishCategoryDialog(){
document.getElementById('dishCategoryDialog').classList.remove('open');
}
function toggleDishCatStatus(id){
var cat = dishCategories.find(function(c){return c.id===id;});
if(!cat) return;
cat.status = cat.status==='启用'?'禁用':'启用';
renderDishCatTable();
}
function toggleDishCatExpand(id){
expandedDishCats[id] = !expandedDishCats[id];
renderDishCatTable();
}
function getDishCatChildren(parentId){
return dishCategories.filter(function(c){return c.parentId===parentId;}).sort(function(a,b){return a.sort-b.sort;});
}
function renderDishCatTable(){
var tops = dishCategories.filter(function(c){return c.parentId===null;}).sort(function(a,b){return a.sort-b.sort;});
var html = '';
tops.forEach(function(cat){
html += renderDishCatRow(cat);
if(expandedDishCats[cat.id]){
getDishCatChildren(cat.id).forEach(function(child){
html += renderDishCatRow(child, true);
});
}
});
document.getElementById('dishCatTableBody').innerHTML = html || '<tr><td colspan="6" style="text-align:center;color:var(--text-placeholder);padding:40px;">暂无分类数据</td></tr>';
}
function renderDishCatRow(cat, isChild){
var hasChildren = getDishCatChildren(cat.id).length > 0;
var isExpanded = expandedDishCats[cat.id];
var toggleHtml = hasChildren
? '<span class="cat-toggle" onclick="event.stopPropagation();toggleDishCatExpand('+cat.id+')">'+(isExpanded?'':'+')+'</span>'
: '<span class="cat-toggle leaf">+</span>';
var rowClass = isChild ? 'cat-row-l2' : '';
var activeClass = cat.status==='启用'?' active':'';
var statusLabel = cat.status==='启用'?'启用':'禁用';
var statusHtml = '<div class="status-toggle'+activeClass+'" onclick="event.stopPropagation();toggleDishCatStatus('+cat.id+')"><span class="status-toggle__switch"></span><span class="status-toggle__label">'+statusLabel+'</span></div>';
var canAddChild = cat.level < MAX_DISH_LEVEL;
var addChildBtn = canAddChild ? '<span class="link" onclick="openAddDishCat('+cat.id+')">新增下级</span>' : '';
return '<tr class="'+rowClass+'"><td class="cat-name">'+toggleHtml+cat.name+'</td><td>'+cat.code+'</td><td>'+cat.level+'</td><td>'+cat.sort+'</td><td>'+statusHtml+'</td><td class="td-actions"><span class="link" onclick="openEditDishCat('+cat.id+')">编辑</span><span class="link" style="color:var(--danger);" onclick="deleteDishCat('+cat.id+')">删除</span>'+addChildBtn+'</td></tr>';
}
function openAddDishCat(parentId){
editingDishCatId = null;
editingDishParentId = parentId || null;
document.getElementById('dishCatFormDialogTitle').textContent = parentId ? '新增下级分类' : '新增分类';
document.getElementById('dishCatFormName').value = '';
document.getElementById('dishCatFormCode').value = '';
document.getElementById('dishCatFormSort').value = '1';
document.getElementById('dishCatFormStatus').value = '启用';
document.getElementById('dishCatFormDialog').classList.add('open');
}
function openEditDishCat(id){
var cat = dishCategories.find(function(c){return c.id===id;});
if(!cat) return;
editingDishCatId = id;
editingDishParentId = cat.parentId;
document.getElementById('dishCatFormDialogTitle').textContent = '编辑分类';
document.getElementById('dishCatFormName').value = cat.name;
document.getElementById('dishCatFormCode').value = cat.code;
document.getElementById('dishCatFormSort').value = cat.sort;
document.getElementById('dishCatFormStatus').value = cat.status;
document.getElementById('dishCatFormDialog').classList.add('open');
}
function closeDishCatFormDialog(){
document.getElementById('dishCatFormDialog').classList.remove('open');
editingDishCatId = null;
editingDishParentId = null;
}
function saveDishCatForm(){
var name = document.getElementById('dishCatFormName').value.trim();
var code = document.getElementById('dishCatFormCode').value.trim();
var sort = parseInt(document.getElementById('dishCatFormSort').value) || 1;
var status = document.getElementById('dishCatFormStatus').value;
if(!name||!code){alert('请填写分类名称和分类编码');return;}
if(editingDishCatId){
var cat = dishCategories.find(function(c){return c.id===editingDishCatId;});
if(cat){cat.name=name;cat.code=code;cat.sort=sort;cat.status=status;}
} else {
var level = editingDishParentId ? 2 : 1;
dishCategories.push({id:nextDishCatId++,name:name,code:code,level:level,sort:sort,status:status,parentId:editingDishParentId});
if(editingDishParentId) expandedDishCats[editingDishParentId] = true;
}
closeDishCatFormDialog();
renderDishCatTable();
}
function deleteDishCat(id){
var cat = dishCategories.find(function(c){return c.id===id;});
if(!cat) return;
if(getDishCatChildren(id).length > 0){alert('该分类下存在子级分类,请先删除子级分类后再删除。');return;}
if(!confirm('确定要删除分类「'+cat.name+'」吗?删除后不可恢复。')) return;
dishCategories = dishCategories.filter(function(c){return c.id!==id;});
renderDishCatTable();
}
</script>
<!-- 新增食材弹窗 -->
<div class="dialog-mask" id="ingredientDialog">
<div class="dialog">
<div class="dialog__header">
<div class="dialog__title">新增食材</div>
<button class="dialog__close" onclick="closeIngredientDialog()">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</button>
</div>
<div class="dialog__body">
<div class="form-row">
<div class="form-group"><label>食材名称<span class="required">*</span></label><input class="form-input" type="text" placeholder="请输入食材名称"></div>
<div class="form-group"><label>净菜类型<span class="required">*</span></label><select class="form-select"><option>请选择</option><option>土豆丝</option><option>土豆块</option><option>土豆泥</option><option>番茄块</option><option>蒜末</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>用量(g/份)<span class="required">*</span></label><input class="form-input" type="number" placeholder="请输入用量"></div>
<div class="form-group"><label>加工状态</label><select class="form-select"><option>请选择</option><option>原料</option><option>净菜</option><option>半成品</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>食材类型</label><select class="form-select"><option>辅材</option><option>主材</option><option>调料</option></select></div>
<div class="form-group"><label>过敏原</label><input class="form-input" type="text" placeholder="无则填无"></div>
</div>
<div class="form-group"><label>备注</label><input class="form-input" type="text" placeholder="请输入备注信息"></div>
</div>
<div class="dialog__footer">
<button class="btn btn-default" onclick="closeIngredientDialog()">取消</button>
<button class="btn btn-primary">确定</button>
</div>
</div>
</div>
<script>
function openIngredientDialog(){document.getElementById('ingredientDialog').classList.add('open');}
function closeIngredientDialog(){document.getElementById('ingredientDialog').classList.remove('open');}
</script>
<!-- 餐品分类管理弹窗 -->
<div class="dialog-mask" id="dishCategoryDialog">
<div class="dialog" style="width:820px;">
<div class="dialog__header">
<div class="dialog__title">餐品分类管理</div>
<button class="dialog__close" onclick="closeDishCategoryDialog()"></button>
</div>
<div class="dialog__body">
<!-- 工具栏 -->
<div style="margin-bottom:12px;">
<button class="btn btn-primary" onclick="openAddDishCat()" style="height:30px;font-size:var(--font-xs);"> 新增一级分类</button>
</div>
<!-- 分类表格 -->
<div style="overflow-x:auto;">
<table class="cat-table">
<thead><tr><th style="width:180px;">分类名称</th><th style="width:100px;">分类编码</th><th style="width:60px;">层级</th><th style="width:60px;">排序</th><th style="width:80px;">状态</th><th style="width:180px;">操作</th></tr></thead>
<tbody id="dishCatTableBody"></tbody>
</table>
</div>
</div>
<div class="dialog__footer">
<button class="btn btn-default" onclick="closeDishCategoryDialog()">关闭</button>
</div>
</div>
</div>
<!-- 餐品分类表单子弹窗 -->
<div class="dialog-mask" id="dishCatFormDialog">
<div class="dialog" style="width:480px;">
<div class="dialog__header">
<div class="dialog__title" id="dishCatFormDialogTitle">新增分类</div>
<button class="dialog__close" onclick="closeDishCatFormDialog()"></button>
</div>
<div class="dialog__body">
<div class="form-group"><label>分类名称<span class="required">*</span></label><input class="form-input" id="dishCatFormName" placeholder="请输入分类名称"></div>
<div class="form-row">
<div class="form-group"><label>分类编码<span class="required">*</span></label><input class="form-input" id="dishCatFormCode" placeholder="请输入分类编码"></div>
<div class="form-group"><label>排序</label><input class="form-input" id="dishCatFormSort" type="number" value="1"></div>
</div>
<div class="form-group"><label>状态</label><select class="form-select" id="dishCatFormStatus"><option>启用</option><option>禁用</option></select></div>
</div>
<div class="dialog__footer">
<button class="btn btn-default" onclick="closeDishCatFormDialog()">取消</button>
<button class="btn btn-primary" onclick="saveDishCatForm()">保存</button>
</div>
</div>
</div>
</body>
</html>
@@ -167,6 +167,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item active"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -210,6 +210,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item active"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -168,6 +168,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup"><div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)"><svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>📐 算法标准</div><div class="sidebar-subgroup__body"><a href="din-algo-population.html" class="sidebar-item"><span class="mi">👥</span>人群基础标准</a><a href="din-algo-vip.html" class="sidebar-item"><span class="mi">💎</span>VIP定制标准</a></div></div>
<a href="din-recipe-mgmt.html" class="sidebar-item"><span class="mi">📋</span>食谱管理</a>
<a href="din-order-meal.html" class="sidebar-item"><span class="mi">🛒</span>营养点餐</a>
+1
View File
@@ -142,6 +142,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -161,6 +161,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -187,6 +187,7 @@ tbody tr:hover td:last-child{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -196,6 +196,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -219,6 +219,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -252,6 +252,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍽️</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -177,6 +177,7 @@ tbody tr:hover{background:#F8FBFF;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+1
View File
@@ -334,6 +334,7 @@ tbody td.td-actions{overflow:visible;}
<a href="prod-qc-nutrition.html" class="sidebar-item"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
+6
View File
@@ -1,5 +1,11 @@
# 营养用餐管理端 CHANGELOG
## [2026-08-03]
### 后台营养管理
- **`din-dish-free.html`**(自由餐品库 · 新增页面):营养用餐侧边栏新增「自由餐品库」菜单项,位于「餐品总库」与「算法标准」之间。页面内容暂复制自 `din-dish-all.html`(餐品总库),后续将做差异化。同步更新全部 26 个营养用餐分组页面的侧边栏导航及 `index.html` SITE_MAP。
## [2026-07-24]
### 后台营养管理
@@ -117,6 +117,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -199,6 +199,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>
@@ -194,6 +194,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<a href="prod-qc-nutrition.html" class="sidebar-item active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
<a href="din-dish-all.html" class="sidebar-item"><span class="mi">🍲</span>餐品总库</a>
<a href="din-dish-free.html" class="sidebar-item"><span class="mi">📦</span>自由餐品库</a>
<div class="sidebar-subgroup">
<div class="sidebar-subgroup__header" onclick="toggleSubgroup(this)">
<svg class="arrow" viewBox="0 0 10 10" fill="none"><path d="M3 1l4 4-4 4" stroke-width="1.5"/></svg>