feat: 营养用餐模块批量增强——新增食堂管理、交互补全、数据模型重构

- 新增食堂管理模块:食堂列表页(din-canteen)、新增食堂页(din-canteen-add)、餐线管理页(din-canteen-line),全部28个现有页面侧边栏统一加入食堂管理菜单项
- 营养检验:列表页重构为筛选+表格+右侧抽屉详情模式,新增检验页菜品选择改为四级级联(食堂→餐线→日期→菜品),新增检验数据同步页(prod-qc-nutrition-sync)
- 餐品总库:数据模型从描述式重构为评分制(味道/口感/色泽),新增餐品页改为步骤条导航
- 食物成分:列表页增加级联分类选择器和来源标签,新增din-ingredient-add替代旧din-ingredient-detail
- 算法标准:人群基础标准和VIP定制标准补全完整CRUD弹窗交互,删除批量安排复评功能
- 字典管理:表结构规范化,列从9列精简为6-7列,表单字段简化
- 绿色种采:地块管理大幅增强(二级表头+弹窗体系),供应商/采购/监测/收成页补全详情和编辑弹窗
- 导航页SITE_MAP同步新增3个食堂管理页面条目

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 13:56:44 +08:00
co-authored by Claude Opus 4.6
parent 141028cb49
commit 5ccc432536
34 changed files with 5421 additions and 1328 deletions
+454 -51
View File
@@ -47,10 +47,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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);}
.page-tabs{display:flex;gap:0;background:#fff;border-radius:var(--radius-md) var(--radius-md) 0 0;padding:0 24px;box-shadow:var(--shadow-sm);border-bottom:2px solid var(--border);}
.page-tab{padding:14px 20px;font-size:var(--font-sm);cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-2px;transition:all .2s;display:flex;align-items:center;gap:6px;}
.page-tab:hover{color:var(--primary);}
@@ -66,6 +62,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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;}
.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);}
.btn-danger-text{background:none;border:none;color:var(--danger);cursor:pointer;font-size:var(--font-sm);padding:0;}.btn-danger-text:hover{color:#ff7875;}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.table-card__title{font-size:var(--font-base);font-weight:600;}
@@ -83,16 +80,44 @@ tbody tr:hover{background:#F8FBFF;}
.tag-orange{background:#FFF7E6;color:var(--warning);}
.tag-red{background:#FFF1F0;color:var(--danger);}
.tag-gray{background:#F5F5F5;color:#999;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);}.link:hover{color:#40a9ff;}
.pagination{display:flex;align-items:center;justify-content:flex-end;padding:16px 24px;gap:8px;border-top:1px solid var(--border);}
.pagination span{font-size:var(--font-sm);color:var(--text-secondary);}
.page-btn{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;transition:all .2s;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}.link:hover{color:#40a9ff;}
.pagination{display:flex;align-items:center;justify-content:space-between;padding:14px 24px;border-top:1px solid var(--border);}
.pagination__info{font-size:var(--font-xs);color:var(--text-placeholder);}
.pagination__btns{display:flex;gap:4px;align-items:center;}
.page-btn{min-width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;padding:0 8px;transition:all .2s;}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn.disabled{cursor:not-allowed;color:var(--text-placeholder);background:#F5F5F5;}
.page-btn.disabled:hover{background:#F5F5F5;color:var(--text-placeholder);border-color:var(--border);}
.action-bar{display:flex;align-items:center;gap:8px;margin-bottom: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;}
.td-actions .link{margin-right:10px;}
.td-actions .link:last-child,.td-actions .btn-danger-text:last-child{margin-right:0;}
.td-actions .btn-danger-text{margin-right:0;}
/* 弹窗 */
.modal-mask{position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;display:none;align-items:center;justify-content:center;}
.modal-mask.open{display:flex;}
.modal{background:#fff;border-radius:var(--radius-lg);width:640px;max-height:85vh;display:flex;flex-direction:column;box-shadow:var(--shadow-md);}
.modal--sm{width:480px;}
.modal--lg{width:800px;}
.modal__header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);}
.modal__title{font-size:var(--font-lg);font-weight:600;}
.modal__close{width:28px;height:28px;border:none;background:none;font-size:18px;cursor:pointer;color:var(--text-placeholder);display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);transition:all .15s;}
.modal__close:hover{background:#F5F5F5;color:var(--text-primary);}
.modal__body{padding:20px 24px;overflow-y:auto;flex:1;}
.modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
.form-row{display:flex;gap:16px;margin-bottom:16px;}
.form-group{flex:1;display:flex;flex-direction:column;gap:6px;}
.form-group label{font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;}
.form-group label .required{color:var(--danger);margin-left:2px;}
.form-group input,.form-group select,.form-group textarea{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;width:100%;}
.form-group input:focus,.form-group select:focus,.form-group textarea:focus{border-color:var(--primary);}
.form-group 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-group textarea{height:80px;padding:10px 12px;resize:vertical;}
.form-group .hint{font-size:var(--font-xs);color:var(--text-placeholder);}
</style>
</head>
<body>
@@ -121,6 +146,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
@@ -178,104 +204,481 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- Tab1: 算法标准 -->
<div class="tab-panel active">
<div class="filter-bar">
<div class="filter-item"><label>人群类型</label><select class="filter-select"><option>全部</option><option>成人男性</option><option>成人女性</option><option>老年人</option><option>儿童</option></select></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
<div class="filter-item"><label>人群类型</label><select class="filter-select" id="filterAlgoType"><option value="">全部</option><option>成人男性</option><option>成人女性</option><option>老年人</option><option>儿童</option></select></div>
<button class="btn btn-primary" onclick="filterAlgo()">查询</button>
<button class="btn btn-default" onclick="resetFilter('filterAlgoType')">重置</button>
</div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">算法标准</div></div>
<div class="table-card__header"><div class="table-card__title">算法标准</div><div class="table-card__actions"><span class="pagination__info" id="algoCount">共 4 条</span></div></div>
<div class="table-card__content"><table><thead><tr><th>人群类型</th><th>热量范围(kcal)</th><th>蛋白质比例(%)</th><th>脂肪比例(%)</th><th>碳水比例(%)</th><th>钙(mg)</th><th>铁(mg)</th><th>维生素C(mg)</th><th>膳食纤维(g)</th><th>每日饮水量(mL)</th><th>操作</th></tr></thead>
<tbody>
<tr><td>成人男性</td><td>2,200 - 2,600</td><td>15</td><td>30</td><td>55</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>成人女性</td><td>1,800 - 2,200</td><td>15</td><td>30</td><td>55</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>老年人</td><td>1,600 - 2,000</td><td>18</td><td>25</td><td>57</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>儿童</td><td>1,400 - 1,800</td><td>14</td><td>32</td><td>54</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>成人男性</td><td>2,200 - 2,600</td><td>15</td><td>30</td><td>55</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link" onclick="openAlgoEdit('成人男性','2200','2600','15','30','55','800','12','100','25','1500')">编辑</span> <span class="link" onclick="openAlgoDetail('成人男性')">详情</span></td></tr>
<tr><td>成人女性</td><td>1,800 - 2,200</td><td>15</td><td>30</td><td>55</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link" onclick="openAlgoEdit('成人女性','1800','2200','15','30','55','800','12','100','25','1500')">编辑</span> <span class="link" onclick="openAlgoDetail('成人女性')">详情</span></td></tr>
<tr><td>老年人</td><td>1,600 - 2,000</td><td>18</td><td>25</td><td>57</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link" onclick="openAlgoEdit('老年人','1600','2000','18','25','57','800','12','100','25','1500')">编辑</span> <span class="link" onclick="openAlgoDetail('老年人')">详情</span></td></tr>
<tr><td>儿童</td><td>1,400 - 1,800</td><td>14</td><td>32</td><td>54</td><td>800</td><td>12</td><td>100</td><td>25</td><td>1,500</td><td class="td-actions"><span class="link" onclick="openAlgoEdit('儿童','1400','1800','14','32','54','800','12','100','25','1500')">编辑</span> <span class="link" onclick="openAlgoDetail('儿童')">详情</span></td></tr>
</tbody></table></div>
<div class="pagination"><div class="pagination__info">第 1-4 条 / 共 4 条</div><div class="pagination__btns"><button class="page-btn disabled">上一页</button><button class="page-btn active">1</button><button class="page-btn disabled">下一页</button></div></div>
</div>
</div>
<!-- Tab2: 个性化方案 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>方案名称</label><input class="filter-input" placeholder="请输入方案名称"></div>
<div class="filter-item"><label>适用人群</label><select class="filter-select"><option>全部</option><option>孕妇</option><option>少儿</option><option>老人</option><option>儿童</option><option>普通成人</option></select></div>
<div class="filter-item"><label>状态</label><select class="filter-select"><option>全部</option><option>已发布</option><option>草稿</option></select></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
<div class="filter-item"><label>方案名称</label><input class="filter-input" id="filterPlanName" placeholder="请输入方案名称"></div>
<div class="filter-item"><label>适用人群</label><select class="filter-select" id="filterPlanPeople"><option value="">全部</option><option>孕妇</option><option>少儿</option><option>老人</option><option>儿童</option><option>普通成人</option></select></div>
<div class="filter-item"><label>状态</label><select class="filter-select" id="filterPlanStatus"><option value="">全部</option><option>已发布</option><option>草稿</option></select></div>
<button class="btn btn-primary" onclick="filterPlan()">查询</button>
<button class="btn btn-default" onclick="resetFilter('filterPlanName','filterPlanPeople','filterPlanStatus')">重置</button>
</div>
<div class="action-bar">
<button class="btn btn-primary"> 新增方案</button>
<button class="btn btn-default">📅 批量安排复评</button>
<button class="btn btn-primary" onclick="openPlanAdd()">+ 新增方案</button>
</div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">个性化方案</div></div>
<div class="table-card__content"><table><thead><tr><th>方案名称</th><th>适用人群</th><th>BMI范围</th><th>每日热量(kcal)</th><th>交换份总数</th><th>状态</th><th>创建人</th><th>操作</th></tr></thead>
<tbody>
<tr><td>孕妇营养方案</td><td>孕妇</td><td>18.5-24.9</td><td>2,200</td><td>24份</td><td><span class="tag tag-green">已发布</span></td><td>营养师A</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>少儿营养方案</td><td>少儿</td><td>15-20</td><td>1,600</td><td>18份</td><td><span class="tag tag-green">已发布</span></td><td>营养师B</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>老人营养方案</td><td>老人</td><td>20-26.9</td><td>1,800</td><td>20份</td><td><span class="tag tag-green">已发布</span></td><td>营养师A</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>儿童营养方案</td><td>儿童</td><td>14-19</td><td>1,400</td><td>16份</td><td><span class="tag tag-green">已发布</span></td><td>营养师B</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>普通成人方案</td><td>普通成人</td><td>18.5-23.9</td><td>2,000</td><td>22份</td><td><span class="tag tag-green">已发布</span></td><td>营养师A</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>孕妇营养方案</td><td>孕妇</td><td>18.5-24.9</td><td>2,200</td><td>24份</td><td><span class="tag tag-green">已发布</span></td><td>营养师A</td><td class="td-actions"><span class="link" onclick="openPlanDetail('孕妇营养方案')">详情</span> <span class="link" onclick="openPlanEdit('孕妇营养方案','孕妇','18.5','24.9','2200','24','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('个性化方案','孕妇营养方案')">删除</span></td></tr>
<tr><td>少儿营养方案</td><td>少儿</td><td>15-20</td><td>1,600</td><td>18份</td><td><span class="tag tag-green">已发布</span></td><td>营养师B</td><td class="td-actions"><span class="link" onclick="openPlanDetail('少儿营养方案')">详情</span> <span class="link" onclick="openPlanEdit('少儿营养方案','少儿','15','20','1600','18','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('个性化方案','少儿营养方案')">删除</span></td></tr>
<tr><td>老人营养方案</td><td>老人</td><td>20-26.9</td><td>1,800</td><td>20份</td><td><span class="tag tag-green">已发布</span></td><td>营养师A</td><td class="td-actions"><span class="link" onclick="openPlanDetail('老人营养方案')">详情</span> <span class="link" onclick="openPlanEdit('老人营养方案','老人','20','26.9','1800','20','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('个性化方案','老人营养方案')">删除</span></td></tr>
<tr><td>儿童营养方案</td><td>儿童</td><td>14-19</td><td>1,400</td><td>16份</td><td><span class="tag tag-green">已发布</span></td><td>营养师B</td><td class="td-actions"><span class="link" onclick="openPlanDetail('儿童营养方案')">详情</span> <span class="link" onclick="openPlanEdit('儿童营养方案','儿童','14','19','1400','16','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('个性化方案','儿童营养方案')">删除</span></td></tr>
<tr><td>普通成人方案</td><td>普通成人</td><td>18.5-23.9</td><td>2,000</td><td>22份</td><td><span class="tag tag-green">已发布</span></td><td>营养师A</td><td class="td-actions"><span class="link" onclick="openPlanDetail('普通成人方案')">详情</span> <span class="link" onclick="openPlanEdit('普通成人方案','普通成人','18.5','23.9','2000','22','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('个性化方案','普通成人方案')">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><div class="pagination__info">第 1-5 条 / 共 5 条</div><div class="pagination__btns"><button class="page-btn disabled">上一页</button><button class="page-btn active">1</button><button class="page-btn disabled">下一页</button></div></div>
</div>
</div>
<!-- Tab3: 大众方案 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>方案场景</label><select class="filter-select"><option>全部</option><option>工厂</option><option>院校-大学</option><option>院校-中学</option><option>院校-小学</option></select></div>
<div class="filter-item"><label>状态</label><select class="filter-select"><option>全部</option><option>已发布</option><option>草稿</option></select></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
<div class="filter-item"><label>方案场景</label><select class="filter-select" id="filterMassScene"><option value="">全部</option><option>工厂</option><option>院校-大学</option><option>院校-中学</option><option>院校-小学</option></select></div>
<div class="filter-item"><label>状态</label><select class="filter-select" id="filterMassStatus"><option value="">全部</option><option>已发布</option><option>草稿</option></select></div>
<button class="btn btn-primary" onclick="filterMass()">查询</button>
<button class="btn btn-default" onclick="resetFilter('filterMassScene','filterMassStatus')">重置</button>
</div>
<div class="action-bar">
<button class="btn btn-primary"> 新增大众方案</button>
<button class="btn btn-primary" onclick="openMassAdd()">+ 新增大众方案</button>
</div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">大众方案</div></div>
<div class="table-card__content"><table><thead><tr><th>方案名称</th><th>适用场景</th><th>涵盖餐次</th><th>平均热量(kcal)</th><th>推荐覆盖率</th><th>状态</th><th>操作</th></tr></thead>
<tbody>
<tr><td>工厂标准方案</td><td>工厂</td><td>正餐/加班餐/夜宵</td><td>2,300</td><td>85%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>院校大学方案</td><td>院校-大学</td><td>正餐</td><td>2,100</td><td>92%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>院校中学方案</td><td>院校-中学</td><td>正餐</td><td>1,900</td><td>88%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>院校小学方案</td><td>院校-小学</td><td>正餐</td><td>1,600</td><td>90%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>工厂标准方案</td><td>工厂</td><td>正餐/加班餐/夜宵</td><td>2,300</td><td>85%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link" onclick="openMassDetail('工厂标准方案')">详情</span> <span class="link" onclick="openMassEdit('工厂标准方案','工厂','正餐/加班餐/夜宵','2300','85','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('大众方案','工厂标准方案')">删除</span></td></tr>
<tr><td>院校大学方案</td><td>院校-大学</td><td>正餐</td><td>2,100</td><td>92%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link" onclick="openMassDetail('院校大学方案')">详情</span> <span class="link" onclick="openMassEdit('院校大学方案','院校-大学','正餐','2100','92','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('大众方案','院校大学方案')">删除</span></td></tr>
<tr><td>院校中学方案</td><td>院校-中学</td><td>正餐</td><td>1,900</td><td>88%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link" onclick="openMassDetail('院校中学方案')">详情</span> <span class="link" onclick="openMassEdit('院校中学方案','院校-中学','正餐','1900','88','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('大众方案','院校中学方案')">删除</span></td></tr>
<tr><td>院校小学方案</td><td>院校-小学</td><td>正餐</td><td>1,600</td><td>90%</td><td><span class="tag tag-green">已发布</span></td><td class="td-actions"><span class="link" onclick="openMassDetail('院校小学方案')">详情</span> <span class="link" onclick="openMassEdit('院校小学方案','院校-小学','正餐','1600','90','已发布')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('大众方案','院校小学方案')">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><div class="pagination__info">第 1-4 条 / 共 4 条</div><div class="pagination__btns"><button class="page-btn disabled">上一页</button><button class="page-btn active">1</button><button class="page-btn disabled">下一页</button></div></div>
</div>
</div>
<!-- Tab4: 动态规则 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>触发数据源</label><select class="filter-select"><option>全部</option><option>智能手表步数</option><option>体脂秤数据</option><option>手动录入运动量</option></select></div>
<div class="filter-item"><label>规则状态</label><select class="filter-select"><option>全部</option><option>启用</option><option>禁用</option></select></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
<div class="filter-item"><label>触发数据源</label><select class="filter-select" id="filterRuleSrc"><option value="">全部</option><option>智能手表步数</option><option>体脂秤数据</option><option>手动录入运动量</option></select></div>
<div class="filter-item"><label>规则状态</label><select class="filter-select" id="filterRuleStatus"><option value="">全部</option><option>启用</option><option>禁用</option></select></div>
<button class="btn btn-primary" onclick="filterRule()">查询</button>
<button class="btn btn-default" onclick="resetFilter('filterRuleSrc','filterRuleStatus')">重置</button>
</div>
<div class="action-bar">
<button class="btn btn-primary"> 新增规则</button>
<button class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
<button class="btn btn-primary" onclick="openRuleAdd()">+ 新增规则</button>
<button class="btn btn-default" onclick="alert('已生成导出任务,请到导出任务列表查看进度。')">导出列表数据</button>
<button class="btn btn-default" onclick="openExportTask()">查看导出任务</button>
</div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">动态规则</div></div>
<div class="table-card__content"><table><thead><tr><th>规则编号</th><th>触发数据源</th><th>触发条件</th><th>调整项目</th><th>调整方式</th><th>调整量</th><th>适用人群</th><th>规则状态</th><th>备注说明</th><th>操作</th></tr></thead>
<tbody>
<tr><td>DR-001</td><td>智能手表步数</td><td>当日步数 ≥ 10000</td><td>热量</td><td>增加</td><td>+180kcal</td><td>全部用户</td><td><span class="tag tag-green">启用</span></td><td>高运动量补能</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>DR-002</td><td>体脂秤数据</td><td>体脂率连续7天上升</td><td>碳水化合物</td><td>减少</td><td>-12%</td><td>指定VIP用户</td><td><span class="tag tag-green">启用</span></td><td>控脂干预</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>DR-003</td><td>手动录入运动量</td><td>有氧运动 ≥ 60分钟</td><td>蛋白质</td><td>增加</td><td>+18g</td><td>成人用户</td><td><span class="tag tag-green">启用</span></td><td>运动后恢复</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>DR-004</td><td>智能手表步数</td><td>当日步数 < 3000</td><td>热量</td><td>减少</td><td>-120kcal</td><td>全部用户</td><td><span class="tag tag-gray">禁用</span></td><td>低活动日控能</td><td class="td-actions"><span class="link">编辑</span></td></tr>
<tr><td>DR-001</td><td>智能手表步数</td><td>当日步数 ≥ 10000</td><td>热量</td><td>增加</td><td>+180kcal</td><td>全部用户</td><td><span class="tag tag-green">启用</span></td><td>高运动量补能</td><td class="td-actions"><span class="link" onclick="openRuleEdit('DR-001','智能手表步数','当日步数 ≥ 10000','热量','增加','+180kcal','全部用户','启用','高运动量补能')">编辑</span> <span class="link" onclick="toggleRuleStatus(this,'DR-001','禁用')">禁用</span> <span class="btn-danger-text" onclick="confirmDelete('动态规则','DR-001')">删除</span></td></tr>
<tr><td>DR-002</td><td>体脂秤数据</td><td>体脂率连续7天上升</td><td>碳水化合物</td><td>减少</td><td>-12%</td><td>指定VIP用户</td><td><span class="tag tag-green">启用</span></td><td>控脂干预</td><td class="td-actions"><span class="link" onclick="openRuleEdit('DR-002','体脂秤数据','体脂率连续7天上升','碳水化合物','减少','-12%','指定VIP用户','启用','控脂干预')">编辑</span> <span class="link" onclick="toggleRuleStatus(this,'DR-002','禁用')">禁用</span> <span class="btn-danger-text" onclick="confirmDelete('动态规则','DR-002')">删除</span></td></tr>
<tr><td>DR-003</td><td>手动录入运动量</td><td>有氧运动 ≥ 60分钟</td><td>蛋白质</td><td>增加</td><td>+18g</td><td>成人用户</td><td><span class="tag tag-green">启用</span></td><td>运动后恢复</td><td class="td-actions"><span class="link" onclick="openRuleEdit('DR-003','手动录入运动量','有氧运动 ≥ 60分钟','蛋白质','增加','+18g','成人用户','启用','运动后恢复')">编辑</span> <span class="link" onclick="toggleRuleStatus(this,'DR-003','禁用')">禁用</span> <span class="btn-danger-text" onclick="confirmDelete('动态规则','DR-003')">删除</span></td></tr>
<tr><td>DR-004</td><td>智能手表步数</td><td>当日步数 < 3000</td><td>热量</td><td>减少</td><td>-120kcal</td><td>全部用户</td><td><span class="tag tag-gray">禁用</span></td><td>低活动日控能</td><td class="td-actions"><span class="link" onclick="openRuleEdit('DR-004','智能手表步数','当日步数 < 3000','热量','减少','-120kcal','全部用户','禁用','低活动日控能')">编辑</span> <span class="link" onclick="toggleRuleStatus(this,'DR-004','启用')">启用</span> <span class="btn-danger-text" onclick="confirmDelete('动态规则','DR-004')">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><div class="pagination__info">第 1-4 条 / 共 4 条</div><div class="pagination__btns"><button class="page-btn disabled">上一页</button><button class="page-btn active">1</button><button class="page-btn disabled">下一页</button></div></div>
</div>
</div>
</main>
<!-- ==================== 弹窗区域 ==================== -->
<!-- 算法标准编辑弹窗 -->
<div class="modal-mask" id="modalAlgoEdit">
<div class="modal modal--lg">
<div class="modal__header"><div class="modal__title" id="algoEditTitle">编辑算法标准</div><button class="modal__close" onclick="closeModal('modalAlgoEdit')">×</button></div>
<div class="modal__body">
<div class="form-row">
<div class="form-group"><label>人群类型</label><input id="aeType" disabled></div>
<div class="form-group"><label>热量下限 (kcal)<span class="required">*</span></label><input id="aeCalMin" type="number"></div>
<div class="form-group"><label>热量上限 (kcal)<span class="required">*</span></label><input id="aeCalMax" type="number"></div>
</div>
<div class="form-row">
<div class="form-group"><label>蛋白质比例 (%)<span class="required">*</span></label><input id="aeProtein" type="number"></div>
<div class="form-group"><label>脂肪比例 (%)<span class="required">*</span></label><input id="aeFat" type="number"></div>
<div class="form-group"><label>碳水比例 (%)<span class="required">*</span></label><input id="aeCarb" type="number"></div>
</div>
<div class="form-row">
<div class="form-group"><label>钙 (mg)</label><input id="aeCa" type="number"></div>
<div class="form-group"><label>铁 (mg)</label><input id="aeFe" type="number"></div>
<div class="form-group"><label>维生素C (mg)</label><input id="aeVitC" type="number"></div>
</div>
<div class="form-row">
<div class="form-group"><label>膳食纤维 (g)</label><input id="aeFiber" type="number"></div>
<div class="form-group"><label>每日饮水量 (mL)</label><input id="aeWater" type="number"></div>
<div class="form-group"></div>
</div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalAlgoEdit')">取消</button><button class="btn btn-primary" onclick="alert('保存成功');closeModal('modalAlgoEdit')">保存</button></div>
</div>
</div>
<!-- 算法标准详情弹窗 -->
<div class="modal-mask" id="modalAlgoDetail">
<div class="modal">
<div class="modal__header"><div class="modal__title" id="algoDetailTitle">算法标准详情</div><button class="modal__close" onclick="closeModal('modalAlgoDetail')">×</button></div>
<div class="modal__body" id="algoDetailBody"></div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalAlgoDetail')">关闭</button></div>
</div>
</div>
<!-- 个性化方案 新增/编辑弹窗 -->
<div class="modal-mask" id="modalPlan">
<div class="modal">
<div class="modal__header"><div class="modal__title" id="planModalTitle">新增个性化方案</div><button class="modal__close" onclick="closeModal('modalPlan')">×</button></div>
<div class="modal__body">
<div class="form-row">
<div class="form-group"><label>方案名称<span class="required">*</span></label><input id="planName" placeholder="请输入方案名称"></div>
<div class="form-group"><label>适用人群<span class="required">*</span></label><select id="planPeople"><option value="">请选择</option><option>孕妇</option><option>少儿</option><option>老人</option><option>儿童</option><option>普通成人</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>BMI下限<span class="required">*</span></label><input id="planBmiMin" type="number" step="0.1"></div>
<div class="form-group"><label>BMI上限<span class="required">*</span></label><input id="planBmiMax" type="number" step="0.1"></div>
</div>
<div class="form-row">
<div class="form-group"><label>每日热量 (kcal)<span class="required">*</span></label><input id="planCalorie" type="number"></div>
<div class="form-group"><label>交换份总数<span class="required">*</span></label><input id="planExchange" type="number"></div>
</div>
<div class="form-row">
<div class="form-group"><label>状态</label><select id="planStatus"><option>已发布</option><option>草稿</option></select></div>
<div class="form-group"></div>
</div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalPlan')">取消</button><button class="btn btn-primary" onclick="alert('保存成功');closeModal('modalPlan')">保存</button></div>
</div>
</div>
<!-- 个性化方案详情弹窗 -->
<div class="modal-mask" id="modalPlanDetail">
<div class="modal">
<div class="modal__header"><div class="modal__title" id="planDetailTitle">方案详情</div><button class="modal__close" onclick="closeModal('modalPlanDetail')">×</button></div>
<div class="modal__body" id="planDetailBody"></div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalPlanDetail')">关闭</button></div>
</div>
</div>
<!-- 大众方案 新增/编辑弹窗 -->
<div class="modal-mask" id="modalMass">
<div class="modal">
<div class="modal__header"><div class="modal__title" id="massModalTitle">新增大众方案</div><button class="modal__close" onclick="closeModal('modalMass')">×</button></div>
<div class="modal__body">
<div class="form-row">
<div class="form-group"><label>方案名称<span class="required">*</span></label><input id="massName" placeholder="请输入方案名称"></div>
<div class="form-group"><label>适用场景<span class="required">*</span></label><select id="massScene"><option value="">请选择</option><option>工厂</option><option>院校-大学</option><option>院校-中学</option><option>院校-小学</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>涵盖餐次<span class="required">*</span></label><input id="massMeal" placeholder="如:正餐/加班餐/夜宵"></div>
<div class="form-group"><label>平均热量 (kcal)<span class="required">*</span></label><input id="massCalorie" type="number"></div>
</div>
<div class="form-row">
<div class="form-group"><label>推荐覆盖率 (%)</label><input id="massCover" type="number" min="0" max="100"></div>
<div class="form-group"><label>状态</label><select id="massStatus"><option>已发布</option><option>草稿</option></select></div>
</div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalMass')">取消</button><button class="btn btn-primary" onclick="alert('保存成功');closeModal('modalMass')">保存</button></div>
</div>
</div>
<!-- 大众方案详情弹窗 -->
<div class="modal-mask" id="modalMassDetail">
<div class="modal">
<div class="modal__header"><div class="modal__title" id="massDetailTitle">大众方案详情</div><button class="modal__close" onclick="closeModal('modalMassDetail')">×</button></div>
<div class="modal__body" id="massDetailBody"></div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalMassDetail')">关闭</button></div>
</div>
</div>
<!-- 动态规则 新增/编辑弹窗 -->
<div class="modal-mask" id="modalRule">
<div class="modal modal--lg">
<div class="modal__header"><div class="modal__title" id="ruleModalTitle">新增动态规则</div><button class="modal__close" onclick="closeModal('modalRule')">×</button></div>
<div class="modal__body">
<div class="form-row">
<div class="form-group"><label>规则编号</label><input id="ruleNo" placeholder="自动生成" disabled></div>
<div class="form-group"><label>触发数据源<span class="required">*</span></label><select id="ruleSrc"><option value="">请选择</option><option>智能手表步数</option><option>体脂秤数据</option><option>手动录入运动量</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>触发条件<span class="required">*</span></label><input id="ruleCond" placeholder="如:当日步数 ≥ 10000"></div>
<div class="form-group"><label>调整项目<span class="required">*</span></label><select id="ruleItem"><option value="">请选择</option><option>热量</option><option>蛋白质</option><option>碳水化合物</option><option>脂肪</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>调整方式<span class="required">*</span></label><select id="ruleDir"><option>增加</option><option>减少</option></select></div>
<div class="form-group"><label>调整量<span class="required">*</span></label><input id="ruleAmount" placeholder="如:+180kcal"></div>
</div>
<div class="form-row">
<div class="form-group"><label>适用人群</label><select id="rulePeople"><option>全部用户</option><option>成人用户</option><option>指定VIP用户</option></select></div>
<div class="form-group"><label>规则状态</label><select id="ruleState"><option>启用</option><option>禁用</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>备注说明</label><textarea id="ruleNote" placeholder="可选备注"></textarea></div>
</div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalRule')">取消</button><button class="btn btn-primary" onclick="alert('保存成功');closeModal('modalRule')">保存</button></div>
</div>
</div>
<!-- 导出任务列表弹窗 -->
<div class="modal-mask" id="modalExportTask">
<div class="modal modal--lg">
<div class="modal__header"><div class="modal__title">导出任务列表</div><button class="modal__close" onclick="closeModal('modalExportTask')">×</button></div>
<div class="modal__body">
<table style="width:100%;border-collapse:collapse;font-size:13px;">
<thead><tr><th style="background:#FAFAFA;padding:10px 12px;text-align:left;border-bottom:1px solid #E8E8E8;">任务编号</th><th style="background:#FAFAFA;padding:10px 12px;text-align:left;border-bottom:1px solid #E8E8E8;">创建时间</th><th style="background:#FAFAFA;padding:10px 12px;text-align:left;border-bottom:1px solid #E8E8E8;">导出内容</th><th style="background:#FAFAFA;padding:10px 12px;text-align:left;border-bottom:1px solid #E8E8E8;">状态</th><th style="background:#FAFAFA;padding:10px 12px;text-align:left;border-bottom:1px solid #E8E8E8;">操作</th></tr></thead>
<tbody>
<tr><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;">EXP-001</td><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;">2026-04-22 15:30</td><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;">动态规则列表</td><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;"><span class="tag tag-green">已完成</span></td><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;"><span class="link">下载</span></td></tr>
<tr><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;">EXP-002</td><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;">2026-04-22 16:00</td><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;">动态规则列表</td><td style="padding:10px 12px;border-bottom:1px solid #F0F0F0;"><span class="tag tag-blue">进行中</span></td><td style="padding:10px 12px;"></td></tr>
</tbody>
</table>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalExportTask')">关闭</button></div>
</div>
</div>
<script>
/* ===== Tab 切换 ===== */
function switchTab(idx){
document.querySelectorAll('.page-tab').forEach((t,i)=>t.classList.toggle('active',i===idx));
document.querySelectorAll('.tab-panel').forEach((p,i)=>p.classList.toggle('active',i===idx));
document.querySelectorAll('.page-tab').forEach(function(t,i){t.classList.toggle('active',i===idx);});
document.querySelectorAll('.tab-panel').forEach(function(p,i){p.classList.toggle('active',i===idx);});
}
/* ===== 侧边栏折叠 ===== */
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
/* ===== 弹窗通用 ===== */
function openModal(id){document.getElementById(id).classList.add('open');}
function closeModal(id){document.getElementById(id).classList.remove('open');}
/* 点击遮罩关闭 */
document.querySelectorAll('.modal-mask').forEach(function(mask){
mask.addEventListener('click',function(e){if(e.target===mask)mask.classList.remove('open');});
});
/* ===== 筛选交互 ===== */
function filterAlgo(){alert('【交互注释】查询算法标准:人群类型 = '+document.getElementById('filterAlgoType').value);}
function filterPlan(){alert('【交互注释】查询个性化方案:名称='+document.getElementById('filterPlanName').value+' 人群='+document.getElementById('filterPlanPeople').value+' 状态='+document.getElementById('filterPlanStatus').value);}
function filterMass(){alert('【交互注释】查询大众方案:场景='+document.getElementById('filterMassScene').value+' 状态='+document.getElementById('filterMassStatus').value);}
function filterRule(){alert('【交互注释】查询动态规则:数据源='+document.getElementById('filterRuleSrc').value+' 状态='+document.getElementById('filterRuleStatus').value);}
function resetFilter(){
for(var i=0;i<arguments.length;i++){
var el=document.getElementById(arguments[i]);
if(el){el.tagName==='SELECT'?el.selectedIndex=0:el.value='';}
}
}
/* ===== 算法标准 ===== */
function openAlgoEdit(type,calMin,calMax,protein,fat,carb,ca,fe,vitC,fiber,water){
document.getElementById('algoEditTitle').textContent='编辑算法标准 - '+type;
document.getElementById('aeType').value=type;
document.getElementById('aeCalMin').value=calMin;
document.getElementById('aeCalMax').value=calMax;
document.getElementById('aeProtein').value=protein;
document.getElementById('aeFat').value=fat;
document.getElementById('aeCarb').value=carb;
document.getElementById('aeCa').value=ca;
document.getElementById('aeFe').value=fe;
document.getElementById('aeVitC').value=vitC;
document.getElementById('aeFiber').value=fiber;
document.getElementById('aeWater').value=water;
openModal('modalAlgoEdit');
}
function openAlgoDetail(type){
var data={
'成人男性':{cal:'2,200 - 2,600',protein:'15%',fat:'30%',carb:'55%',ca:'800mg',fe:'12mg',vitC:'100mg',fiber:'25g',water:'1,500mL'},
'成人女性':{cal:'1,800 - 2,200',protein:'15%',fat:'30%',carb:'55%',ca:'800mg',fe:'12mg',vitC:'100mg',fiber:'25g',water:'1,500mL'},
'老年人':{cal:'1,600 - 2,000',protein:'18%',fat:'25%',carb:'57%',ca:'800mg',fe:'12mg',vitC:'100mg',fiber:'25g',water:'1,500mL'},
'儿童':{cal:'1,400 - 1,800',protein:'14%',fat:'32%',carb:'54%',ca:'800mg',fe:'12mg',vitC:'100mg',fiber:'25g',water:'1,500mL'}
};
var d=data[type]||data['成人男性'];
document.getElementById('algoDetailTitle').textContent='算法标准详情 - '+type;
document.getElementById('algoDetailBody').innerHTML=
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;">'+
'<div><strong style="color:#666;font-size:12px;">人群类型</strong><div style="margin-top:4px;">'+type+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">热量范围</strong><div style="margin-top:4px;">'+d.cal+' kcal</div></div>'+
'<div><strong style="color:#666;font-size:12px;">蛋白质比例</strong><div style="margin-top:4px;">'+d.protein+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">脂肪比例</strong><div style="margin-top:4px;">'+d.fat+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">碳水比例</strong><div style="margin-top:4px;">'+d.carb+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">钙</strong><div style="margin-top:4px;">'+d.ca+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">铁</strong><div style="margin-top:4px;">'+d.fe+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">维生素C</strong><div style="margin-top:4px;">'+d.vitC+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">膳食纤维</strong><div style="margin-top:4px;">'+d.fiber+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">每日饮水量</strong><div style="margin-top:4px;">'+d.water+'</div></div>'+
'</div>';
openModal('modalAlgoDetail');
}
/* ===== 个性化方案 ===== */
function openPlanAdd(){
document.getElementById('planModalTitle').textContent='新增个性化方案';
document.getElementById('planName').value='';
document.getElementById('planPeople').selectedIndex=0;
document.getElementById('planBmiMin').value='';
document.getElementById('planBmiMax').value='';
document.getElementById('planCalorie').value='';
document.getElementById('planExchange').value='';
document.getElementById('planStatus').selectedIndex=0;
openModal('modalPlan');
}
function openPlanEdit(name,people,bmiMin,bmiMax,cal,exchange,status){
document.getElementById('planModalTitle').textContent='编辑个性化方案';
document.getElementById('planName').value=name;
var sel=document.getElementById('planPeople');
for(var i=0;i<sel.options.length;i++){if(sel.options[i].text===people)sel.selectedIndex=i;}
document.getElementById('planBmiMin').value=bmiMin;
document.getElementById('planBmiMax').value=bmiMax;
document.getElementById('planCalorie').value=cal;
document.getElementById('planExchange').value=exchange;
document.getElementById('planStatus').value=status;
openModal('modalPlan');
}
function openPlanDetail(name){
document.getElementById('planDetailTitle').textContent=name+' - 详情';
var data={
'孕妇营养方案':{people:'孕妇',bmi:'18.5-24.9',cal:'2,200 kcal',ex:'24份',status:'已发布',creator:'营养师A'},
'少儿营养方案':{people:'少儿',bmi:'15-20',cal:'1,600 kcal',ex:'18份',status:'已发布',creator:'营养师B'},
'老人营养方案':{people:'老人',bmi:'20-26.9',cal:'1,800 kcal',ex:'20份',status:'已发布',creator:'营养师A'},
'儿童营养方案':{people:'儿童',bmi:'14-19',cal:'1,400 kcal',ex:'16份',status:'已发布',creator:'营养师B'},
'普通成人方案':{people:'普通成人',bmi:'18.5-23.9',cal:'2,000 kcal',ex:'22份',status:'已发布',creator:'营养师A'}
};
var d=data[name]||data['孕妇营养方案'];
document.getElementById('planDetailBody').innerHTML=
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;">'+
'<div><strong style="color:#666;font-size:12px;">方案名称</strong><div style="margin-top:4px;">'+name+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">适用人群</strong><div style="margin-top:4px;">'+d.people+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">BMI范围</strong><div style="margin-top:4px;">'+d.bmi+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">每日热量</strong><div style="margin-top:4px;">'+d.cal+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">交换份总数</strong><div style="margin-top:4px;">'+d.ex+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">状态</strong><div style="margin-top:4px;"><span class="tag tag-green">'+d.status+'</span></div></div>'+
'<div><strong style="color:#666;font-size:12px;">创建人</strong><div style="margin-top:4px;">'+d.creator+'</div></div>'+
'</div>';
openModal('modalPlanDetail');
}
/* ===== 大众方案 ===== */
function openMassAdd(){
document.getElementById('massModalTitle').textContent='新增大众方案';
document.getElementById('massName').value='';
document.getElementById('massScene').selectedIndex=0;
document.getElementById('massMeal').value='';
document.getElementById('massCalorie').value='';
document.getElementById('massCover').value='';
document.getElementById('massStatus').selectedIndex=0;
openModal('modalMass');
}
function openMassEdit(name,scene,meal,cal,cover,status){
document.getElementById('massModalTitle').textContent='编辑大众方案';
document.getElementById('massName').value=name;
var sel=document.getElementById('massScene');
for(var i=0;i<sel.options.length;i++){if(sel.options[i].text===scene)sel.selectedIndex=i;}
document.getElementById('massMeal').value=meal;
document.getElementById('massCalorie').value=cal;
document.getElementById('massCover').value=cover;
document.getElementById('massStatus').value=status;
openModal('modalMass');
}
function openMassDetail(name){
document.getElementById('massDetailTitle').textContent=name+' - 详情';
var data={
'工厂标准方案':{scene:'工厂',meal:'正餐/加班餐/夜宵',cal:'2,300 kcal',cover:'85%',status:'已发布'},
'院校大学方案':{scene:'院校-大学',meal:'正餐',cal:'2,100 kcal',cover:'92%',status:'已发布'},
'院校中学方案':{scene:'院校-中学',meal:'正餐',cal:'1,900 kcal',cover:'88%',status:'已发布'},
'院校小学方案':{scene:'院校-小学',meal:'正餐',cal:'1,600 kcal',cover:'90%',status:'已发布'}
};
var d=data[name]||data['工厂标准方案'];
document.getElementById('massDetailBody').innerHTML=
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;">'+
'<div><strong style="color:#666;font-size:12px;">方案名称</strong><div style="margin-top:4px;">'+name+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">适用场景</strong><div style="margin-top:4px;">'+d.scene+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">涵盖餐次</strong><div style="margin-top:4px;">'+d.meal+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">平均热量</strong><div style="margin-top:4px;">'+d.cal+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">推荐覆盖率</strong><div style="margin-top:4px;">'+d.cover+'</div></div>'+
'<div><strong style="color:#666;font-size:12px;">状态</strong><div style="margin-top:4px;"><span class="tag tag-green">'+d.status+'</span></div></div>'+
'</div>';
openModal('modalMassDetail');
}
/* ===== 动态规则 ===== */
function openRuleAdd(){
document.getElementById('ruleModalTitle').textContent='新增动态规则';
document.getElementById('ruleNo').value='自动生成';
document.getElementById('ruleSrc').selectedIndex=0;
document.getElementById('ruleCond').value='';
document.getElementById('ruleItem').selectedIndex=0;
document.getElementById('ruleDir').selectedIndex=0;
document.getElementById('ruleAmount').value='';
document.getElementById('rulePeople').selectedIndex=0;
document.getElementById('ruleState').selectedIndex=0;
document.getElementById('ruleNote').value='';
openModal('modalRule');
}
function openRuleEdit(no,src,cond,item,dir,amount,people,state,note){
document.getElementById('ruleModalTitle').textContent='编辑动态规则 - '+no;
document.getElementById('ruleNo').value=no;
var srcSel=document.getElementById('ruleSrc');
for(var i=0;i<srcSel.options.length;i++){if(srcSel.options[i].text===src)srcSel.selectedIndex=i;}
document.getElementById('ruleCond').value=cond;
var itemSel=document.getElementById('ruleItem');
for(var j=0;j<itemSel.options.length;j++){if(itemSel.options[j].text===item)itemSel.selectedIndex=j;}
document.getElementById('ruleDir').value=dir;
document.getElementById('ruleAmount').value=amount;
var peopleSel=document.getElementById('rulePeople');
for(var k=0;k<peopleSel.options.length;k++){if(peopleSel.options[k].text===people)peopleSel.selectedIndex=k;}
document.getElementById('ruleState').value=state;
document.getElementById('ruleNote').value=note;
openModal('modalRule');
}
function toggleRuleStatus(el,no,newStatus){
if(confirm('确认将规则 '+no+' 状态更改为「'+newStatus+'」?')){
/* 交互注释:调用后端接口切换规则状态,成功后刷新列表 */
var row=el.closest('tr');
var statusCell=row.querySelector('td:nth-child(8)');
if(newStatus==='启用'){
statusCell.innerHTML='<span class="tag tag-green">启用</span>';
el.textContent='禁用';
el.setAttribute('onclick',"toggleRuleStatus(this,'"+no+"','禁用')");
}else{
statusCell.innerHTML='<span class="tag tag-gray">禁用</span>';
el.textContent='启用';
el.setAttribute('onclick',"toggleRuleStatus(this,'"+no+"','启用')");
}
}
}
function openExportTask(){openModal('modalExportTask');}
/* ===== 通用删除确认 ===== */
function confirmDelete(type,name){
if(confirm('确认删除'+type+'「'+name+'」?此操作不可恢复。')){
/* 交互注释:调用后端删除接口,成功后从列表移除该行 */
alert('已删除'+type+'「'+name+'」');
}
}
</script>
</body>
</html>
</html>
+225 -5
View File
@@ -87,6 +87,38 @@ tbody tr:hover{background:#F8FBFF;}
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;}
.td-actions .link{margin-right:10px;}
.td-actions .link:last-child,.td-actions .btn-danger-text:last-child{margin-right:0;}
.btn-danger-text{background:none;border:none;color:var(--danger);cursor:pointer;font-size:var(--font-sm);padding:0;}.btn-danger-text:hover{color:#ff7875;}
/* 弹窗 */
.modal-mask{position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;display:none;align-items:center;justify-content:center;}
.modal-mask.open{display:flex;}
.modal{background:#fff;border-radius:var(--radius-lg);width:640px;max-height:85vh;display:flex;flex-direction:column;box-shadow:var(--shadow-md);}
.modal--sm{width:480px;}
.modal--lg{width:800px;}
.modal__header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);}
.modal__title{font-size:var(--font-lg);font-weight:600;}
.modal__close{width:28px;height:28px;border:none;background:none;font-size:18px;cursor:pointer;color:var(--text-placeholder);display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);transition:all .15s;}
.modal__close:hover{background:#F5F5F5;color:var(--text-primary);}
.modal__body{padding:20px 24px;overflow-y:auto;flex:1;}
.modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
.form-row{display:flex;gap:16px;margin-bottom:16px;}
.form-group{flex:1;display:flex;flex-direction:column;gap:6px;}
.form-group label{font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;}
.form-group label .required{color:var(--danger);margin-left:2px;}
.form-group input,.form-group select,.form-group textarea{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;width:100%;}
.form-group input:focus,.form-group select:focus,.form-group textarea:focus{border-color:var(--primary);}
.form-group 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-group textarea{height:80px;padding:10px 12px;resize:vertical;}
.form-group .hint{font-size:var(--font-xs);color:var(--text-placeholder);}
.form-group input:disabled,.form-group select:disabled{background:#F5F5F5;cursor:not-allowed;}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
.detail-grid__item strong{color:#666;font-size:12px;display:block;margin-bottom:4px;}
.detail-grid__item div{font-size:var(--font-sm);color:var(--text-primary);}
.detail-grid--3col{grid-template-columns:1fr 1fr 1fr;}
.detail-section{margin-bottom:20px;}
.detail-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid #F0F0F0;}
</style>
</head>
<body>
@@ -115,6 +147,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
@@ -164,8 +197,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary"> 新增VIP定制标准</button>
<button class="btn btn-default">📅 批量安排复评</button>
<button class="btn btn-primary" onclick="openVipAdd()"> 新增VIP定制标准</button>
</div>
<!-- 表格卡片 -->
@@ -177,9 +209,9 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<table>
<thead><tr><th>姓名</th><th>用户编号</th><th>所属单位</th><th>所属部门</th><th>定制目标类型</th><th>每日热量(kcal)</th><th>蛋白质目标(g)</th><th>脂肪目标(g)</th><th>碳水目标(g)</th><th>特殊限制项</th><th>制定营养师</th><th>生效日期</th><th>复评日期</th><th>操作</th></tr></thead>
<tbody>
<tr><td>张明</td><td>VIP001</td><td>长庆油田</td><td>采油一厂</td><td>减重</td><td>1650</td><td>95</td><td>45</td><td>170</td><td>禁高糖饮料</td><td>营养师李明</td><td>2026-04-01</td><td>2026-05-01</td><td class="td-actions"><span class="link">详情</span> <span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
<tr><td>刘芳</td><td>VIP002</td><td>长庆油田</td><td>采气二厂</td><td>控糖(糖尿病)</td><td>1750</td><td>88</td><td>50</td><td>160</td><td>低GI优先</td><td>营养师王芳</td><td>2026-03-20</td><td>2026-04-25</td><td class="td-actions"><span class="link">详情</span> <span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
<tr><td>陈伟</td><td>VIP003</td><td>长庆油田</td><td>机械制造公司</td><td>增肌</td><td>2450</td><td>140</td><td>68</td><td>280</td><td>增加优质蛋白</td><td>营养师李明</td><td>2026-04-10</td><td>2026-05-10</td><td class="td-actions"><span class="link">详情</span> <span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
<tr><td>张明</td><td>VIP001</td><td>长庆油田</td><td>采油一厂</td><td><span class="tag tag-blue">减重</span></td><td>1650</td><td>95</td><td>45</td><td>170</td><td>禁高糖饮料</td><td>营养师李明</td><td>2026-04-01</td><td>2026-05-01</td><td class="td-actions"><span class="link" onclick="openVipDetail('VIP001')">详情</span> <span class="link" onclick="openVipEdit('VIP001')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('VIP001','张明')">删除</span></td></tr>
<tr><td>刘芳</td><td>VIP002</td><td>长庆油田</td><td>采气二厂</td><td><span class="tag tag-orange">控糖(糖尿病)</span></td><td>1750</td><td>88</td><td>50</td><td>160</td><td>低GI优先</td><td>营养师王芳</td><td>2026-03-20</td><td>2026-04-25</td><td class="td-actions"><span class="link" onclick="openVipDetail('VIP002')">详情</span> <span class="link" onclick="openVipEdit('VIP002')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('VIP002','刘芳')">删除</span></td></tr>
<tr><td>陈伟</td><td>VIP003</td><td>长庆油田</td><td>机械制造公司</td><td><span class="tag tag-green">增肌</span></td><td>2450</td><td>140</td><td>68</td><td>280</td><td>增加优质蛋白</td><td>营养师李明</td><td>2026-04-10</td><td>2026-05-10</td><td class="td-actions"><span class="link" onclick="openVipDetail('VIP003')">详情</span> <span class="link" onclick="openVipEdit('VIP003')">编辑</span> <span class="btn-danger-text" onclick="confirmDelete('VIP003','陈伟')">删除</span></td></tr>
</tbody>
</table>
</div>
@@ -200,5 +232,193 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
</script>
<!-- ==================== 弹窗区域 ==================== -->
<!-- 新增/编辑 VIP定制标准弹窗 -->
<div class="modal-mask" id="modalVipForm">
<div class="modal modal--lg">
<div class="modal__header"><div class="modal__title" id="vipFormTitle">新增VIP定制标准</div><button class="modal__close" onclick="closeModal('modalVipForm')">×</button></div>
<div class="modal__body">
<div class="detail-section">
<div class="detail-section__title">👤 用户信息</div>
<div class="form-row">
<div class="form-group"><label>用户姓名<span class="required">*</span></label><input id="vfName" placeholder="请输入用户姓名"></div>
<div class="form-group"><label>用户编号<span class="required">*</span></label><input id="vfCode" placeholder="如 VIP004"></div>
</div>
<div class="form-row">
<div class="form-group"><label>所属单位<span class="required">*</span></label><select id="vfUnit"><option value="">请选择</option><option>长庆油田</option><option>采油一厂</option><option>采气二厂</option><option>机械制造公司</option></select></div>
<div class="form-group"><label>所属部门<span class="required">*</span></label><input id="vfDept" placeholder="请输入所属部门"></div>
</div>
</div>
<div class="detail-section">
<div class="detail-section__title">🎯 定制目标</div>
<div class="form-row">
<div class="form-group"><label>定制目标类型<span class="required">*</span></label><select id="vfGoalType"><option value="">请选择</option><option>减重</option><option>增肌</option><option>控糖(糖尿病)</option><option>控脂(高血脂)</option><option>控压(高血压)</option><option>术后康复</option></select></div>
<div class="form-group"><label>每日热量 (kcal)<span class="required">*</span></label><input id="vfCalorie" type="number" placeholder="如 1650"></div>
</div>
<div class="form-row">
<div class="form-group"><label>蛋白质目标 (g)<span class="required">*</span></label><input id="vfProtein" type="number" placeholder="如 95"></div>
<div class="form-group"><label>脂肪目标 (g)<span class="required">*</span></label><input id="vfFat" type="number" placeholder="如 45"></div>
<div class="form-group"><label>碳水目标 (g)<span class="required">*</span></label><input id="vfCarb" type="number" placeholder="如 170"></div>
</div>
</div>
<div class="detail-section">
<div class="detail-section__title">📋 其他信息</div>
<div class="form-row">
<div class="form-group"><label>特殊限制项</label><textarea id="vfRestriction" placeholder="如:禁高糖饮料、低GI优先等"></textarea></div>
</div>
<div class="form-row">
<div class="form-group"><label>制定营养师<span class="required">*</span></label><select id="vfNutritionist"><option value="">请选择</option><option>营养师李明</option><option>营养师王芳</option><option>营养师张华</option></select></div>
<div class="form-group"><label>生效日期<span class="required">*</span></label><input id="vfStartDate" type="date"></div>
<div class="form-group"><label>复评日期<span class="required">*</span></label><input id="vfReviewDate" type="date"></div>
</div>
</div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalVipForm')">取消</button><button class="btn btn-primary" onclick="alert('保存成功');closeModal('modalVipForm')">保存</button></div>
</div>
</div>
<!-- 详情弹窗 -->
<div class="modal-mask" id="modalVipDetail">
<div class="modal modal--lg">
<div class="modal__header"><div class="modal__title" id="vipDetailTitle">VIP定制标准详情</div><button class="modal__close" onclick="closeModal('modalVipDetail')">×</button></div>
<div class="modal__body" id="vipDetailBody"></div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalVipDetail')">关闭</button><button class="btn btn-primary" onclick="closeModal('modalVipDetail');openVipEdit(currentDetailId)">编辑</button></div>
</div>
</div>
<!-- 删除确认弹窗 -->
<div class="modal-mask" id="modalDelete">
<div class="modal modal--sm">
<div class="modal__header"><div class="modal__title">确认删除</div><button class="modal__close" onclick="closeModal('modalDelete')">×</button></div>
<div class="modal__body">
<div style="text-align:center;padding:12px 0;">
<div style="font-size:40px;margin-bottom:12px;">⚠️</div>
<div style="font-size:var(--font-base);font-weight:500;margin-bottom:8px;" id="deleteMsg">确认删除该VIP定制标准?</div>
<div style="font-size:var(--font-sm);color:var(--text-secondary);">删除后数据将无法恢复,请谨慎操作。</div>
</div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeModal('modalDelete')">取消</button><button class="btn btn-primary" style="background:var(--danger);" onclick="alert('已删除');closeModal('modalDelete')">确认删除</button></div>
</div>
</div>
<script>
/* ===== VIP数据 ===== */
var VIP_DATA = {
'VIP001':{name:'张明',code:'VIP001',unit:'长庆油田',dept:'采油一厂',goalType:'减重',calorie:1650,protein:95,fat:45,carb:170,restriction:'禁高糖饮料',nutritionist:'营养师李明',startDate:'2026-04-01',reviewDate:'2026-05-01'},
'VIP002':{name:'刘芳',code:'VIP002',unit:'长庆油田',dept:'采气二厂',goalType:'控糖(糖尿病)',calorie:1750,protein:88,fat:50,carb:160,restriction:'低GI优先',nutritionist:'营养师王芳',startDate:'2026-03-20',reviewDate:'2026-04-25'},
'VIP003':{name:'陈伟',code:'VIP003',unit:'长庆油田',dept:'机械制造公司',goalType:'增肌',calorie:2450,protein:140,fat:68,carb:280,restriction:'增加优质蛋白',nutritionist:'营养师李明',startDate:'2026-04-10',reviewDate:'2026-05-10'}
};
var currentDetailId = '';
/* ===== 弹窗通用 ===== */
function openModal(id){document.getElementById(id).classList.add('open');}
function closeModal(id){document.getElementById(id).classList.remove('open');}
document.querySelectorAll('.modal-mask').forEach(function(mask){
mask.addEventListener('click',function(e){if(e.target===mask)mask.classList.remove('open');});
});
/* ===== 新增 ===== */
function openVipAdd(){
document.getElementById('vipFormTitle').textContent='新增VIP定制标准';
document.getElementById('vfName').value='';
document.getElementById('vfCode').value='';
document.getElementById('vfUnit').selectedIndex=0;
document.getElementById('vfDept').value='';
document.getElementById('vfGoalType').selectedIndex=0;
document.getElementById('vfCalorie').value='';
document.getElementById('vfProtein').value='';
document.getElementById('vfFat').value='';
document.getElementById('vfCarb').value='';
document.getElementById('vfRestriction').value='';
document.getElementById('vfNutritionist').selectedIndex=0;
document.getElementById('vfStartDate').value='';
document.getElementById('vfReviewDate').value='';
document.getElementById('vfName').disabled=false;
document.getElementById('vfCode').disabled=false;
openModal('modalVipForm');
}
/* ===== 编辑 ===== */
function openVipEdit(code){
var d=VIP_DATA[code];if(!d)return;
document.getElementById('vipFormTitle').textContent='编辑VIP定制标准 - '+d.name;
document.getElementById('vfName').value=d.name;
document.getElementById('vfName').disabled=true;
document.getElementById('vfCode').value=d.code;
document.getElementById('vfCode').disabled=true;
setSelect('vfUnit',d.unit);
document.getElementById('vfDept').value=d.dept;
setSelect('vfGoalType',d.goalType);
document.getElementById('vfCalorie').value=d.calorie;
document.getElementById('vfProtein').value=d.protein;
document.getElementById('vfFat').value=d.fat;
document.getElementById('vfCarb').value=d.carb;
document.getElementById('vfRestriction').value=d.restriction;
setSelect('vfNutritionist',d.nutritionist);
document.getElementById('vfStartDate').value=d.startDate;
document.getElementById('vfReviewDate').value=d.reviewDate;
openModal('modalVipForm');
}
function setSelect(id,val){
var sel=document.getElementById(id);
for(var i=0;i<sel.options.length;i++){
if(sel.options[i].text===val||sel.options[i].value===val){sel.selectedIndex=i;return;}
}
}
/* ===== 详情 ===== */
function openVipDetail(code){
var d=VIP_DATA[code];if(!d)return;
currentDetailId=code;
document.getElementById('vipDetailTitle').textContent='VIP定制标准详情 - '+d.name;
var goalTagClass='tag-blue';
if(d.goalType.indexOf('控糖')>=0||d.goalType.indexOf('控脂')>=0)goalTagClass='tag-orange';
else if(d.goalType==='增肌')goalTagClass='tag-green';
else if(d.goalType.indexOf('控压')>=0)goalTagClass='tag-red';
document.getElementById('vipDetailBody').innerHTML=
'<div class="detail-section"><div class="detail-section__title">👤 用户信息</div>'+
'<div class="detail-grid">'+
'<div class="detail-grid__item"><strong>姓名</strong><div>'+d.name+'</div></div>'+
'<div class="detail-grid__item"><strong>用户编号</strong><div>'+d.code+'</div></div>'+
'<div class="detail-grid__item"><strong>所属单位</strong><div>'+d.unit+'</div></div>'+
'<div class="detail-grid__item"><strong>所属部门</strong><div>'+d.dept+'</div></div>'+
'</div></div>'+
'<div class="detail-section"><div class="detail-section__title">🎯 定制目标</div>'+
'<div class="detail-grid">'+
'<div class="detail-grid__item"><strong>定制目标类型</strong><div><span class="tag '+goalTagClass+'">'+d.goalType+'</span></div></div>'+
'<div class="detail-grid__item"><strong>每日热量</strong><div>'+d.calorie+' kcal</div></div>'+
'</div>'+
'<div class="detail-grid detail-grid--3col" style="margin-top:14px;">'+
'<div class="detail-grid__item"><strong>蛋白质目标</strong><div>'+d.protein+' g</div></div>'+
'<div class="detail-grid__item"><strong>脂肪目标</strong><div>'+d.fat+' g</div></div>'+
'<div class="detail-grid__item"><strong>碳水目标</strong><div>'+d.carb+' g</div></div>'+
'</div></div>'+
'<div class="detail-section"><div class="detail-section__title">📋 其他信息</div>'+
'<div class="detail-grid">'+
'<div class="detail-grid__item" style="grid-column:span 2;"><strong>特殊限制项</strong><div style="background:#FFFBE6;padding:8px 12px;border-radius:var(--radius-sm);border:1px solid #FFE58F;">⚠️ '+d.restriction+'</div></div>'+
'</div>'+
'<div class="detail-grid" style="margin-top:14px;">'+
'<div class="detail-grid__item"><strong>制定营养师</strong><div>'+d.nutritionist+'</div></div>'+
'<div class="detail-grid__item"><strong>生效日期</strong><div>'+d.startDate+'</div></div>'+
'<div class="detail-grid__item"><strong>复评日期</strong><div>'+d.reviewDate+'</div></div>'+
'</div></div>';
openModal('modalVipDetail');
}
/* ===== 删除 ===== */
function confirmDelete(code,name){
document.getElementById('deleteMsg').textContent='确认删除用户「'+name+'」('+code+') 的VIP定制标准?';
openModal('modalDelete');
}
/* ===== 批量复评 ===== */
</script>
</body>
</html>
+281
View File
@@ -0,0 +1,281 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>营养管理 - 新增食堂 | 健康长庆原型</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);}
.sidebar-item .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;filter:none;}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:12px;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;}
.breadcrumb a{color:var(--primary);text-decoration:none;}.breadcrumb a:hover{text-decoration:underline;}
.breadcrumb__sep{color:var(--text-placeholder);}
.form-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:24px;margin-bottom:16px;}
.form-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:20px;padding-bottom:12px;border-bottom:1px solid var(--border);}
.form-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px 24px;}
.form-group{display:flex;flex-direction:column;gap:6px;}
.form-group.full{grid-column:1/-1;}
.form-label{font-size:var(--font-sm);color:var(--text-secondary);}
.form-label .required{color:var(--danger);margin-left:2px;}
.form-input,.form-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;width:100%;}
.form-input:focus,.form-select:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.1);}
.form-input:disabled{background:#F5F5F5;color:var(--text-placeholder);cursor:not-allowed;}
.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-hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;}
.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);}
.form-footer{display:flex;align-items:center;gap:12px;padding:16px 24px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
/* 地图区域 */
.map-container{width:100%;height:320px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#E8ECF0;position:relative;overflow:hidden;}
.map-placeholder{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-placeholder);gap:8px;background:linear-gradient(135deg,#e8ecf0 25%,#dde2e8 25%,#dde2e8 50%,#e8ecf0 50%,#e8ecf0 75%,#dde2e8 75%);background-size:20px 20px;}
.map-placeholder__icon{font-size:40px;opacity:0.5;}
.map-placeholder__text{font-size:var(--font-sm);}
/* 模拟地图内容 */
.map-mock{width:100%;height:100%;position:relative;background:#e8edf2;}
.map-mock__grid{position:absolute;inset:0;background-image:linear-gradient(rgba(0,0,0,0.05) 1px,transparent 1px),linear-gradient(90deg,rgba(0,0,0,0.05) 1px,transparent 1px);background-size:40px 40px;}
.map-mock__roads{position:absolute;inset:0;}
.map-mock__road-h{position:absolute;height:3px;background:rgba(255,255,255,0.8);left:0;right:0;}
.map-mock__road-v{position:absolute;width:3px;background:rgba(255,255,255,0.8);top:0;bottom:0;}
.map-mock__pin{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%);z-index:5;}
.map-mock__pin svg{width:36px;height:48px;filter:drop-shadow(0 2px 4px rgba(0,0,0,0.3));}
.map-mock__label{position:absolute;top:calc(50% + 6px);left:50%;transform:translateX(-50%);background:#fff;padding:4px 10px;border-radius:var(--radius-sm);font-size:var(--font-xs);color:var(--text-primary);box-shadow:var(--shadow-sm);white-space:nowrap;z-index:5;}
.map-mock__controls{position:absolute;right:12px;top:12px;display:flex;flex-direction:column;gap:4px;z-index:5;}
.map-mock__ctrl-btn{width:32px;height:32px;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;font-size:16px;cursor:pointer;color:var(--text-primary);}
.map-mock__ctrl-btn:hover{background:var(--primary-light);color:var(--primary);}
.map-mock__search{position:absolute;left:12px;top:12px;display:flex;gap:6px;z-index:5;}
.map-mock__search input{width:220px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-xs);background:#fff;box-shadow:var(--shadow-sm);}
.map-mock__search button{height:32px;padding:0 12px;background:var(--primary);color:#fff;border:none;border-radius:var(--radius-sm);font-size:var(--font-xs);cursor:pointer;}
/* 单选按钮组 */
.radio-group{display:flex;gap:20px;height:36px;align-items:center;}
.radio-item{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);cursor:pointer;}
.radio-item input[type="radio"]{width:16px;height:16px;accent-color:var(--primary);}
</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-canteen.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 active"><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>
<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="breadcrumb">
<a href="din-canteen.html">食堂管理</a>
<span class="breadcrumb__sep">/</span>
<span>新增食堂</span>
</div>
<!-- 基本信息表单 -->
<div class="form-card">
<div class="form-card__title">基本信息</div>
<div class="form-grid">
<div class="form-group">
<label class="form-label">食堂名称<span class="required">*</span></label>
<input class="form-input" placeholder="请输入食堂名称">
</div>
<div class="form-group">
<label class="form-label">食堂编号</label>
<input class="form-input" value="系统自动生成" disabled>
</div>
<div class="form-group">
<label class="form-label">食堂所属单位<span class="required">*</span></label>
<select class="form-select">
<option value="">请选择所属单位</option>
<option>长庆油田总部</option>
<option>采油一厂</option>
<option>采油二厂</option>
<option>采油三厂</option>
<option>采气一厂</option>
<option>采气二厂</option>
<option>油气工艺研究院</option>
</select>
</div>
<div class="form-group">
<label class="form-label">负责人</label>
<input class="form-input" placeholder="请输入负责人姓名">
</div>
<div class="form-group">
<label class="form-label">负责人电话</label>
<input class="form-input" placeholder="请输入联系电话">
</div>
<div class="form-group">
<label class="form-label">使用三减终端</label>
<div class="radio-group">
<label class="radio-item"><input type="radio" name="terminal" value="yes" checked></label>
<label class="radio-item"><input type="radio" name="terminal" value="no"></label>
</div>
</div>
</div>
</div>
<!-- 地址信息 -->
<div class="form-card">
<div class="form-card__title">地址信息</div>
<!-- 地图选点 -->
<div class="map-container">
<div class="map-mock">
<!-- 网格背景 -->
<div class="map-mock__grid"></div>
<!-- 模拟道路 -->
<div class="map-mock__roads">
<div class="map-mock__road-h" style="top:35%"></div>
<div class="map-mock__road-h" style="top:55%"></div>
<div class="map-mock__road-h" style="top:75%"></div>
<div class="map-mock__road-v" style="left:25%"></div>
<div class="map-mock__road-v" style="left:50%"></div>
<div class="map-mock__road-v" style="left:72%"></div>
</div>
<!-- 搜索框 -->
<div class="map-mock__search">
<input placeholder="搜索地址或地名">
<button>搜索</button>
</div>
<!-- 缩放控件 -->
<div class="map-mock__controls">
<div class="map-mock__ctrl-btn">+</div>
<div class="map-mock__ctrl-btn"></div>
</div>
<!-- 标记点 -->
<div class="map-mock__pin">
<svg viewBox="0 0 36 48" fill="none">
<path d="M18 0C8.06 0 0 8.06 0 18c0 13.5 18 30 18 30s18-16.5 18-30C36 8.06 27.94 0 18 0z" fill="#FF4D4F"/>
<circle cx="18" cy="18" r="8" fill="#fff"/>
<circle cx="18" cy="18" r="4" fill="#FF4D4F"/>
</svg>
</div>
<div class="map-mock__label">📍 点击地图选择位置</div>
</div>
</div>
<div style="margin-top:12px;">
<div class="form-group">
<label class="form-label">详细地址</label>
<input class="form-input" id="addressInput" placeholder="地图选点后自动填入,也可手动输入详细地址">
<span class="form-hint">点击地图选点后,地址信息将自动写入此输入框</span>
</div>
</div>
</div>
<!-- 底部按钮 -->
<div class="form-footer">
<button class="btn btn-primary" onclick="handleSave()">保存</button>
<a href="din-canteen.html" class="btn btn-default">取消</a>
</div>
</main>
<script>
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
/* 模拟地图选点交互 */
document.querySelector('.map-mock').addEventListener('click',function(e){
var rect=this.getBoundingClientRect();
var x=e.clientX-rect.left;
var y=e.clientY-rect.top;
/* 移动标记点 */
var pin=this.querySelector('.map-mock__pin');
var label=this.querySelector('.map-mock__label');
pin.style.left=x+'px';
pin.style.top=y+'px';
label.style.left=x+'px';
label.style.top=(y+6)+'px';
label.textContent='📍 已选择位置';
/* 模拟填入地址 */
document.getElementById('addressInput').value='陕西省西安市未央区凤城八路长庆油田总部基地';
});
function handleSave(){
alert('【交互说明】点击保存后,校验必填项,通过后保存并返回食堂列表页');
}
</script>
</body>
</html>
+267
View File
@@ -0,0 +1,267 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>营养管理 - 餐线管理 | 健康长庆原型</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);}
.sidebar-item .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;filter:none;}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:12px;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;}
.breadcrumb a{color:var(--primary);text-decoration:none;}.breadcrumb a:hover{text-decoration:underline;}
.breadcrumb__sep{color:var(--text-placeholder);}
/* 食堂信息卡片 */
.info-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:20px 24px;margin-bottom:20px;}
.info-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:16px;display:flex;align-items:center;gap:8px;}
.info-card__title .icon{font-size:20px;}
.info-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px 24px;}
.info-item{display:flex;flex-direction:column;gap:4px;}
.info-item__label{font-size:var(--font-xs);color:var(--text-secondary);}
.info-item__value{font-size:var(--font-sm);color:var(--text-primary);font-weight:500;}
/* 表格 */
.btn{height:32px;padding:0 14px;border:none;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;}
.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{border-color:var(--primary);color:var(--primary);}
.action-bar{display:flex;align-items:center;gap:8px;margin-bottom:16px;}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--border);}
.table-card__title{font-size:var(--font-base);font-weight:600;}
table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
th{background:#FAFAFA;text-align:left;padding:10px 14px;font-weight:600;white-space:nowrap;border-bottom:1px solid var(--border);}
td{padding:10px 14px;border-bottom:1px solid #f0f0f0;white-space:nowrap;}
tr:hover td{background:#FAFAFA;}
.link{color:var(--primary);cursor:pointer;text-decoration:none;margin-right:8px;font-size:var(--font-sm);}
.link:hover{text-decoration:underline;}
.link-danger{color:var(--danger);}.link-danger:hover{color:#ff7875;}
.pagination{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 20px;font-size:var(--font-xs);color:var(--text-secondary);}
.page-btn{width:28px;height:28px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-xs);}
.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.tag{display:inline-block;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
.tag-green{background:#F6FFED;color:#52C41A;}
/* 弹窗 */
.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:1000;align-items:center;justify-content:center;}
.modal-mask.show{display:flex;}
.modal{background:#fff;border-radius:var(--radius-md);width:480px;box-shadow:var(--shadow-md);}
.modal__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.modal__title{font-size:var(--font-base);font-weight:600;}
.modal__close{font-size:22px;cursor:pointer;color:var(--text-secondary);line-height:1;}.modal__close:hover{color:var(--text-primary);}
.modal__body{padding:24px;}
.modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 24px;border-top:1px solid var(--border);}
.form-row{margin-bottom:16px;}
.form-row:last-child{margin-bottom:0;}
.form-label{display:block;font-size:var(--font-sm);color:var(--text-primary);margin-bottom:6px;}
.form-label em{color:var(--danger);font-style:normal;}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);}
.form-input:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.1);}
</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-canteen.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 active"><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>
<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="breadcrumb">
<a href="din-canteen.html">食堂管理</a>
<span class="breadcrumb__sep">/</span>
<span>餐线管理</span>
</div>
<!-- 食堂基本信息 -->
<div class="info-card">
<div class="info-card__title"><span class="icon">🏢</span> 总部一号食堂</div>
<div class="info-grid">
<div class="info-item">
<div class="info-item__label">食堂编号</div>
<div class="info-item__value">ST-2026-001</div>
</div>
<div class="info-item">
<div class="info-item__label">所属单位</div>
<div class="info-item__value">长庆油田总部</div>
</div>
<div class="info-item">
<div class="info-item__label">负责人</div>
<div class="info-item__value">张建国</div>
</div>
<div class="info-item">
<div class="info-item__label">使用三减终端</div>
<div class="info-item__value"><span class="tag tag-green"></span></div>
</div>
<div class="info-item" style="grid-column:span 2;">
<div class="info-item__label">详细地址</div>
<div class="info-item__value">陕西省西安市未央区凤城八路总部基地A区</div>
</div>
<div class="info-item">
<div class="info-item__label">负责人电话</div>
<div class="info-item__value">138****1001</div>
</div>
</div>
</div>
<!-- 餐线列表 -->
<div class="action-bar">
<button class="btn btn-primary" onclick="openModal('add')"> 新增餐线</button>
</div>
<div class="table-card">
<div class="table-card__header">
<div class="table-card__title">餐线列表</div>
<span style="font-size:var(--font-xs);color:var(--text-secondary);">共 3 条</span>
</div>
<div style="overflow-x:auto;">
<table>
<thead><tr><th>餐线名称</th><th>设备数量</th><th>操作</th></tr></thead>
<tbody>
<tr>
<td>A餐线(自选餐)</td>
<td>6 台</td>
<td><span class="link" onclick="openModal('edit','A餐线(自选餐)')">编辑</span><span class="link link-danger">删除</span></td>
</tr>
<tr>
<td>B餐线(营养点餐)</td>
<td>4 台</td>
<td><span class="link" onclick="openModal('edit','B餐线(营养点餐)')">编辑</span><span class="link link-danger">删除</span></td>
</tr>
<tr>
<td>C餐线(清真)</td>
<td>2 台</td>
<td><span class="link" onclick="openModal('edit','C餐线(清真)')">编辑</span><span class="link link-danger">删除</span></td>
</tr>
</tbody>
</table>
</div>
<div class="pagination"><span>显示 3 条,共 3 条</span><button class="page-btn active">1</button></div>
</div>
</main>
<!-- 新增/编辑餐线弹窗 -->
<div class="modal-mask" id="lineModal" onclick="closeModal(event)">
<div class="modal" onclick="event.stopPropagation()">
<div class="modal__header"><span class="modal__title" id="modalTitle">新增餐线</span><span class="modal__close" onclick="closeModal()">&times;</span></div>
<div class="modal__body">
<div class="form-row">
<label class="form-label"><em>*</em> 餐线名称</label>
<input class="form-input" id="lineName" placeholder="请输入餐线名称">
</div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="closeModal()">取消</button>
<button class="btn btn-primary">确定</button>
</div>
</div>
</div>
<script>
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
function openModal(mode, name){
var modal=document.getElementById('lineModal');
var title=document.getElementById('modalTitle');
var input=document.getElementById('lineName');
if(mode==='edit'){
title.textContent='编辑餐线';
input.value=name||'';
}else{
title.textContent='新增餐线';
input.value='';
}
modal.classList.add('show');
}
function closeModal(e){
if(e&&e.target!==e.currentTarget)return;
document.getElementById('lineModal').classList.remove('show');
}
</script>
</body>
</html>
+278
View File
@@ -0,0 +1,278 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>营养管理 - 食堂管理 | 健康长庆原型</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);}
.sidebar-item .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;filter:none;}
.sidebar-subgroup__header{display:flex;align-items:center;gap:6px;padding:10px 24px;font-size:var(--font-sm);color:rgba(255,255,255,0.45);cursor:pointer;user-select:none;}
.sidebar-subgroup__header .arrow{width:10px;height:10px;stroke:currentColor;transition:transform .2s;}
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
.sidebar-subgroup__body{display:none;padding-left:12px;}
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
.filter-bar{display:flex;align-items:flex-end;gap:12px;background:#fff;padding:16px 20px;border-radius:var(--radius-md);margin-bottom:16px;box-shadow:var(--shadow-sm);flex-wrap:wrap;}
.filter-item{display:flex;flex-direction:column;gap:4px;}
.filter-item label{font-size:var(--font-xs);color:var(--text-secondary);}
.filter-input,.filter-select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);min-width:160px;}
.btn{height:32px;padding:0 14px;border:none;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;display:inline-flex;align-items:center;gap:4px;white-space:nowrap;}
.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{border-color:var(--primary);color:var(--primary);}
.btn-success{background:var(--success);color:#fff;}.btn-success:hover{background:#73d13d;}
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.table-card__header{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--border);}
.table-card__title{font-size:var(--font-base);font-weight:600;}
.table-card__actions{display:flex;gap:8px;}
.table-card__content{overflow-x:auto;}
table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
th{background:#FAFAFA;text-align:left;padding:10px 14px;font-weight:600;white-space:nowrap;border-bottom:1px solid var(--border);}
td{padding:10px 14px;border-bottom:1px solid #f0f0f0;white-space:nowrap;}
tr:hover td{background:#FAFAFA;}
.tag{display:inline-block;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:#E6F7FF;color:#1890FF;}.tag-green{background:#F6FFED;color:#52C41A;}.tag-gray{background:#F5F5F5;color:#999;}
.link{color:var(--primary);cursor:pointer;text-decoration:none;margin-right:8px;font-size:var(--font-sm);}
.link:hover{text-decoration:underline;}
.link-danger{color:var(--danger);}.link-danger:hover{color:#ff7875;}
.pagination{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 20px;font-size:var(--font-xs);color:var(--text-secondary);}
.page-btn{width:28px;height:28px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-xs);}
.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.action-bar{display:flex;align-items:center;gap:8px;margin-bottom: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;}
/* 右侧抽屉 */
.drawer-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:1000;}
.drawer-mask.show{display:block;}
.drawer{position:fixed;top:0;right:-520px;bottom:0;width:520px;background:#fff;z-index:1001;transition:right .3s ease;box-shadow:-4px 0 20px rgba(0,0,0,0.15);display:flex;flex-direction:column;}
.drawer.show{right:0;}
.drawer__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);flex-shrink:0;}
.drawer__title{font-size:var(--font-base);font-weight:600;}
.drawer__close{font-size:22px;cursor:pointer;color:var(--text-secondary);line-height:1;}.drawer__close:hover{color:var(--text-primary);}
.drawer__body{padding:24px;overflow-y:auto;flex:1;}
.detail-row{display:flex;margin-bottom:18px;}
.detail-label{width:110px;flex-shrink:0;font-size:var(--font-sm);color:var(--text-secondary);}
.detail-value{font-size:var(--font-sm);color:var(--text-primary);flex:1;}
</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-canteen.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 active"><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>
<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="filter-bar">
<div class="filter-item"><label>食堂名称</label><input class="filter-input" placeholder="请输入食堂名称"></div>
<div class="filter-item"><label>食堂编号</label><input class="filter-input" placeholder="请输入食堂编号"></div>
<div class="filter-item"><label>所属单位</label>
<select class="filter-select">
<option value="">全部</option>
<option>长庆油田总部</option>
<option>采油一厂</option>
<option>采油二厂</option>
<option>采油三厂</option>
<option>采气一厂</option>
</select>
</div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<!-- 操作按钮 -->
<div class="action-bar">
<a href="din-canteen-add.html" class="btn btn-primary" style="text-decoration:none;"> 新增食堂</a>
<button class="btn btn-success">📥 导出列表数据</button>
<button class="btn btn-default">📋 查看导出任务</button>
</div>
<!-- 表格 -->
<div class="table-card">
<div class="table-card__header">
<div class="table-card__title">食堂列表</div>
<div class="table-card__actions"><span style="font-size:var(--font-xs);color:var(--text-secondary);">共 5 条</span></div>
</div>
<div class="table-card__content">
<table>
<thead><tr><th>食堂名称</th><th>食堂编号</th><th>所属单位</th><th>详细地址</th><th>操作</th></tr></thead>
<tbody>
<tr>
<td>总部一号食堂</td><td>ST-2026-001</td><td>长庆油田总部</td><td>陕西省西安市未央区凤城八路总部基地A区</td>
<td class="td-actions">
<span class="link" onclick="openDrawer(0)">详情</span>
<a class="link" href="din-canteen-line.html?id=1&name=总部一号食堂">餐线管理</a>
<a class="link" href="din-canteen-add.html?id=1">编辑</a>
<span class="link link-danger">删除</span>
</td>
</tr>
<tr>
<td>总部二号食堂</td><td>ST-2026-002</td><td>长庆油田总部</td><td>陕西省西安市未央区凤城八路总部基地B区</td>
<td class="td-actions">
<span class="link" onclick="openDrawer(1)">详情</span>
<a class="link" href="din-canteen-line.html?id=2&name=总部二号食堂">餐线管理</a>
<a class="link" href="din-canteen-add.html?id=2">编辑</a>
<span class="link link-danger">删除</span>
</td>
</tr>
<tr>
<td>采油一厂职工食堂</td><td>ST-2026-003</td><td>采油一厂</td><td>陕西省延安市安塞区采油一厂生活区</td>
<td class="td-actions">
<span class="link" onclick="openDrawer(2)">详情</span>
<a class="link" href="din-canteen-line.html?id=3&name=采油一厂职工食堂">餐线管理</a>
<a class="link" href="din-canteen-add.html?id=3">编辑</a>
<span class="link link-danger">删除</span>
</td>
</tr>
<tr>
<td>采油二厂综合食堂</td><td>ST-2026-004</td><td>采油二厂</td><td>甘肃省庆阳市庆城县采油二厂驻地</td>
<td class="td-actions">
<span class="link" onclick="openDrawer(3)">详情</span>
<a class="link" href="din-canteen-line.html?id=4&name=采油二厂综合食堂">餐线管理</a>
<a class="link" href="din-canteen-add.html?id=4">编辑</a>
<span class="link link-danger">删除</span>
</td>
</tr>
<tr>
<td>采气一厂营养餐厅</td><td>ST-2026-005</td><td>采气一厂</td><td>陕西省榆林市靖边县采气一厂作业区</td>
<td class="td-actions">
<span class="link" onclick="openDrawer(4)">详情</span>
<a class="link" href="din-canteen-line.html?id=5&name=采气一厂营养餐厅">餐线管理</a>
<a class="link" href="din-canteen-add.html?id=5">编辑</a>
<span class="link link-danger">删除</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination"><span>显示 5 条,共 5 条</span><button class="page-btn active">1</button></div>
</div>
</main>
<!-- 详情抽屉 -->
<div class="drawer-mask" id="drawerMask" onclick="closeDrawer()"></div>
<div class="drawer" id="drawer">
<div class="drawer__header">
<span class="drawer__title">食堂详情</span>
<span class="drawer__close" onclick="closeDrawer()">&times;</span>
</div>
<div class="drawer__body" id="drawerBody"></div>
</div>
<script>
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
/* 详情数据 */
var canteens=[
{name:'总部一号食堂',code:'ST-2026-001',unit:'长庆油田总部',address:'陕西省西安市未央区凤城八路总部基地A区',leader:'张建国',phone:'138****1001',terminal:'是'},
{name:'总部二号食堂',code:'ST-2026-002',unit:'长庆油田总部',address:'陕西省西安市未央区凤城八路总部基地B区',leader:'李红梅',phone:'139****2002',terminal:'是'},
{name:'采油一厂职工食堂',code:'ST-2026-003',unit:'采油一厂',address:'陕西省延安市安塞区采油一厂生活区',leader:'王志强',phone:'137****3003',terminal:'否'},
{name:'采油二厂综合食堂',code:'ST-2026-004',unit:'采油二厂',address:'甘肃省庆阳市庆城县采油二厂驻地',leader:'刘文静',phone:'136****4004',terminal:'是'},
{name:'采气一厂营养餐厅',code:'ST-2026-005',unit:'采气一厂',address:'陕西省榆林市靖边县采气一厂作业区',leader:'陈伟',phone:'135****5005',terminal:'否'}
];
function openDrawer(idx){
var d=canteens[idx];
document.getElementById('drawerBody').innerHTML=
'<div class="detail-row"><div class="detail-label">食堂名称</div><div class="detail-value">'+d.name+'</div></div>'+
'<div class="detail-row"><div class="detail-label">食堂编号</div><div class="detail-value">'+d.code+'</div></div>'+
'<div class="detail-row"><div class="detail-label">所属单位</div><div class="detail-value">'+d.unit+'</div></div>'+
'<div class="detail-row"><div class="detail-label">详细地址</div><div class="detail-value">'+d.address+'</div></div>'+
'<div class="detail-row"><div class="detail-label">负责人</div><div class="detail-value">'+d.leader+'</div></div>'+
'<div class="detail-row"><div class="detail-label">负责人电话</div><div class="detail-value">'+d.phone+'</div></div>'+
'<div class="detail-row"><div class="detail-label">使用三减终端</div><div class="detail-value"><span class="tag '+(d.terminal==='是'?'tag-green':'tag-gray')+'">'+d.terminal+'</span></div></div>';
document.getElementById('drawerMask').classList.add('show');
document.getElementById('drawer').classList.add('show');
}
function closeDrawer(){
document.getElementById('drawerMask').classList.remove('show');
document.getElementById('drawer').classList.remove('show');
}
</script>
</body>
</html>
@@ -157,6 +157,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
@@ -148,6 +148,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+1
View File
@@ -321,6 +321,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+23 -31
View File
@@ -117,6 +117,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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 active"><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>
@@ -164,9 +165,8 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item"><label>字典标签</label><input class="filter-input" placeholder="请输入字典标签"></div>
<div class="filter-item"><label>字典值</label><input class="filter-input" placeholder="请输入字典值"></div>
<div class="filter-item"><label>状态</label><select class="filter-select"><option>全部</option><option>启用</option><option>停用</option></select></div>
<div class="filter-item"><label>名称</label><input class="filter-input" placeholder="请输入名称"></div>
<div class="filter-item"><label>状态</label><select class="filter-select"><option value="">请选择状态</option><option>启用</option><option>停用</option></select></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
@@ -179,19 +179,19 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<div class="table-card__content">
<table>
<thead><tr><th>字典编码</th><th>字典标签</th><th>字典</th><th>排序</th><th>状态</th><th>备注</th><th>修改人</th><th>修改时间</th><th>操作</th></tr></thead>
<thead><tr><th>序号</th><th>名称</th><th>数据</th><th>状态</th><th>描述</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>
<tr><td>cook_method_01</td><td></td><td>stir_fry</td><td>1</td><td><span class="tag tag-green">启用</span></td><td>大火快炒</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_02</td><td></td><td>steam</td><td>2</td><td><span class="tag tag-green">启用</span></td><td>隔水蒸制</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_03</td><td></td><td>stew</td><td>3</td><td><span class="tag tag-green">启用</span></td><td>慢火炖煮</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_04</td><td></td><td>boil</td><td>4</td><td><span class="tag tag-green">启用</span></td><td>水煮</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_05</td><td>凉拌</td><td>cold_mix</td><td>5</td><td><span class="tag tag-green">启用</span></td><td>不加热</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_06</td><td></td><td>deep_fry</td><td>6</td><td><span class="tag tag-green">启用</span></td><td>油炸</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_07</td><td></td><td>roast</td><td>7</td><td><span class="tag tag-green">启用</span></td><td>烤箱/明火</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_08</td><td></td><td>pan_fry</td><td>8</td><td><span class="tag tag-green">启用</span></td><td>少油煎制</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_09</td><td></td><td>braise</td><td>9</td><td><span class="tag tag-green">启用</span></td><td>加盖焖制</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_10</td><td></td><td>marinade</td><td>10</td><td><span class="tag tag-green">启用</span></td><td>卤汁浸煮</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>cook_method_11</td><td></td><td>blanch</td><td>11</td><td><span class="tag tag-gray">停用</span></td><td>沸水汆烫</td><td>王运营</td><td>2026-03-20</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>1</td><td></td><td>1</td><td><span class="tag tag-green">启用</span></td><td>大火快炒</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>2</td><td></td><td>2</td><td><span class="tag tag-green">启用</span></td><td>隔水蒸制</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>3</td><td></td><td>3</td><td><span class="tag tag-green">启用</span></td><td>慢火炖煮</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>4</td><td></td><td>4</td><td><span class="tag tag-green">启用</span></td><td>水煮</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>5</td><td>凉拌</td><td>5</td><td><span class="tag tag-green">启用</span></td><td>不加热</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>6</td><td></td><td>6</td><td><span class="tag tag-green">启用</span></td><td>油炸</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>7</td><td></td><td>7</td><td><span class="tag tag-green">启用</span></td><td>烤箱/明火</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>8</td><td></td><td>8</td><td><span class="tag tag-green">启用</span></td><td>少油煎制</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>9</td><td></td><td>9</td><td><span class="tag tag-green">启用</span></td><td>加盖焖制</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>10</td><td></td><td>10</td><td><span class="tag tag-green">启用</span></td><td>卤汁浸煮</td><td>2026-01-01 10:00:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
<tr><td>11</td><td></td><td>11</td><td><span class="tag tag-gray">停用</span></td><td>沸水汆烫</td><td>2026-03-20 14:30:00</td><td class="td-actions"><span class="link" onclick="openDialog('edit',this)">编辑</span> <span class="link link-danger">删除</span></td></tr>
</tbody>
</table>
</div>
@@ -204,12 +204,10 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<div class="modal" onclick="event.stopPropagation()">
<div class="modal__header"><span class="modal__title" id="modalTitle">新增字典项</span><span class="modal__close" onclick="closeDialog()">&times;</span></div>
<div class="modal__body">
<div class="form-row"><label class="form-label">字典编码 <em>*</em></label><input class="form-input" id="formCode" placeholder="请输入字典编码"></div>
<div class="form-row"><label class="form-label">字典标签 <em>*</em></label><input class="form-input" id="formLabel" placeholder="请输入字典标签(显示名称)"></div>
<div class="form-row"><label class="form-label">字典值 <em>*</em></label><input class="form-input" id="formValue" placeholder="请输入字典值(英文标识)"></div>
<div class="form-row"><label class="form-label">排序 <em>*</em></label><input class="form-input" id="formSort" type="number" placeholder="请输入排序号"></div>
<div class="form-row"><label class="form-label">名称 <em>*</em></label><input class="form-input" id="formName" placeholder="请输入名称"></div>
<div class="form-row"><label class="form-label">数据值 <em>*</em></label><input class="form-input" id="formValue" type="number" placeholder="请输入数据值"></div>
<div class="form-row"><label class="form-label">状态</label><select class="form-input" id="formStatus"><option value="1">启用</option><option value="0">停用</option></select></div>
<div class="form-row"><label class="form-label">备注</label><textarea class="form-input form-textarea" id="formRemark" placeholder="请输入备注信息"></textarea></div>
<div class="form-row"><label class="form-label">描述</label><textarea class="form-input form-textarea" id="formDesc" placeholder="请输入描述信息"></textarea></div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeDialog()">取消</button><button class="btn btn-primary">确定</button></div>
</div>
@@ -236,22 +234,16 @@ function openDialog(mode, el){
title.textContent='编辑字典项';
var row=el.closest('tr');
var cells=row.querySelectorAll('td');
document.getElementById('formCode').value=cells[0].textContent;
document.getElementById('formCode').disabled=true;
document.getElementById('formLabel').value=cells[1].textContent;
document.getElementById('formName').value=cells[1].textContent;
document.getElementById('formValue').value=cells[2].textContent;
document.getElementById('formSort').value=cells[3].textContent;
document.getElementById('formStatus').value=cells[4].textContent.trim()==='启用'?'1':'0';
document.getElementById('formRemark').value=cells[5].textContent;
document.getElementById('formStatus').value=cells[3].textContent.trim()==='启用'?'1':'0';
document.getElementById('formDesc').value=cells[4].textContent;
}else{
title.textContent='新增字典项';
document.getElementById('formCode').value='';
document.getElementById('formCode').disabled=false;
document.getElementById('formLabel').value='';
document.getElementById('formName').value='';
document.getElementById('formValue').value='';
document.getElementById('formSort').value='';
document.getElementById('formStatus').value='1';
document.getElementById('formRemark').value='';
document.getElementById('formDesc').value='';
}
modal.classList.add('show');
}
+63 -60
View File
@@ -121,6 +121,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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 active"><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>
@@ -169,31 +170,31 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- Tab 切换栏 -->
<div class="page-tabs">
<div class="page-tab active" onclick="switchTab(0)">🍳 烹饪/工艺</div>
<div class="page-tab" onclick="switchTab(1)">👅 感官</div>
<div class="page-tab" onclick="switchTab(2)">🥗 营养</div>
<div class="page-tab" onclick="switchTab(3)">👥 适配</div>
<div class="page-tab" onclick="switchTab(4)">📦 仓储/规格</div>
<div class="page-tab" onclick="switchTab(5)">📋 业务</div>
<div class="page-tab active" onclick="switchTab(0)">烹饪/工艺</div>
<div class="page-tab" onclick="switchTab(1)">感官</div>
<div class="page-tab" onclick="switchTab(2)">营养</div>
<div class="page-tab" onclick="switchTab(3)">适配</div>
<div class="page-tab" onclick="switchTab(4)">仓储/规格</div>
<div class="page-tab" onclick="switchTab(5)">业务</div>
</div>
<!-- Tab1: 烹饪/工艺 -->
<div class="tab-panel active">
<div class="filter-bar">
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入字典名"></div>
<div class="filter-item"><label>系统内置</label><select class="filter-select"><option>全部</option><option></option><option></option></select></div>
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入"></div>
<div class="filter-item"><label>字典编码</label><input class="filter-input" placeholder="请输入"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<div class="action-bar"><button class="btn btn-primary" onclick="openDialog('add')"> 新增</button></div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">烹饪/工艺</div></div>
<div class="table-card__content"><table><thead><tr><th>字典编码</th><th>字典</th><th>系统内置</th><th>备注</th><th>修改人</th><th>修改时间</th><th>操作</th></tr></thead>
<div class="table-card__content"><table><thead><tr><th>字典名称</th><th>字典编码</th><th>字典分类</th><th>描述</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>
<tr><td>cook_method</td><td>主烹饪法</td><td><span class="tag tag-blue"></span></td><td>炒蒸炖煮等烹饪方式</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=cook_method&name=主烹饪法">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>fire_level</td><td>火候</td><td><span class="tag tag-blue"></span></td><td>大火/中火/小火</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=fire_level&name=火候">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>process_type</td><td>加工类型</td><td><span class="tag tag-blue"></span></td><td>清洗/去皮/切割</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=process_type&name=加工类型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>process_spec</td><td>加工规格</td><td><span class="tag tag-blue"></span></td><td>整棵/段/丁等规格</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=process_spec&name=加工规格">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>主烹饪法</td><td>cook_method</td><td>烹饪/工艺</td><td>炒蒸炖煮等烹饪方式</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=cook_method&name=主烹饪法">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>火候</td><td>fire_level</td><td>烹饪/工艺</td><td>大火/中火/小火</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=fire_level&name=火候">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>加工类型</td><td>process_type</td><td>烹饪/工艺</td><td>清洗/去皮/切割</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=process_type&name=加工类型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>加工规格</td><td>process_spec</td><td>烹饪/工艺</td><td>整棵/段/丁等规格</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=process_spec&name=加工规格">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><span>显示 4 条,共 4 条</span><button class="page-btn active">1</button></div>
</div>
@@ -202,22 +203,22 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- Tab2: 感官 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入字典名"></div>
<div class="filter-item"><label>系统内置</label><select class="filter-select"><option>全部</option><option></option><option></option></select></div>
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入"></div>
<div class="filter-item"><label>字典编码</label><input class="filter-input" placeholder="请输入"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<div class="action-bar"><button class="btn btn-primary" onclick="openDialog('add')"> 新增</button></div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">感官</div></div>
<div class="table-card__content"><table><thead><tr><th>字典编码</th><th>字典</th><th>系统内置</th><th>备注</th><th>修改人</th><th>修改时间</th><th>操作</th></tr></thead>
<div class="table-card__content"><table><thead><tr><th>字典名称</th><th>字典编码</th><th>字典分类</th><th>描述</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>
<tr><td>taste_type</td><td>主味型</td><td><span class="tag tag-blue"></span></td><td>淡味/鲜味/甜味等</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=taste_type&name=主味型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>taste_level</td><td>味道强度</td><td><span class="tag tag-blue"></span></td><td>弱/中/强</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=taste_level&name=味道强度">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>color_type</td><td>主色系</td><td><span class="tag tag-blue"></span></td><td>白色/金黄/绿色等</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=color_type&name=主色系">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>sensory_grade</td><td>感官评级</td><td><span class="tag tag-blue"></span></td><td>优/良/中</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=sensory_grade&name=感官评级">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>texture_type</td><td>主质地</td><td><span class="tag tag-blue"></span></td><td>软烂/嫩滑/酥脆</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=texture_type&name=主质地">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>chew_level</td><td>咀嚼难度</td><td><span class="tag tag-blue"></span></td><td>易/中/难</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=chew_level&name=咀嚼难度">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>主味型</td><td>taste_type</td><td>感官</td><td>淡味/鲜味/甜味等</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=taste_type&name=主味型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>味道强度</td><td>taste_level</td><td>感官</td><td>弱/中/强</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=taste_level&name=味道强度">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>主色系</td><td>color_type</td><td>感官</td><td>白色/金黄/绿色等</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=color_type&name=主色系">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>感官评级</td><td>sensory_grade</td><td>感官</td><td>优/良/中</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=sensory_grade&name=感官评级">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>主质地</td><td>texture_type</td><td>感官</td><td>软烂/嫩滑/酥脆</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=texture_type&name=主质地">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>咀嚼难度</td><td>chew_level</td><td>感官</td><td>易/中/难</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=chew_level&name=咀嚼难度">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><span>显示 6 条,共 6 条</span><button class="page-btn active">1</button></div>
</div>
@@ -226,20 +227,20 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- Tab3: 营养 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入字典名"></div>
<div class="filter-item"><label>系统内置</label><select class="filter-select"><option>全部</option><option></option><option></option></select></div>
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入"></div>
<div class="filter-item"><label>字典编码</label><input class="filter-input" placeholder="请输入"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<div class="action-bar"><button class="btn btn-primary" onclick="openDialog('add')"> 新增</button></div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">营养</div></div>
<div class="table-card__content"><table><thead><tr><th>字典编码</th><th>字典</th><th>系统内置</th><th>备注</th><th>修改人</th><th>修改时间</th><th>操作</th></tr></thead>
<div class="table-card__content"><table><thead><tr><th>字典名称</th><th>字典编码</th><th>字典分类</th><th>描述</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>
<tr><td>diet_pyramid</td><td>膳食宝塔分类</td><td><span class="tag tag-blue"></span></td><td>膳食宝塔六层分类</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=diet_pyramid&name=膳食宝塔分类">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>food_category</td><td>食材分类</td><td><span class="tag tag-blue"></span></td><td>谷薯/畜肉/禽肉等</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=food_category&name=食材分类">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>data_source</td><td>数据来源</td><td><span class="tag tag-blue"></span></td><td>食物成分表/实测等</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=data_source&name=数据来源">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>test_item</td><td>检测项目</td><td><span class="tag tag-blue"></span></td><td>农残/重金属/微生物</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=test_item&name=检测项目">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>膳食宝塔分类</td><td>diet_pyramid</td><td>营养</td><td>膳食宝塔六层分类</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=diet_pyramid&name=膳食宝塔分类">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>食材分类</td><td>food_category</td><td>营养</td><td>谷薯/畜肉/禽肉等</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=food_category&name=食材分类">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>数据来源</td><td>data_source</td><td>营养</td><td>食物成分表/实测等</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=data_source&name=数据来源">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>检测项目</td><td>test_item</td><td>营养</td><td>农残/重金属/微生物</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=test_item&name=检测项目">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><span>显示 4 条,共 4 条</span><button class="page-btn active">1</button></div>
</div>
@@ -248,20 +249,20 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- Tab4: 适配 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入字典名"></div>
<div class="filter-item"><label>系统内置</label><select class="filter-select"><option>全部</option><option></option><option></option></select></div>
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入"></div>
<div class="filter-item"><label>字典编码</label><input class="filter-input" placeholder="请输入"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<div class="action-bar"><button class="btn btn-primary" onclick="openDialog('add')"> 新增</button></div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">适配</div></div>
<div class="table-card__content"><table><thead><tr><th>字典编码</th><th>字典</th><th>系统内置</th><th>备注</th><th>修改人</th><th>修改时间</th><th>操作</th></tr></thead>
<div class="table-card__content"><table><thead><tr><th>字典名称</th><th>字典编码</th><th>字典分类</th><th>描述</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>
<tr><td>crowd_type</td><td>适配人群</td><td><span class="tag tag-blue"></span></td><td>全人群/老年人/孕妇等</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=crowd_type&name=适配人群">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>adapt_level</td><td>适配等级</td><td><span class="tag tag-blue"></span></td><td>推荐/适量/慎食</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=adapt_level&name=适配等级">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>person_type</td><td>人员类别</td><td><span class="tag tag-blue"></span></td><td>会员/非会员/员工等</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=person_type&name=人员类别">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>activity_level</td><td>活动水平</td><td><span class="tag tag-blue"></span></td><td>久坐/轻度/中度/重度</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=activity_level&name=活动水平">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>适配人群</td><td>crowd_type</td><td>适配</td><td>全人群/老年人/孕妇等</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=crowd_type&name=适配人群">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>适配等级</td><td>adapt_level</td><td>适配</td><td>推荐/适量/慎食</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=adapt_level&name=适配等级">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>人员类别</td><td>person_type</td><td>适配</td><td>会员/非会员/员工等</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=person_type&name=人员类别">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>活动水平</td><td>activity_level</td><td>适配</td><td>久坐/轻度/中度/重度</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=activity_level&name=活动水平">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><span>显示 4 条,共 4 条</span><button class="page-btn active">1</button></div>
</div>
@@ -270,19 +271,19 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- Tab5: 仓储/规格 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入字典名"></div>
<div class="filter-item"><label>系统内置</label><select class="filter-select"><option>全部</option><option></option><option></option></select></div>
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入"></div>
<div class="filter-item"><label>字典编码</label><input class="filter-input" placeholder="请输入"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<div class="action-bar"><button class="btn btn-primary" onclick="openDialog('add')"> 新增</button></div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">仓储/规格</div></div>
<div class="table-card__content"><table><thead><tr><th>字典编码</th><th>字典</th><th>系统内置</th><th>备注</th><th>修改人</th><th>修改时间</th><th>操作</th></tr></thead>
<div class="table-card__content"><table><thead><tr><th>字典名称</th><th>字典编码</th><th>字典分类</th><th>描述</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>
<tr><td>stock_spec</td><td>入库规格</td><td><span class="tag tag-blue"></span></td><td>25kg/件等包装规格</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=stock_spec&name=入库规格">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>temp_zone</td><td>温区类型</td><td><span class="tag tag-blue"></span></td><td>常温/冷藏/冷冻</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=temp_zone&name=温区类型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>store_category</td><td>适存品类</td><td><span class="tag tag-blue"></span></td><td>叶菜/根茎/肉类等</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=store_category&name=适存品类">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>入库规格</td><td>stock_spec</td><td>仓储/规格</td><td>25kg/件等包装规格</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=stock_spec&name=入库规格">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>温区类型</td><td>temp_zone</td><td>仓储/规格</td><td>常温/冷藏/冷冻</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=temp_zone&name=温区类型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>适存品类</td><td>store_category</td><td>仓储/规格</td><td>叶菜/根茎/肉类等</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=store_category&name=适存品类">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><span>显示 3 条,共 3 条</span><button class="page-btn active">1</button></div>
</div>
@@ -291,22 +292,22 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- Tab6: 业务 -->
<div class="tab-panel">
<div class="filter-bar">
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入字典名"></div>
<div class="filter-item"><label>系统内置</label><select class="filter-select"><option>全部</option><option></option><option></option></select></div>
<div class="filter-item"><label>字典名</label><input class="filter-input" placeholder="请输入"></div>
<div class="filter-item"><label>字典编码</label><input class="filter-input" placeholder="请输入"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
<div class="action-bar"><button class="btn btn-primary" onclick="openDialog('add')"> 新增</button></div>
<div class="table-card">
<div class="table-card__header"><div class="table-card__title">业务</div></div>
<div class="table-card__content"><table><thead><tr><th>字典编码</th><th>字典</th><th>系统内置</th><th>备注</th><th>修改人</th><th>修改时间</th><th>操作</th></tr></thead>
<div class="table-card__content"><table><thead><tr><th>字典名称</th><th>字典编码</th><th>字典分类</th><th>描述</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody>
<tr><td>meal_time</td><td>餐次</td><td><span class="tag tag-blue"></span></td><td>早餐/午餐/晚餐/夜宵</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=meal_time&name=餐次">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>order_source</td><td>订单来源</td><td><span class="tag tag-blue"></span></td><td>点餐/餐线/账户/团餐</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=order_source&name=订单来源">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>dispose_type</td><td>处置方式</td><td><span class="tag tag-blue"></span></td><td>福利/捐赠/废弃/再利用</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=dispose_type&name=处置方式">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>pickup_type</td><td>取餐类型</td><td><span class="tag tag-blue"></span></td><td>正常取餐/余量取餐</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=pickup_type&name=取餐类型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>recognize_type</td><td>识别方式</td><td><span class="tag tag-blue"></span></td><td>自动识别/人工选择</td><td>系统</td><td>2026-01-01</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=recognize_type&name=识别方式">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>pickup_method</td><td>取餐方式</td><td><span class="tag tag-gray"></span></td><td>点餐/餐线/混合/外卖</td><td>王运营</td><td>2026-03-15</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=pickup_method&name=取餐方式">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>餐次</td><td>meal_time</td><td>业务</td><td>早餐/午餐/晚餐/夜宵</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=meal_time&name=餐次">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>订单来源</td><td>order_source</td><td>业务</td><td>点餐/餐线/账户/团餐</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=order_source&name=订单来源">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>处置方式</td><td>dispose_type</td><td>业务</td><td>福利/捐赠/废弃/再利用</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=dispose_type&name=处置方式">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>取餐类型</td><td>pickup_type</td><td>业务</td><td>正常取餐/余量取餐</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=pickup_type&name=取餐类型">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>识别方式</td><td>recognize_type</td><td>业务</td><td>自动识别/人工选择</td><td>2026-01-01 10:00:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=recognize_type&name=识别方式">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
<tr><td>取餐方式</td><td>pickup_method</td><td>业务</td><td>点餐/餐线/混合/外卖</td><td>2026-03-15 09:30:00</td><td class="td-actions"><a class="link" href="din-dict-config.html?code=pickup_method&name=取餐方式">字典配置</a><span class="link" onclick="openDialog('edit',this)">编辑</span><span class="link link-danger">删除</span></td></tr>
</tbody></table></div>
<div class="pagination"><span>显示 6 条,共 6 条</span><button class="page-btn active">1</button></div>
</div>
@@ -319,16 +320,19 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<div class="modal__header"><span class="modal__title" id="modalTitle">新增字典</span><span class="modal__close" onclick="closeDialog()">&times;</span></div>
<div class="modal__body">
<div class="form-row"><label class="form-label">字典编码 <em>*</em></label><input class="form-input" id="formCode" placeholder="请输入字典编码(英文下划线)"></div>
<div class="form-row"><label class="form-label">字典名 <em>*</em></label><input class="form-input" id="formName" placeholder="请输入字典名称"></div>
<div class="form-row"><label class="form-label">系统内置</label><select class="form-input" id="formBuiltin"><option value="1"></option><option value="0"></option></select></div>
<div class="form-row"><label class="form-label">备注</label><textarea class="form-input form-textarea" id="formRemark" placeholder="请输入备注信息"></textarea></div>
<div class="form-row"><label class="form-label">字典名 <em>*</em></label><input class="form-input" id="formName" placeholder="请输入字典名称"></div>
<div class="form-row"><label class="form-label">字典分类</label><input class="form-input" id="formCategory" disabled></div>
<div class="form-row"><label class="form-label">描述</label><textarea class="form-input form-textarea" id="formDesc" placeholder="请输入描述信息"></textarea></div>
</div>
<div class="modal__footer"><button class="btn btn-default" onclick="closeDialog()">取消</button><button class="btn btn-primary">确定</button></div>
</div>
</div>
<script>
var tabNames=['烹饪/工艺','感官','营养','适配','仓储/规格','业务'];
var currentTab=0;
function switchTab(idx){
currentTab=idx;
document.querySelectorAll('.page-tab').forEach((t,i)=>t.classList.toggle('active',i===idx));
document.querySelectorAll('.tab-panel').forEach((p,i)=>p.classList.toggle('active',i===idx));
}
@@ -338,22 +342,21 @@ function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function openDialog(mode, el){
var modal=document.getElementById('dictModal');
var title=document.getElementById('modalTitle');
document.getElementById('formCategory').value=tabNames[currentTab];
if(mode==='edit'){
title.textContent='编辑字典';
var row=el.closest('tr');
var cells=row.querySelectorAll('td');
document.getElementById('formCode').value=cells[0].textContent;
document.getElementById('formCode').value=cells[1].textContent;
document.getElementById('formCode').disabled=true;
document.getElementById('formName').value=cells[1].textContent;
document.getElementById('formBuiltin').value=cells[2].textContent.trim()==='是'?'1':'0';
document.getElementById('formRemark').value=cells[3].textContent;
document.getElementById('formName').value=cells[0].textContent;
document.getElementById('formDesc').value=cells[3].textContent;
}else{
title.textContent='新增字典';
document.getElementById('formCode').value='';
document.getElementById('formCode').disabled=false;
document.getElementById('formName').value='';
document.getElementById('formBuiltin').value='0';
document.getElementById('formRemark').value='';
document.getElementById('formDesc').value='';
}
modal.classList.add('show');
}
+484 -277
View File
@@ -44,17 +44,20 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;}
.breadcrumb a{color:var(--primary);text-decoration:none;}.breadcrumb a:hover{text-decoration:underline;}
.breadcrumb__sep{color:var(--text-placeholder);}
/* Tab表单样式 */
.form-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.form-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);padding:0 24px;flex-wrap:wrap;}
.form-tab{padding:14px 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;}
.form-tab:hover{color:var(--primary);}
.form-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:500;}
.form-tab.done{color:var(--success);}
.form-panel{display:none;padding:24px;}
.form-panel.active{display:block;}
.form-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px 24px;}
.form-grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:16px 24px;}
/* 步骤条 */
.steps{display:flex;align-items:stretch;margin-bottom:24px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.step{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;padding:14px 12px;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s;user-select:none;}
.step:hover{background:var(--primary-light);}
.step__num{width:24px;height:24px;border-radius:50%;border:2px solid var(--border);background:#fff;display:flex;align-items:center;justify-content:center;font-size:var(--font-xs);font-weight:600;color:var(--text-placeholder);transition:all .2s;flex-shrink:0;}
.step__label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;transition:all .2s;}
.step.active{border-bottom-color:var(--primary);background:var(--primary-light);}
.step.active .step__num{border-color:var(--primary);background:var(--primary);color:#fff;}
.step.active .step__label{color:var(--primary);font-weight:600;}
/* 表单卡片 */
.form-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:24px;margin-bottom:16px;}
.form-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:20px;padding-bottom:12px;border-bottom:1px solid var(--border);}
.form-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px 24px;}
.form-grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:16px 24px;}
.form-group{display:flex;flex-direction:column;gap:6px;}
.form-group.full{grid-column:1/-1;}
.form-label{font-size:var(--font-sm);color:var(--text-secondary);}
@@ -66,19 +69,51 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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);}
.btn-success{background:var(--success);color:#fff;}.btn-success:hover{background:#73d13d;}
.form-footer{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-top:1px solid var(--border);}
.form-footer__left{display:flex;gap:8px;}
.form-footer__right{display:flex;gap:8px;}
.btn-lg{height:40px;padding:0 24px;font-size:var(--font-base);}
.form-footer{display:flex;align-items:center;gap:12px;padding:16px 24px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
.form-subtitle{font-size:var(--font-sm);font-weight:500;color:var(--text-secondary);margin:16px 0 12px;padding-top:12px;border-top:1px dashed #f0f0f0;}
.form-subtitle:first-child{margin-top:0;padding-top:0;border-top:none;}
/* 规格表格 */
.spec-table{width:100%;border-collapse:collapse;font-size:12px;margin-top:8px;}
.spec-table th{background:#FAFAFA;padding:6px 10px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.spec-table td{padding:6px 10px;border-bottom:1px solid #F0F0F0;color:var(--text-primary);}
.spec-table tr:hover{background:#F8FBFF;}
.spec-add-row:hover{background:var(--primary-light) !important;}
.spec-add-row td{color:var(--primary);cursor:pointer;border-bottom:none;padding:5px 10px;}
.spec-edit-row .form-input{height:28px;font-size:12px;padding:0 8px;}
.spec-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
.spec-toggle__switch{width:32px;height:18px;background:#D9D9D9;border-radius:9px;position:relative;transition:background .2s;flex-shrink:0;}
.spec-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);}
.spec-toggle.active .spec-toggle__switch{background:var(--primary);}
.spec-toggle.active .spec-toggle__switch::after{transform:translateX(14px);}
.spec-toggle__label{font-size:11px;color:var(--text-placeholder);}
.spec-toggle.active .spec-toggle__label{color:var(--primary);font-weight:500;}
.btn-text{background:none;border:none;cursor:pointer;font-size:12px;padding:2px 6px;border-radius:var(--radius-sm);transition:all .15s;}
.btn-text--primary{color:var(--primary);}.btn-text--primary:hover{background:var(--primary-light);}
.btn-text--danger{color:var(--danger);}.btn-text--danger:hover{background:#FFF1F0;}
/* 图片上传网格 */
.upload-grid{display:flex;flex-wrap:wrap;gap:12px;}
.upload-box{width:104px;height:104px;border:1px dashed var(--border);border-radius:var(--radius-sm);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;cursor:pointer;transition:all .2s;position:relative;}
.upload-box:hover{border-color:var(--primary);}
.upload-box__icon{font-size:24px;color:var(--text-placeholder);}
.upload-box__text{font-size:var(--font-xs);color:var(--text-placeholder);}
.upload-box.has-img{border-style:solid;border-color:#f0f0f0;}
.upload-box.has-img .upload-box__icon,.upload-box.has-img .upload-box__text{display:none;}
.upload-box__preview{width:100%;height:100%;object-fit:cover;border-radius:var(--radius-sm);display:none;}
.upload-box.has-img .upload-box__preview{display:block;}
.upload-box__remove{position:absolute;top:-6px;right:-6px;width:18px;height:18px;border-radius:50%;background:rgba(0,0,0,0.55);color:#fff;font-size:11px;display:none;align-items:center;justify-content:center;cursor:pointer;line-height:1;}
.upload-box.has-img .upload-box__remove{display:flex;}
.upload-hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:8px;}
/* 视频上传区域 */
.upload-area{border:1px dashed var(--border);border-radius:var(--radius-md);padding:32px;text-align:center;color:var(--text-placeholder);cursor:pointer;transition:all .2s;}
.upload-area:hover{border-color:var(--primary);color:var(--primary);}
.upload-area__icon{font-size:28px;margin-bottom:6px;color:var(--text-placeholder);}
.upload-area__text{font-size:var(--font-sm);color:var(--text-secondary);}
.upload-area__hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:4px;}
/* 食材表格 */
.ingredient-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);margin-top:12px;}
.ingredient-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.ingredient-table td{padding:10px 12px;border-bottom:1px solid #F0F0F0;color:var(--text-primary);}
.ingredient-table tr:hover{background:#F8FBFF;}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-gray{background:#F5F5F5;color:#999;}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}.link:hover{color:#40a9ff;}
/* 弹窗 */
.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;}
@@ -92,38 +127,23 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.form-row .form-group{margin-bottom:16px;}
.form-row .form-group label{display:block;font-size:var(--font-sm);color:var(--text-primary);margin-bottom:8px;font-weight:500;}
/* 上传区域 */
.upload-area{border:2px dashed var(--border);border-radius:var(--radius-md);padding:40px;text-align:center;color:var(--text-placeholder);cursor:pointer;transition:all .2s;}
.upload-area:hover{border-color:var(--primary);color:var(--primary);}
.upload-area__icon{font-size:36px;margin-bottom:8px;}
.upload-area__text{font-size:var(--font-sm);}
/* 餐品规格区域 */
.spec-section{margin-top:16px;padding-top:16px;border-top:1px solid var(--border);}
.spec-section__header{display:flex;align-items:center;margin-bottom:8px;}
.spec-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:6px;}
.spec-table{width:100%;border-collapse:collapse;font-size:12px;}
.spec-table th{background:#FAFAFA;padding:6px 10px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.spec-table td{padding:6px 10px;border-bottom:1px solid #F0F0F0;color:var(--text-primary);}
.spec-table tr:hover{background:#F8FBFF;}
.spec-add-row:hover{background:var(--primary-light) !important;}
.spec-add-row td{color:var(--primary);cursor:pointer;border-bottom:none;padding:5px 10px;}
.spec-edit-row .form-input{height:28px;font-size:12px;padding:0 8px;}
.tag-default{background:var(--primary-light);color:var(--primary);padding:1px 8px;border-radius:10px;font-size:var(--font-xs);font-weight:500;}
.spec-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
.spec-toggle__switch{width:32px;height:18px;background:#D9D9D9;border-radius:9px;position:relative;transition:background .2s;flex-shrink:0;}
.spec-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);}
.spec-toggle.active .spec-toggle__switch{background:var(--primary);}
.spec-toggle.active .spec-toggle__switch::after{transform:translateX(14px);}
.spec-toggle__label{font-size:11px;color:var(--text-placeholder);}
.spec-toggle.active .spec-toggle__label{color:var(--primary);font-weight:500;}
.btn-text{background:none;border:none;cursor:pointer;font-size:12px;padding:2px 6px;border-radius:var(--radius-sm);transition:all .15s;}
.btn-text--primary{color:var(--primary);}.btn-text--primary:hover{background:var(--primary-light);}
.btn-text--danger{color:var(--danger);}.btn-text--danger:hover{background:#FFF1F0;}
.btn-text--default{color:var(--text-secondary);}.btn-text--default:hover{background:#F5F5F5;color:var(--text-primary);}
/* 操作列固定右侧 */
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;}
/* 搜索选择器 */
.search-select{position:relative;}
.search-select__dropdown{display:none;position:absolute;top:100%;left:0;right:0;z-index:10;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);max-height:200px;overflow-y:auto;margin-top:4px;}
.search-select__dropdown.show{display:block;}
.search-select__item{padding:8px 12px;font-size:var(--font-sm);cursor:pointer;transition:background .15s;display:flex;align-items:center;justify-content:space-between;}
.search-select__item:hover{background:var(--primary-light);}
.search-select__item.selected{color:var(--primary);font-weight:500;}
.search-select__item-sub{font-size:var(--font-xs);color:var(--text-placeholder);}
.search-select__empty{padding:16px 12px;font-size:var(--font-sm);color:var(--text-placeholder);text-align:center;}
/* 动态行 */
.dynamic-rows{display:flex;flex-direction:column;gap:10px;}
.dynamic-row{display:flex;align-items:center;gap:12px;}
.dynamic-row .form-input,.dynamic-row .form-select{flex:1;height:32px;}
.dynamic-row__del{width:24px;height:24px;border:none;background:none;color:var(--text-placeholder);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all .15s;flex-shrink:0;}
.dynamic-row__del:hover{color:var(--danger);background:#FFF1F0;}
.dynamic-row-add{display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border:1px dashed var(--primary);color:var(--primary);border-radius:var(--radius-sm);font-size:var(--font-xs);cursor:pointer;background:none;margin-top:8px;transition:all .2s;}
.dynamic-row-add:hover{background:var(--primary-light);}
</style>
</head>
<body>
@@ -152,6 +172,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
@@ -193,191 +214,29 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<div class="breadcrumb">
<a href="din-dish-all.html">餐品总库</a>
<span class="breadcrumb__sep">/</span>
<span>新增餐品</span>
<span id="pageTitle">新增餐品</span>
</div>
<div class="form-card">
<!-- Tab导航 -->
<div class="form-tabs" id="formTabs">
<div class="form-tab active" onclick="switchTab(0)">🍽️ 基本档案</div>
<div class="form-tab" onclick="switchTab(1)">🧩 餐品构成</div>
<div class="form-tab" onclick="switchTab(2)">🍳 烹饪工艺</div>
<div class="form-tab" onclick="switchTab(3)">📊 营养指标</div>
<div class="form-tab" onclick="switchTab(4)">👅 味道档案</div>
<div class="form-tab" onclick="switchTab(5)">🎨 色泽档案</div>
<div class="form-tab" onclick="switchTab(6)">🫦 口感档案</div>
<div class="form-tab" onclick="switchTab(7)">🖼️ 图片库</div>
<div class="form-tab" onclick="switchTab(8)">🎬 视频库</div>
</div>
<!-- 步骤条 -->
<div class="steps">
<div class="step active" onclick="switchTab(0)"><span class="step__num">1</span><span class="step__label">基本档案</span></div>
<div class="step" onclick="switchTab(1)"><span class="step__num">2</span><span class="step__label">餐品构成</span></div>
<div class="step" onclick="switchTab(2)"><span class="step__num">3</span><span class="step__label">烹饪工艺</span></div>
<div class="step" onclick="switchTab(3)"><span class="step__num">4</span><span class="step__label">营养指标</span></div>
<div class="step" onclick="switchTab(4)"><span class="step__num">5</span><span class="step__label">味道档案</span></div>
<div class="step" onclick="switchTab(5)"><span class="step__num">6</span><span class="step__label">口感档案</span></div>
<div class="step" onclick="switchTab(6)"><span class="step__num">7</span><span class="step__label">色泽档案</span></div>
</div>
<!-- Tab 0: 基本档案 -->
<div class="form-panel active" id="panel0">
<div class="form-grid">
<div class="form-group"><label class="form-label">餐品名称<span class="required">*</span></label><input class="form-input" type="text" placeholder="请输入餐品名称"></div>
<div class="form-group"><label class="form-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 class="form-group"><label class="form-label">烹饪方法</label><select class="form-select"><option>请选择</option><option></option><option></option><option></option><option></option><option>凉拌</option><option></option><option></option></select></div>
<div class="form-group"><label class="form-label">适用餐次</label><input class="form-input" type="text" placeholder="早/午/晚"></div>
</div>
<!-- 餐品规格 -->
<div class="spec-section">
<div class="spec-section__header">
<div class="spec-section__title">📐 餐品规格<span class="required">*</span></div>
</div>
<table class="spec-table" id="specTable">
<thead>
<tr><th>规格</th><th>单位重量(g)</th><th>售价(元/份)</th><th>默认</th><th style="width:60px;">操作</th></tr>
</thead>
<tbody id="specBody">
<tr>
<td>标准份</td>
<td>350</td>
<td>15.00</td>
<td><div class="spec-toggle active" 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>
</tr>
<tr>
<td>小份</td>
<td>200</td>
<td>10.00</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>
</tr>
<tr>
<td>大份</td>
<td>500</td>
<td>20.00</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>
</tr>
<!-- 添加行 -->
<tr class="spec-add-row" onclick="addSpecRow(this)">
<td colspan="5" style="text-align:center;"> 添加规格</td>
</tr>
</tbody>
</table>
</div>
<div class="form-grid" style="margin-top:16px;">
<div class="form-group"><label class="form-label">过敏原</label><input class="form-input" type="text" placeholder="无则填无"></div>
<div class="form-group"><label class="form-label">餐品标签</label><input class="form-input" type="text" placeholder="低脂/高蛋白/素食,逗号分隔"></div>
<div class="form-group"><label class="form-label">上架状态</label><select class="form-select"><option>上架</option><option>下架</option></select></div>
</div>
</div>
<!-- 表单区域 -->
<div class="form-card" id="stepContent"></div>
<!-- Tab 1: 餐品构成 -->
<div class="form-panel" id="panel1">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<span style="font-size:var(--font-sm);font-weight:600;">食材清单</span>
<button class="btn btn-primary" style="height:30px;font-size:var(--font-xs);" onclick="openIngredientDialog()"> 新增食材</button>
</div>
<table class="ingredient-table">
<thead><tr><th>食材名称</th><th>食材分类</th><th>用量(g/份)</th><th>加工状态</th><th>是否主料</th><th>过敏原</th><th>备注</th><th>操作</th></tr></thead>
<tbody>
<tr><td class="td-actions" colspan="8" style="text-align:center;color:var(--text-placeholder);padding:32px;">暂无食材,请点击"新增食材"添加</td></tr>
</tbody>
</table>
</div>
<!-- Tab 2: 烹饪工艺 -->
<div class="form-panel" id="panel2">
<div class="form-grid">
<div class="form-group"><label class="form-label">主烹饪法<span class="required">*</span></label><select class="form-select"><option>请选择</option><option></option><option></option><option></option><option></option><option></option><option></option><option>凉拌</option></select></div>
<div class="form-group"><label class="form-label">辅烹饪法</label><select class="form-select"><option></option><option></option><option></option><option></option><option></option></select></div>
<div class="form-group"><label class="form-label">火候</label><input class="form-input" type="text" placeholder="如:大火→中火"></div>
<div class="form-group"><label class="form-label">烹饪时长(min)</label><input class="form-input" type="number" placeholder="请输入时长"></div>
<div class="form-group"><label class="form-label">油用量(g)</label><input class="form-input" type="number" placeholder="请输入油用量"></div>
<div class="form-group"><label class="form-label">盐用量(g)</label><input class="form-input" type="number" placeholder="请输入盐用量"></div>
<div class="form-group"><label class="form-label">烹饪温度(℃)</label><input class="form-input" type="text" placeholder="如:100→80"></div>
<div class="form-group"><label class="form-label">出品量(g)</label><input class="form-input" type="number" placeholder="请输入出品量"></div>
<div class="form-group"><label class="form-label">工艺步骤数</label><input class="form-input" type="number" placeholder="请输入步骤数"></div>
</div>
</div>
<!-- Tab 3: 营养指标 -->
<div class="form-panel" id="panel3">
<div class="form-grid-3">
<div class="form-group"><label class="form-label">热量(kcal/份)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">蛋白质(g)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">脂肪(g)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">碳水化合物(g)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">钠(mg)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">GI值</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">GI等级</label><select class="form-select"><option>请选择</option><option>低GI</option><option>中GI</option><option>高GI</option></select></div>
<div class="form-group"><label class="form-label">GL值</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">SI值</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">ORAC值</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">嘌呤(mg/100g)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">胆固醇(mg/份)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">反式脂肪(g/份)</label><input class="form-input" type="number" placeholder="请输入"></div>
<div class="form-group"><label class="form-label">膳食宝塔分类</label><input class="form-input" type="text" placeholder="如:畜禽肉蛋水产"></div>
</div>
</div>
<!-- Tab 4: 味道档案 -->
<div class="form-panel" id="panel4">
<div class="form-grid">
<div class="form-group"><label class="form-label">主味型<span class="required">*</span></label><select class="form-select"><option>请选择</option><option>咸鲜</option><option>酸甜</option><option>淡味</option><option>酸鲜</option><option>麻辣</option><option>香辣</option></select></div>
<div class="form-group"><label class="form-label">辅味型</label><select class="form-select"><option></option><option>微甜</option><option>清香</option><option>淡咸</option><option>咸鲜</option></select></div>
<div class="form-group"><label class="form-label">味道强度</label><select class="form-select"><option>请选择</option><option></option><option></option><option></option></select></div>
<div class="form-group full"><label class="form-label">味道描述</label><textarea class="form-textarea" placeholder="请描述味道特征"></textarea></div>
<div class="form-group"><label class="form-label">适配人群</label><input class="form-input" type="text" placeholder="如:成人、老年人,逗号分隔"></div>
<div class="form-group"><label class="form-label">适配等级</label><select class="form-select"><option>推荐</option><option>适量</option><option>慎食</option></select></div>
<div class="form-group full"><label class="form-label">饮食禁忌说明</label><input class="form-input" type="text" placeholder="如:糖尿病人群慎食"></div>
</div>
</div>
<!-- Tab 5: 色泽档案 -->
<div class="form-panel" id="panel5">
<div class="form-grid">
<div class="form-group"><label class="form-label">主色系<span class="required">*</span></label><input class="form-input" type="text" placeholder="如:红褐色"></div>
<div class="form-group"><label class="form-label">感官评级</label><select class="form-select"><option>请选择</option><option></option><option></option><option></option><option></option></select></div>
<div class="form-group full"><label class="form-label">色泽描述</label><textarea class="form-textarea" placeholder="请描述色泽特征"></textarea></div>
<div class="form-group full"><label class="form-label">色泽成因</label><input class="form-input" type="text" placeholder="如:酱油上色,焦糖化反应"></div>
<div class="form-group"><label class="form-label">适配人群</label><input class="form-input" type="text" placeholder="如:成人、老年人"></div>
<div class="form-group"><label class="form-label">适配等级</label><select class="form-select"><option>推荐</option><option>适量</option><option>慎食</option></select></div>
<div class="form-group full"><label class="form-label">备注</label><input class="form-input" type="text" placeholder="请输入备注"></div>
</div>
</div>
<!-- Tab 6: 口感档案 -->
<div class="form-panel" id="panel6">
<div class="form-grid">
<div class="form-group"><label class="form-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 class="form-group"><label class="form-label">咀嚼难度</label><select class="form-select"><option>请选择</option><option></option><option></option><option></option></select></div>
<div class="form-group"><label class="form-label">消化难度</label><select class="form-select"><option>请选择</option><option></option><option></option><option></option></select></div>
<div class="form-group full"><label class="form-label">口感描述</label><textarea class="form-textarea" placeholder="请描述口感特征"></textarea></div>
<div class="form-group"><label class="form-label">适配人群</label><input class="form-input" type="text" placeholder="如:全人群、老年人"></div>
<div class="form-group"><label class="form-label">适配等级</label><select class="form-select"><option>推荐</option><option>适量</option><option>慎食</option></select></div>
<div class="form-group full"><label class="form-label">烹饪建议</label><input class="form-input" type="text" placeholder="如:老年人可炒至更软"></div>
</div>
</div>
<!-- Tab 7: 图片库 -->
<div class="form-panel" id="panel7">
<div class="upload-area">
<div class="upload-area__icon">📤</div>
<div class="upload-area__text">点击或拖拽上传餐品图片<br><span style="font-size:var(--font-xs);color:var(--text-placeholder);">支持 JPG/PNG,单张不超过 5MB</span></div>
</div>
</div>
<!-- Tab 8: 视频库 -->
<div class="form-panel" id="panel8">
<div class="upload-area">
<div class="upload-area__icon">🎬</div>
<div class="upload-area__text">点击或拖拽上传餐品视频<br><span style="font-size:var(--font-xs);color:var(--text-placeholder);">支持 MP4/MOV,单个不超过 500MB</span></div>
</div>
</div>
<!-- 底部操作栏 -->
<div class="form-footer">
<div class="form-footer__left">
<a href="din-dish-all.html" class="btn btn-default">取消</a>
</div>
<div class="form-footer__right">
<button class="btn btn-default" onclick="prevTab()">← 上一步</button>
<button class="btn btn-primary" onclick="nextTab()">下一步 →</button>
<button class="btn btn-success" onclick="submitForm()">✓ 提交</button>
</div>
</div>
<!-- 底部操作 -->
<div class="form-footer">
<button class="btn btn-default btn-lg" id="btnPrev" onclick="prevStep()" style="display:none;">上一步</button>
<button class="btn btn-primary btn-lg" id="btnNext" onclick="nextStep()">下一步</button>
<button class="btn btn-primary btn-lg" id="btnSubmit" onclick="submitForm()" style="display:none;">提交</button>
<a href="din-dish-all.html" class="btn btn-default btn-lg">取消</a>
</div>
</main>
@@ -389,10 +248,14 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<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><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></select></div><div class="form-group"><label>过敏原</label><input class="form-input" type="text" placeholder="无则填无"></div></div>
<div class="form-row"><div class="form-group" style="grid-column:1/-1;"><label>备注</label><input class="form-input" type="text" placeholder="请输入备注信息"></div></div>
<div class="form-group"><label>食材<span class="required">*</span></label>
<div class="search-select" id="ingredientSearchSelect">
<input class="form-input" type="text" id="ingredientSearchInput" placeholder="搜索食材名称..." oninput="searchIngredient(this.value)" onfocus="showIngredientDropdown()">
<div class="search-select__dropdown" id="ingredientDropdown"></div>
</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></select></div></div>
<div class="form-row"><div class="form-group"><label>过敏原</label><input class="form-input" type="text" placeholder="无则填无"></div><div class="form-group"><label>备注</label><input class="form-input" type="text" placeholder="请输入备注信息"></div></div>
</div>
<div class="dialog__footer">
<button class="btn btn-default" onclick="closeIngredientDialog()">取消</button>
@@ -402,68 +265,412 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<script>
let currentTab = 0;
const totalTabs = 9;
function switchTab(idx){
currentTab = idx;
document.querySelectorAll('.form-tab').forEach((t,i)=>t.classList.toggle('active',i===idx));
document.querySelectorAll('.form-panel').forEach((p,i)=>p.classList.toggle('active',i===idx));
}
function nextTab(){ if(currentTab < totalTabs-1) switchTab(currentTab+1); }
function prevTab(){ if(currentTab > 0) switchTab(currentTab-1); }
function submitForm(){ alert('提交成功!'); window.location.href='din-dish-all.html'; }
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
function openIngredientDialog(){document.getElementById('ingredientDialog').classList.add('open');}
function closeIngredientDialog(){document.getElementById('ingredientDialog').classList.remove('open');}
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
var urlParams = new URLSearchParams(window.location.search);
var pageMode = urlParams.get('mode') || 'add';
var initTab = parseInt(urlParams.get('tab')) || 0;
var currentStep = 0;
var totalSteps = 7;
if(initTab >= 0 && initTab < totalSteps) currentStep = initTab;
/* 适配人群数据 */
var crowdList = [];
var CROWD_OPTIONS=['成人','老年人','儿童','婴幼儿','孕妇','糖尿病人群','高血压人群','高血脂人群','肾病人群','痛风人群','肾结石人群','术后恢复','全人群'];
var STEP_HTML = [
// Step 0: 基本档案
`<div class="form-card__title">🍽️ 基本档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">餐品名称<span class="required">*</span></label><input class="form-input" placeholder="请输入餐品名称"></div>
<div class="form-group"><label class="form-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 class="form-group"><label class="form-label">烹饪方法</label><select class="form-select"><option>请选择</option><option>炒</option><option>蒸</option><option>炖</option><option>煮</option><option>凉拌</option><option>炸</option><option>烤</option></select></div>
<div class="form-group"><label class="form-label">适用餐次</label><select class="form-select"><option>请选择</option><option>早餐</option><option>午餐</option><option>晚餐</option><option>早/午</option><option>午/晚</option><option>早/午/晚</option></select></div>
<div class="form-group"><label class="form-label">膳食宝塔分类</label><select class="form-select"><option>请选择</option><option>谷薯杂豆</option><option>蔬菜</option><option>水果</option><option>畜禽肉蛋水产</option><option>奶及奶制品</option><option>大豆坚果</option><option>油脂</option><option>盐</option></select></div>
<div class="form-group"><label class="form-label">过敏原</label><input class="form-input" placeholder="无则填无"></div>
<div class="form-group"><label class="form-label">餐品标签</label><select class="form-select"><option>请选择</option><option>低脂</option><option>高蛋白</option><option>素食</option><option>低卡</option><option>高纤维</option><option>低GI</option><option>高糖</option><option>无麸质</option></select></div>
<div class="form-group"><label class="form-label">上架状态</label><select class="form-select"><option>上架</option><option>下架</option></select></div>
</div>
<div class="form-subtitle">👥 适配人群</div>
<div class="dynamic-rows" id="crowdRows"></div>
<button type="button" class="dynamic-row-add" onclick="addCrowdRow()">+ 添加人群</button>
<div class="form-subtitle">📐 餐品规格</div>
<table class="spec-table" id="specTable">
<thead><tr><th>规格</th><th>单位重量(g)</th><th>售价(元/份)</th><th>默认</th><th style="width:60px;">操作</th></tr></thead>
<tbody id="specBody">
<tr><td>标准份</td><td>350</td><td>15.00</td><td><div class="spec-toggle active" 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></tr>
<tr><td>小份</td><td>200</td><td>10.00</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></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>
<div class="upload-grid" id="imageGrid">
<div class="upload-box has-img">
<img class="upload-box__preview" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='104' height='104'%3E%3Crect width='104' height='104' fill='%23f0f0f0'/%3E%3Ctext x='52' y='56' text-anchor='middle' fill='%23999' font-size='12'%3E示例图%3C/text%3E%3C/svg%3E">
<span class="upload-box__remove" onclick="removeImage(this)">×</span>
</div>
<div class="upload-box" onclick="triggerImageUpload()">
<span class="upload-box__icon">+</span>
<span class="upload-box__text">上传图片</span>
</div>
</div>
<div class="upload-hint">支持 JPG、PNG 格式,最多上传 9 张</div>
<div class="form-subtitle">🎬 制作流程</div>
<div class="upload-area" onclick="triggerVideoUpload()">
<div class="upload-area__icon">⇧</div>
<div class="upload-area__text">点击或拖拽上传制作视频</div>
<div class="upload-area__hint">支持 MP4 / MOV,不超过 500MB</div>
</div>`,
// Step 1: 餐品构成
`<div class="form-card__title">🧩 餐品构成</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<span style="font-size:var(--font-sm);font-weight:600;">食材清单</span>
<button class="btn btn-primary" style="height:30px;font-size:var(--font-xs);" onclick="openIngredientDialog()"> 新增食材</button>
</div>
<table class="ingredient-table">
<thead><tr><th>食材名称</th><th>食材分类</th><th>用量(g/份)</th><th>加工状态</th><th>是否主料</th><th>过敏原</th><th>备注</th><th>操作</th></tr></thead>
<tbody><tr><td colspan="8" style="text-align:center;color:var(--text-placeholder);padding:32px;">暂无食材,请点击"新增食材"添加</td></tr></tbody>
</table>`,
// Step 2: 烹饪工艺
`<div class="form-card__title">🍳 烹饪工艺</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主烹饪法<span class="required">*</span></label><select class="form-select"><option>请选择</option><option>炒</option><option>蒸</option><option>炖</option><option>煮</option><option>炸</option><option>烤</option><option>凉拌</option></select></div>
<div class="form-group"><label class="form-label">辅烹饪法</label><select class="form-select"><option>无</option><option>煎</option><option>炒</option><option>蒸</option><option>炸</option></select></div>
<div class="form-group"><label class="form-label">火候</label><input class="form-input" placeholder="如:大火→中火"></div>
<div class="form-group"><label class="form-label">烹饪时长(min)</label><input class="form-input" type="number" placeholder="请输入时长"></div>
<div class="form-group"><label class="form-label">油用量(g)</label><input class="form-input" type="number" placeholder="请输入油用量"></div>
<div class="form-group"><label class="form-label">盐用量(g)</label><input class="form-input" type="number" placeholder="请输入盐用量"></div>
<div class="form-group"><label class="form-label">烹饪温度(℃)</label><input class="form-input" placeholder="如:100→80"></div>
<div class="form-group"><label class="form-label">出品量(g)</label><input class="form-input" type="number" placeholder="请输入出品量"></div>
<div class="form-group"><label class="form-label">工艺步骤数</label><input class="form-input" type="number" placeholder="请输入步骤数"></div>
</div>`,
// Step 3: 营养指标
`<div class="form-card__title">📊 营养指标</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">热量 (kcal/100g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">蛋白质 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">碳水化合物 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">脂肪 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">维生素C (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">矿物质 (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">膳食纤维 (g)</label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">钠 (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
</div>`,
// Step 4: 味道档案
`<div class="form-card__title">👅 味道档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">酸度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">甜度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">苦度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">辣度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">咸度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">气味评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">综合风味评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
</div>`,
// Step 5: 口感档案
`<div class="form-card__title">🫦 口感档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">软硬度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">黏糯度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">酥脆度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">嫩滑度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">弹性 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">综合口感评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
</div>`,
// Step 6: 色泽档案
`<div class="form-card__title">🎨 色泽档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主色调<span class="required">*</span></label><input class="form-input" type="text" placeholder="如:深红褐色"></div>
<div class="form-group"><label class="form-label">色泽均匀度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">光泽度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">新鲜度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">卖相评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="form-group"><label class="form-label">综合色泽评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="form-group"><label class="form-label">综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
</div>`
];
function renderStep(){
document.getElementById('stepContent').innerHTML = STEP_HTML[currentStep];
updateStepUI();
if(currentStep===0) renderCrowdRows();
if(pageMode==='edit') fillEditData();
}
function updateStepUI(){
document.querySelectorAll('.step').forEach(function(el,i){el.classList.toggle('active',i===currentStep);});
if(pageMode==='edit'){
document.getElementById('btnPrev').style.display = 'none';
document.getElementById('btnNext').style.display = 'none';
document.getElementById('btnSubmit').textContent = '保存修改';
document.getElementById('btnSubmit').style.display = 'inline-flex';
} else {
document.getElementById('btnPrev').style.display = currentStep>0?'inline-flex':'none';
document.getElementById('btnNext').style.display = currentStep<totalSteps-1?'inline-flex':'none';
document.getElementById('btnSubmit').style.display = currentStep===totalSteps-1?'inline-flex':'none';
}
}
function switchTab(idx){
if(idx===currentStep) return;
currentStep = idx;
renderStep();
window.scrollTo(0,0);
}
function nextStep(){ if(currentStep<totalSteps-1){currentStep++;renderStep();window.scrollTo(0,0);} }
function prevStep(){ if(currentStep>0){currentStep--;renderStep();window.scrollTo(0,0);} }
function submitForm(){ alert('提交成功!'); window.location.href='din-dish-all.html'; }
/* 规格管理 */
function addSpecRow(addRow){
/* 如果已有编辑行,不重复添加 */
if(document.querySelector('.spec-edit-row')) return;
var tr=document.createElement('tr');
tr.className='spec-edit-row';
tr.innerHTML='<td><input class="form-input" type="text" placeholder="规格名称"></td>'
+'<td><input class="form-input" type="number" placeholder="重量"></td>'
+'<td><input class="form-input" type="number" placeholder="售价"></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><select class="form-select" style="height:28px;font-size:12px;padding:0 24px 0 8px;"><option>请选择</option><option>标准份</option><option>小份</option><option>大份</option><option>迷你份</option><option>加大份</option></select></td><td><input class="form-input" type="number" placeholder="重量"></td><td><input class="form-input" type="number" placeholder="售价"></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);
tr.querySelector('input').focus();
}
function confirmSpec(btn){
var row=btn.closest('tr');
var sel=row.querySelector('select');
var inputs=row.querySelectorAll('input');
var name=inputs[0].value.trim();
var weight=inputs[1].value.trim();
var price=inputs[2].value.trim();
if(!name||!weight||!price){alert('请填写完整信息');return;}
/* 将编辑行转为数据行 */
var name=sel?sel.value:'';
var weight=inputs[0].value.trim(),price=inputs[1].value.trim();
if(!name||name==='请选择'||!weight||!price){alert('请填写完整信息');return;}
row.className='';
row.innerHTML='<td>'+name+'</td><td>'+weight+'</td><td>'+parseFloat(price).toFixed(2)+'</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 cancelSpec(btn){
btn.closest('tr').remove();
row.innerHTML='<td>'+name+'</td><td>'+weight+'</td><td>'+parseFloat(price).toFixed(2)+'</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 cancelSpec(btn){btn.closest('tr').remove();}
function toggleDefault(el){
var tbody=document.getElementById('specBody');
/* 清除所有开关状态 */
tbody.querySelectorAll('.spec-toggle').forEach(function(t){ t.classList.remove('active'); });
/* 激活当前开关 */
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 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 INGREDIENT_LIST=[
{name:'猪五花肉',cat:'畜禽肉类/猪肉'},{name:'猪里脊肉',cat:'畜禽肉类/猪肉'},{name:'牛腩',cat:'畜禽肉类/牛肉'},{name:'鸡胸肉',cat:'畜禽肉类/鸡肉'},{name:'鸡蛋',cat:'蛋类'},
{name:'番茄',cat:'蔬菜类/叶菜类'},{name:'菠菜',cat:'蔬菜类'},{name:'土豆',cat:'蔬菜类'},{name:'胡萝卜',cat:'蔬菜类'},{name:'大白菜',cat:'蔬菜类'},
{name:'大米',cat:'谷薯类/稻米'},{name:'面粉',cat:'谷薯类'},{name:'豆腐',cat:'豆制品'},{name:'食用油',cat:'油脂类'},{name:'生姜',cat:'调料'},
{name:'大蒜',cat:'调料'},{name:'生抽',cat:'调料'},{name:'食盐',cat:'调料'},{name:'白糖',cat:'调料'},{name:'料酒',cat:'调料'}
];
function searchIngredient(keyword){
var dropdown=document.getElementById('ingredientDropdown');
if(!keyword.trim()){dropdown.classList.remove('show');return;}
var results=INGREDIENT_LIST.filter(function(item){return item.name.indexOf(keyword)!==-1;});
if(results.length===0){
dropdown.innerHTML='<div class="search-select__empty">未找到匹配食材</div>';
} else {
dropdown.innerHTML=results.map(function(item){
return '<div class="search-select__item" onclick="selectIngredient(\''+item.name+'\')"><span>'+item.name+'</span><span class="search-select__item-sub">'+item.cat+'</span></div>';
}).join('');
}
dropdown.classList.add('show');
}
function showIngredientDropdown(){
var val=document.getElementById('ingredientSearchInput').value;
if(val.trim()) searchIngredient(val);
}
function selectIngredient(name){
document.getElementById('ingredientSearchInput').value=name;
document.getElementById('ingredientDropdown').classList.remove('show');
}
document.addEventListener('click',function(e){
if(!e.target.closest('#ingredientSearchSelect')){
var dd=document.getElementById('ingredientDropdown');
if(dd) dd.classList.remove('show');
}
});
/* 图片上传 */
function triggerImageUpload(){
var grid=document.getElementById('imageGrid');
if(!grid) return;
var count=grid.querySelectorAll('.upload-box.has-img').length;
if(count>=9){alert('最多上传9张图片');return;}
var box=document.createElement('div');
box.className='upload-box has-img';
box.innerHTML='<img class="upload-box__preview" src="data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'104\' height=\'104\'%3E%3Crect width=\'104\' height=\'104\' fill=\'%23f0f0f0\'/%3E%3Ctext x=\'52\' y=\'56\' text-anchor=\'middle\' fill=\'%23999\' font-size=\'12\'%3E图片%3C/text%3E%3C/svg%3E"><span class="upload-box__remove" onclick="removeImage(this)">×</span>';
var addBtn=grid.querySelector('.upload-box:not(.has-img)');
grid.insertBefore(box,addBtn);
if(grid.querySelectorAll('.upload-box.has-img').length>=9 && addBtn) addBtn.style.display='none';
}
function removeImage(btn){
var box=btn.parentElement;
var grid=box.parentElement;
box.remove();
var addBtn=grid.querySelector('.upload-box:not(.has-img)');
if(addBtn) addBtn.style.display='';
}
function triggerVideoUpload(){
alert('选择视频文件(原型演示)');
}
/* === 适配人群动态行 === */
function renderCrowdRows(){
var container = document.getElementById('crowdRows');
if(!container) return;
if(crowdList.length===0) crowdList.push({name:'',level:''});
var html = '';
crowdList.forEach(function(item,i){
html += '<div class="dynamic-row">';
html += '<select class="form-select crowd-name"><option value="">请选择人群</option>';
CROWD_OPTIONS.forEach(function(opt){html+='<option'+(item.name===opt?' selected':'')+'>'+opt+'</option>';});
html += '</select>';
html += '<select class="form-select crowd-level"><option value="">适配等级</option><option'+(item.level==='推荐'?' selected':'')+'>推荐</option><option'+(item.level==='适量'?' selected':'')+'>适量</option><option'+(item.level==='慎食'?' selected':'')+'>慎食</option></select>';
html += '<button type="button" class="dynamic-row__del" onclick="removeCrowdRow('+i+')" title="删除">×</button>';
html += '</div>';
});
container.innerHTML = html;
}
function addCrowdRow(){
saveCrowdRows();
crowdList.push({name:'',level:''});
renderCrowdRows();
}
function removeCrowdRow(idx){
saveCrowdRows();
crowdList.splice(idx,1);
if(crowdList.length===0) crowdList.push({name:'',level:''});
renderCrowdRows();
}
function saveCrowdRows(){
var rows = document.querySelectorAll('#crowdRows .dynamic-row');
crowdList = [];
rows.forEach(function(row){
var name = row.querySelector('.crowd-name');
var level = row.querySelector('.crowd-level');
if(name && level) crowdList.push({name:name.value,level:level.value});
});
}
// 编辑模式:修改标题 + 预填数据
var EDIT_DATA = {
name:'红烧肉', category:'荤菜', method:'炖', meal:'午/晚', pyramid:'畜禽肉蛋水产',
allergen:'无', tag:'高蛋白', status:'上架',
crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],
specs:[{spec:'标准份',weight:200,price:12,isDefault:true},{spec:'小份',weight:120,price:8,isDefault:false},{spec:'大份',weight:300,price:18,isDefault:false}],
composition:[{name:'五花肉',amount:150,pct:'60%',main:true,allergen:'无',note:'—'},{name:'冰糖',amount:20,pct:'8%',main:false,allergen:'无',note:'上色'},{name:'酱油',amount:15,pct:'6%',main:false,allergen:'含大豆',note:'调味'},{name:'料酒',amount:10,pct:'4%',main:false,allergen:'无',note:'去腥'}],
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,oil:15,salt:3,temp:100,output:200,steps:6,desc:'1.五花肉切块焯水;2.锅中煎至金黄;3.加冰糖炒色;4.加酱油料酒;5.加水小火炖40分钟;6.大火收汁'},
nutri:{calorie:385,protein:18.2,carb:12.4,fat:28.6,vitC:2.1,mineral:45,fiber:1.8,sodium:486},
taste:{sour:1,sweet:3,bitter:1,spicy:1,salty:4,smell:4.2,flavorScore:3.8,flavorGrade:'good'},
color:{mainColor:'红褐色',uniformity:4,gloss:4,freshness:4,appearance:4,colorScore:4.0,colorGrade:'good'},
texture:{hardness:2,sticky:3,crispy:1,smooth:4,elastic:3,textureScore:3.6,textureGrade:'good'}
};
if(pageMode==='edit'){
document.getElementById('pageTitle').textContent = '编辑餐品';
document.title = '营养管理 - 编辑餐品 | 健康长庆原型';
}
renderStep();
// 编辑模式预填表单
if(pageMode==='edit'){
fillEditData();
}
function fillEditData(){
var container = document.getElementById('stepContent');
var inputs = container.querySelectorAll('.form-input');
var selects = container.querySelectorAll('.form-select');
var d = EDIT_DATA;
if(currentStep===0){
// 基本档案
if(inputs[0]) inputs[0].value = d.name;
if(inputs[1]) inputs[1].value = d.allergen;
setSelectByText(selects[0], d.category);
setSelectByText(selects[1], d.method);
setSelectByText(selects[2], d.meal);
setSelectByText(selects[3], d.pyramid);
setSelectByText(selects[4], d.tag);
setSelectByText(selects[5], d.status);
// 预填适配人群
if(d.crowd && d.crowd.length>0){
crowdList = d.crowd.map(function(c){return {name:c.name,level:c.level};});
renderCrowdRows();
}
// 预填规格表
var specBody = container.querySelector('#specTable tbody');
if(specBody){
specBody.innerHTML = '';
d.specs.forEach(function(s){
var tr = document.createElement('tr');
tr.innerHTML = '<td><select class="form-select form-select--sm"><option'+(s.spec==='标准份'?' selected':'')+'>标准份</option><option'+(s.spec==='小份'?' selected':'')+'>小份</option><option'+(s.spec==='大份'?' selected':'')+'>大份</option><option'+(s.spec==='加大份'?' selected':'')+'>加大份</option></select></td><td><input class="form-input form-input--sm" value="'+s.weight+'"></td><td><input class="form-input form-input--sm" value="'+s.price+'"></td><td><input type="checkbox"'+(s.isDefault?' checked':'')+'></td><td><span class="link" onclick="this.closest(\'tr\').remove()">删除</span></td>';
specBody.appendChild(tr);
});
}
} else if(currentStep===1){
// 餐品构成 - 预填食材表格
var tbody = container.querySelector('table tbody');
if(tbody){
tbody.innerHTML = '';
d.composition.forEach(function(c){
var tr = document.createElement('tr');
tr.innerHTML = '<td>'+c.name+'</td><td>'+c.amount+'g</td><td>'+c.pct+'</td><td><span class="tag tag-'+(c.main?'green':'gray')+'">'+(c.main?'主料':'辅料')+'</span></td><td>'+c.allergen+'</td><td>'+c.note+'</td><td><span class="link">编辑</span> <span class="link" style="color:var(--danger)">删除</span></td>';
tbody.appendChild(tr);
});
}
} else if(currentStep===2){
// 烹饪工艺
var c = d.cook;
var vals = [c.mainMethod, c.subMethod, c.heat, c.duration, c.oil, c.salt, c.temp, c.output, c.steps];
inputs.forEach(function(inp, i){ if(vals[i]!==undefined) inp.value = vals[i]; });
var ta = container.querySelector('textarea');
if(ta) ta.value = c.desc||'';
} else if(currentStep===3){
// 营养指标
var n = d.nutri;
var nvals = [n.calorie, n.protein, n.carb, n.fat, n.vitC, n.mineral, n.fiber, n.sodium];
inputs.forEach(function(inp, i){ if(nvals[i]!==undefined) inp.value = nvals[i]; });
} else if(currentStep===4){
// 味道档案
var t = d.taste;
var tvals = [t.sour, t.sweet, t.bitter, t.spicy, t.salty, t.smell, t.flavorScore];
inputs.forEach(function(inp, i){ if(tvals[i]!==undefined) inp.value = tvals[i]; });
if(selects[0]) setSelectByValue(selects[0], t.flavorGrade);
} else if(currentStep===5){
// 口感档案
var tx = d.texture;
var txvals = [tx.hardness, tx.sticky, tx.crispy, tx.smooth, tx.elastic, tx.textureScore];
inputs.forEach(function(inp, i){ if(txvals[i]!==undefined) inp.value = txvals[i]; });
if(selects[0]) setSelectByValue(selects[0], tx.textureGrade);
} else if(currentStep===6){
// 色泽档案
var cl = d.color;
var cvals = [cl.mainColor, cl.uniformity, cl.gloss, cl.freshness, cl.appearance, cl.colorScore];
inputs.forEach(function(inp, i){ if(cvals[i]!==undefined) inp.value = cvals[i]; });
if(selects[0]) setSelectByValue(selects[0], cl.colorGrade);
}
}
function setSelectByText(sel, text){
if(!sel || !text) return;
for(var i=0;i<sel.options.length;i++){
if(sel.options[i].text === text){ sel.selectedIndex = i; return; }
}
}
function setSelectByValue(sel, val){
if(!sel || !val) return;
for(var i=0;i<sel.options.length;i++){
if(sel.options[i].value === val){ sel.selectedIndex = i; return; }
}
}
</script>
</body>
</html>
+100 -83
View File
@@ -173,6 +173,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
@@ -236,7 +237,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</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">🔄 同步检验数据</button>
<button class="btn btn-default">📤 批量导入</button>
</div>
<div class="food-list" id="dishList"></div>
@@ -262,10 +263,8 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<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 class="detail-tab" onclick="switchDetailTab(7)">🖼️ 图片库</div>
<div class="detail-tab" onclick="switchDetailTab(8)">🎬 视频库</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>
@@ -276,45 +275,45 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<script>
// 餐品数据
const DISHES = [
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,isDefault:true},{name:'小份',weight:120,price:8},{name:'大份',weight:300,price:18}],method:'炖',meal:'午/晚',allergen:'无',status:'上架',
composition:[{name:'猪五花肉',cat:'畜肉类',amount:160,pct:'80%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'生姜',cat:'调料',amount:5,pct:'2.5%',state:'净菜',main:false,allergen:'无',note:'—'},{name:'生抽',cat:'调',amount:15,pct:'7.5%',state:'原料',main:false,allergen:'含麸质',note:'酱油'},{name:'冰糖',cat:'调',amount:10,pct:'5%',state:'原料',main:false,allergen:'无',note:'上色提味'},{name:'料酒',cat:'调',amount:10,pct:'5%',state:'原料',main:false,allergen:'无',note:'去腥'}],
nutri:{calorie:385,protein:18.2,fat:28.6,carb:12.4,sodium:486,gi:52,giLevel:'中GI',gl:18.6,si:68,orac:420,purine:120,cholesterol:86,transFat:0.2,pyramid:'畜禽肉蛋水产'},
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,isDefault:true},{name:'小份',weight:120,price:8},{name:'大份',weight:300,price:18}],method:'炖',meal:'午/晚',allergen:'无',status:'上架',crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',
composition:[{name:'猪五花肉',cat:'肉禽类/猪肉',amount:160,pct:'80%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'生姜',cat:'调味品/香辛料',amount:5,pct:'2.5%',state:'净菜',main:false,allergen:'无',note:'—'},{name:'生抽',cat:'调味品/酱油',amount:15,pct:'7.5%',state:'原料',main:false,allergen:'含麸质',note:'酱油'},{name:'冰糖',cat:'调味品/糖类',amount:10,pct:'5%',state:'原料',main:false,allergen:'无',note:'上色提味'},{name:'料酒',cat:'调味品/酒类',amount:10,pct:'5%',state:'原料',main:false,allergen:'无',note:'去腥'}],
nutri:{calorie:385,protein:18.2,carb:12.4,fat:28.6,vitC:0.8,mineral:42,fiber:0.3,sodium:486,note:'铁2.4mg、锌1.8mg'},
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,oil:15,salt:3,temp:'100→80',output:200,steps:6},
taste:{mainFlavor:'咸鲜',subFlavor:'微甜',intensity:'强',desc:'酱香浓郁,咸甜适中,肉香四溢',crowd:['成人','老年人'],level:'适量',taboo:'老年人控制摄入量,钠含量偏高'},
color:{mainColor:'红褐色',desc:'色泽红亮,油润光泽,肉质饱满',cause:'酱油上色,焦糖化反应',grade:'优',crowd:['成人','老年人'],level:'适量',note:'老年人消化负担稍重'},
texture:{mainTexture:'软烂',chew:'易',digest:'中',desc:'肥而不腻,入口即化,胶质丰富',crowd:['成人','老年人','儿童'],level:'适量',suggest:'儿童控制脂肪摄入'}
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,isDefault:true},{name:'小份',weight:100,price:4}],method:'炒',meal:'早/午/晚',allergen:'无',status:'上架',
composition:[{name:'时令青菜',cat:'蔬菜类',amount:120,pct:'80%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'蒜末',cat:'调料',amount:5,pct:'3.3%',state:'净菜',main:false,allergen:'无',note:'—'},{name:'食用油',cat:'油脂类',amount:8,pct:'5.3%',state:'原料',main:false,allergen:'无',note:'—'},{name:'食盐',cat:'调',amount:2,pct:'1.3%',state:'原料',main:false,allergen:'无',note:'—'}],
nutri:{calorie:68,protein:2.1,fat:3.2,carb:8.6,sodium:186,gi:15,giLevel:'低GI',gl:2.1,si:142,orac:2860,purine:18,cholesterol:0,transFat:0,pyramid:'蔬菜'},
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{name:'标准份',weight:150,price:6,isDefault:true},{name:'小份',weight:100,price:4}],method:'炒',meal:'早/午/晚',allergen:'无',status:'上架',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',
composition:[{name:'时令青菜',cat:'蔬菜类/叶菜',amount:120,pct:'80%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'蒜末',cat:'调味品/香辛料',amount:5,pct:'3.3%',state:'净菜',main:false,allergen:'无',note:'—'},{name:'食用油',cat:'油脂类/植物油',amount:8,pct:'5.3%',state:'原料',main:false,allergen:'无',note:'—'},{name:'食盐',cat:'调味品/盐类',amount:2,pct:'1.3%',state:'原料',main:false,allergen:'无',note:'—'}],
nutri:{calorie:68,protein:2.1,carb:8.6,fat:3.2,vitC:28.5,mineral:86,fiber:2.4,sodium:186,note:'铁1.2mg、钙45mg'},
cook:{mainMethod:'炒',subMethod:'—',heat:'大火',duration:5,oil:8,salt:2,temp:'200',output:150,steps:4},
taste:{mainFlavor:'淡味',subFlavor:'清香',intensity:'弱',desc:'清淡爽口,蔬菜本味,微有蒜香',crowd:['全人群'],level:'推荐',taboo:''},
color:{mainColor:'翠绿色',desc:'翠绿鲜亮,色泽均匀,叶片完整',cause:'叶绿素保留,大火快炒',grade:'优',crowd:['全人群'],level:'推荐',note:''},
texture:{mainTexture:'嫩脆',chew:'易',digest:'易',desc:'清脆爽口,纤维感适中,汁水丰富',crowd:['全人群','老年人'],level:'推荐',suggest:'老年人可炒至更软'}
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,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'上架',
composition:[{name:'番茄',cat:'蔬菜类',amount:150,pct:'50%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'鸡蛋',cat:'蛋类',amount:60,pct:'20%',state:'原料',main:true,allergen:'含蛋类',note:'—'},{name:'食用油',cat:'油脂类',amount:5,pct:'1.7%',state:'原料',main:false,allergen:'无',note:'—'},{name:'食盐',cat:'调',amount:2,pct:'0.7%',state:'原料',main:false,allergen:'无',note:'—'}],
nutri:{calorie:45,protein:3.8,fat:2.1,carb:4.2,sodium:280,gi:35,giLevel:'低GI',gl:4.8,si:128,orac:1240,purine:42,cholesterol:186,transFat:0,pyramid:'蔬菜+蛋类'},
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{name:'标准份',weight:300,price:5,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'上架',crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',
composition:[{name:'番茄',cat:'蔬菜类/茄果',amount:150,pct:'50%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'鸡蛋',cat:'蛋类/禽蛋',amount:60,pct:'20%',state:'原料',main:true,allergen:'含蛋类',note:'—'},{name:'食用油',cat:'油脂类/植物油',amount:5,pct:'1.7%',state:'原料',main:false,allergen:'无',note:'—'},{name:'食盐',cat:'调味品/盐类',amount:2,pct:'0.7%',state:'原料',main:false,allergen:'无',note:'—'}],
nutri:{calorie:45,protein:3.8,carb:4.2,fat:2.1,vitC:18.2,mineral:32,fiber:1.1,sodium:280,note:'铁0.8mg、钾210mg'},
cook:{mainMethod:'煮',subMethod:'炒',heat:'中火',duration:12,oil:5,salt:2,temp:'100',output:300,steps:5},
taste:{mainFlavor:'酸鲜',subFlavor:'淡咸',intensity:'中',desc:'酸甜开胃,汤鲜味美,口感清爽',crowd:['全人群','儿童'],level:'推荐',taboo:''},
color:{mainColor:'橙红色',desc:'汤色橙红,蛋花嫩黄,色彩鲜艳',cause:'番茄红素,蛋黄色素',grade:'优',crowd:['全人群','儿童'],level:'推荐',note:''},
texture:{mainTexture:'嫩滑',chew:'易',digest:'易',desc:'汤汁鲜美,蛋花嫩滑,易于消化',crowd:['全人群','婴幼儿','术后恢复'],level:'推荐',suggest:''}
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,isDefault:true},{name:'小份',weight:100,price:2},{name:'大份',weight:300,price:4}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'上架',
composition:[{name:'大米',cat:'谷薯类',amount:100,pct:'100%',state:'原料',main:true,allergen:'无',note:'—'}],
nutri:{calorie:232,protein:4.8,fat:0.6,carb:51.2,sodium:3,gi:83,giLevel:'高GI',gl:33.2,si:52,orac:180,purine:0,cholesterol:0,transFat:0,pyramid:'谷薯杂豆'},
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{name:'标准份',weight:200,price:3,isDefault:true},{name:'小份',weight:100,price:2},{name:'大份',weight:300,price:4}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'上架',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',
composition:[{name:'大米',cat:'谷薯类/稻米',amount:100,pct:'100%',state:'原料',main:true,allergen:'无',note:'—'}],
nutri:{calorie:232,protein:4.8,carb:51.2,fat:0.6,vitC:0,mineral:12,fiber:0.4,sodium:3,note:''},
cook:{mainMethod:'蒸',subMethod:'—',heat:'大火→中火',duration:30,oil:0,salt:0,temp:'100',output:200,steps:2},
taste:{mainFlavor:'淡味',subFlavor:'微甜',intensity:'弱',desc:'清淡微甜,米香自然,无刺激性',crowd:['全人群','糖尿病人群'],level:'适量',taboo:'糖尿病人群控制摄入量'},
color:{mainColor:'乳白色',desc:'洁白晶莹,颗粒饱满,有光泽',cause:'淀粉糊化,水分均匀',grade:'优',crowd:['全人群'],level:'推荐',note:''},
texture:{mainTexture:'软糯',chew:'易',digest:'易',desc:'颗粒分明,软糯适中,不粘不散',crowd:['全人群','老年人'],level:'推荐',suggest:'老年人可煮软一些'}
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,isDefault:true},{name:'大份',weight:280,price:20}],method:'炸',meal:'午',allergen:'无',status:'下架',
composition:[{name:'猪里脊肉',cat:'畜肉类',amount:120,pct:'66.7%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'淀粉',cat:'谷薯类',amount:20,pct:'11.1%',state:'原料',main:false,allergen:'无',note:'挂糊'},{name:'白糖',cat:'调',amount:25,pct:'13.9%',state:'原料',main:false,allergen:'无',note:'糖醋汁'},{name:'食醋',cat:'调',amount:15,pct:'8.3%',state:'原料',main:false,allergen:'无',note:'糖醋汁'}],
nutri:{calorie:420,protein:22.4,fat:18.6,carb:38.2,sodium:620,gi:68,giLevel:'中GI',gl:28.4,si:45,orac:380,purine:98,cholesterol:72,transFat:0.8,pyramid:'畜禽肉蛋水产'},
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{name:'标准份',weight:180,price:14,isDefault:true},{name:'大份',weight:280,price:20}],method:'炸',meal:'午',allergen:'无',status:'下架',crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',
composition:[{name:'猪里脊肉',cat:'肉禽类/猪肉',amount:120,pct:'66.7%',state:'净菜',main:true,allergen:'无',note:'—'},{name:'淀粉',cat:'谷薯类/淀粉',amount:20,pct:'11.1%',state:'原料',main:false,allergen:'无',note:'挂糊'},{name:'白糖',cat:'调味品/糖类',amount:25,pct:'13.9%',state:'原料',main:false,allergen:'无',note:'糖醋汁'},{name:'食醋',cat:'调味品/醋类',amount:15,pct:'8.3%',state:'原料',main:false,allergen:'无',note:'糖醋汁'}],
nutri:{calorie:420,protein:22.4,carb:38.2,fat:18.6,vitC:1.2,mineral:28,fiber:0.6,sodium:620,note:'铁1.8mg'},
cook:{mainMethod:'炸',subMethod:'炒',heat:'大火',duration:15,oil:50,salt:1,temp:'180',output:180,steps:7},
taste:{mainFlavor:'酸甜',subFlavor:'咸鲜',intensity:'强',desc:'酸甜浓郁,外酥里嫩,口感丰富',crowd:['成人','儿童','糖尿病人群'],level:'慎食',taboo:'糖尿病人群慎食,含糖量高'},
color:{mainColor:'金黄色',desc:'外皮金黄酥脆,色泽均匀,油亮',cause:'高温油炸,美拉德反应',grade:'优',crowd:['成人','老年人','儿童'],level:'适量',note:'油炸食品,老年人儿童适量'},
texture:{mainTexture:'外酥内嫩',chew:'中',digest:'中',desc:'外皮酥脆,内里嫩滑,层次丰富',crowd:['成人','老年人','婴幼儿'],level:'慎食',suggest:'婴幼儿不宜,油炸食品'}
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:'良好'}
}
];
@@ -333,6 +332,12 @@ function renderDishList(){
}
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(){
@@ -342,7 +347,7 @@ function renderDetail(){
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,renderColor,renderTexture,renderImages,renderVideos];
const renderers = [renderBasic,renderComposition,renderCook,renderNutri,renderTaste,renderTexture,renderColor];
body.innerHTML = renderers[currentTab](d);
}
@@ -351,7 +356,7 @@ function renderBasic(d){
var statusHtml = s.isDefault ? '<span class="tag tag-blue" style="font-size:11px;">默认</span>' : '';
return '<tr><td>'+s.name+'</td><td>'+s.weight+'</td><td>'+s.price.toFixed(2)+'</td><td>'+statusHtml+'</td></tr>';
}).join('');
return `<div class="info-section"><div class="info-section__title">基本信息 <span><span class="link">编辑</span></span></div>
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>
@@ -361,19 +366,32 @@ function renderBasic(d){
<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">上架状态</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></tr></thead><tbody>${specRows}</tbody></table></div>`;
<table class="info-table spec-detail-table"><thead><tr><th>规格</th><th>单位重量(g)</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.cat}</td><td>${c.amount}</td><td>${c.pct}</td><td>${c.state}</td><td><span class="tag tag-${c.main?'green':'gray'}">${c.main?'主料':'辅料'}</span></td><td>${c.allergen}</td><td>${c.note}</td><td><span class="link">编辑</span></td></tr>`).join('');
return `<div class="info-section"><div class="info-section__title">食材构成(共 ${(d.composition||[]).length} 项) <span><button class="btn btn-primary" style="height:28px;font-size:var(--font-xs);padding:0 10px;" onclick="openIngredientDialog()"> 新增食材</button></div>
<table class="info-table"><thead><tr><th>食材名称</th><th>食材分类</th><th>用量(g/份)</th><th>占比(%)</th><th>加工状态</th><th>是否主料</th><th>过敏原</th><th>备注</th><th>操作</th></tr></thead><tbody>${rows}</tbody></table></div>`;
const rows = (d.composition||[]).map(c=>`<tr><td>${c.name}</td><td>${c.cat}</td><td>${c.amount}</td><td>${c.pct}</td><td><span class="tag tag-${c.main?'green':'gray'}">${c.main?'主料':'辅料'}</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><span class="link">编辑</span></span></div>
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>
@@ -389,72 +407,71 @@ function renderCook(d){
function renderNutri(d){
const n = d.nutri;
return `<div class="info-section"><div class="info-section__title">营养指标 <span><span class="link">编辑</span></span></div>
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">热量</div><div class="info-cell__value">${n.calorie} kcal/份</div></div>
<div class="info-cell"><div class="info-cell__label">蛋白质</div><div class="info-cell__value">${n.protein}g</div></div>
<div class="info-cell"><div class="info-cell__label">脂肪</div><div class="info-cell__value">${n.fat}g</div></div>
<div class="info-cell"><div class="info-cell__label">碳水化合物</div><div class="info-cell__value">${n.carb}g</div></div>
<div class="info-cell"><div class="info-cell__label"></div><div class="info-cell__value">${n.sodium}mg</div></div>
<div class="info-cell"><div class="info-cell__label">GI值</div><div class="info-cell__value">${n.gi} <span class="tag tag-${n.giLevel==='低GI'?'green':n.giLevel==='中GI'?'orange':'red'}">${n.giLevel}</span></div></div>
<div class="info-cell"><div class="info-cell__label">GL值</div><div class="info-cell__value">${n.gl}</div></div>
<div class="info-cell"><div class="info-cell__label">SI值</div><div class="info-cell__value">${n.si}</div></div>
<div class="info-cell"><div class="info-cell__label">ORAC值</div><div class="info-cell__value">${n.orac}</div></div>
<div class="info-cell"><div class="info-cell__label">嘌呤</div><div class="info-cell__value">${n.purine}mg/100g</div></div>
<div class="info-cell"><div class="info-cell__label">胆固醇</div><div class="info-cell__value">${n.cholesterol}mg/份</div></div>
<div class="info-cell"><div class="info-cell__label">反式脂肪</div><div class="info-cell__value">${n.transFat}g/份</div></div>
<div class="info-cell"><div class="info-cell__label">膳食宝塔分类</div><div class="info-cell__value">${n.pyramid}</div></div>
<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></div>`;
}
function renderTaste(d){
const t = d.taste;
return `<div class="info-section"><div class="info-section__title">味道档案 <span><span class="link">编辑</span></span></div>
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">主味型</div><div class="info-cell__value">${t.mainFlavor}</div></div>
<div class="info-cell"><div class="info-cell__label">辅味型</div><div class="info-cell__value">${t.subFlavor}</div></div>
<div class="info-cell"><div class="info-cell__label">味道强度</div><div class="info-cell__value">${t.intensity}</div></div>
<div class="info-cell"><div class="info-cell__label">味道描述</div><div class="info-cell__value">${t.desc}</div></div>
<div class="info-cell"><div class="info-cell__label">适配人群</div><div class="info-cell__value">${t.crowd.map(c=>'<span class="tag tag-green">'+c+'</span>').join('')}</div></div>
<div class="info-cell"><div class="info-cell__label">适配等级</div><div class="info-cell__value"><span class="tag tag-${t.level==='推荐'?'green':t.level==='适量'?'orange':'red'}">${t.level}</span></div></div>
<div class="info-cell"><div class="info-cell__label">饮食禁忌说明</div><div class="info-cell__value">${t.taboo}</div></div>
<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;
return `<div class="info-section"><div class="info-section__title">色泽档案 <span><span class="link">编辑</span></span></div>
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">色泽描述</div><div class="info-cell__value">${c.desc}</div></div>
<div class="info-cell"><div class="info-cell__label">色泽成因</div><div class="info-cell__value">${c.cause}</div></div>
<div class="info-cell"><div class="info-cell__label">感官评级</div><div class="info-cell__value">${c.grade}</div></div>
<div class="info-cell"><div class="info-cell__label">适配人群</div><div class="info-cell__value">${c.crowd.map(x=>'<span class="tag tag-green">'+x+'</span>').join('')}</div></div>
<div class="info-cell"><div class="info-cell__label">适配等级</div><div class="info-cell__value"><span class="tag tag-${c.level==='推荐'?'green':c.level==='适量'?'orange':'red'}">${c.level}</span></div></div>
<div class="info-cell"><div class="info-cell__label">备注</div><div class="info-cell__value">${c.note}</div></div>
<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;
return `<div class="info-section"><div class="info-section__title">口感档案 <span><span class="link">编辑</span></span></div>
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">主质地</div><div class="info-cell__value">${t.mainTexture}</div></div>
<div class="info-cell"><div class="info-cell__label">咀嚼难度</div><div class="info-cell__value">${t.chew}</div></div>
<div class="info-cell"><div class="info-cell__label">消化难度</div><div class="info-cell__value">${t.digest}</div></div>
<div class="info-cell"><div class="info-cell__label">口感描述</div><div class="info-cell__value">${t.desc}</div></div>
<div class="info-cell"><div class="info-cell__label">适配人群</div><div class="info-cell__value">${t.crowd.map(x=>'<span class="tag tag-green">'+x+'</span>').join('')}</div></div>
<div class="info-cell"><div class="info-cell__label">适配等级</div><div class="info-cell__value"><span class="tag tag-${t.level==='推荐'?'green':t.level==='适量'?'orange':'red'}">${t.level}</span></div></div>
<div class="info-cell"><div class="info-cell__label">烹饪建议</div><div class="info-cell__value">${t.suggest}</div></div>
<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__title">主图 - 正面展示</div><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__title">摆盘图 - 精美展示</div><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__title">制作过程</div><div class="media-card__meta">800×600 · 96KB · <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 · 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>`;
}
+753
View File
@@ -0,0 +1,753 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>营养管理 - 新增食物 | 健康长庆原型</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);}
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;}
.breadcrumb a{color:var(--primary);text-decoration:none;}.breadcrumb a:hover{text-decoration:underline;}
.breadcrumb__sep{color:var(--text-placeholder);}
/* 选项卡 */
.steps{display:flex;align-items:stretch;margin-bottom:24px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.step{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;padding:14px 12px;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s;user-select:none;}
.step:hover{background:var(--bg-secondary);}
.step__num{width:24px;height:24px;border-radius:50%;border:2px solid var(--border);background:#fff;display:flex;align-items:center;justify-content:center;font-size:var(--font-xs);font-weight:600;color:var(--text-placeholder);transition:all .2s;flex-shrink:0;}
.step__label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;transition:all .2s;}
.step.active{border-bottom-color:var(--primary);background:var(--primary-light,#e6f7ff);}
.step.active .step__num{border-color:var(--primary);background:var(--primary);color:#fff;}
.step.active .step__label{color:var(--primary);font-weight:600;}
/* 表单卡片 */
.form-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:24px;margin-bottom:16px;}
.form-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:20px;padding-bottom:12px;border-bottom:1px solid var(--border);}
.form-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px 24px;}
.form-group{display:flex;flex-direction:column;gap:6px;}
.form-group.full{grid-column:1/-1;}
.form-label{font-size:var(--font-sm);color:var(--text-secondary);}
.form-label .required{color:var(--danger);margin-left:2px;}
.form-input,.form-select,.form-textarea{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;width:100%;}
.form-input:focus,.form-select:focus,.form-textarea:focus{border-color:var(--primary);}
.form-textarea{height:80px;padding:8px 12px;resize:vertical;}
.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;}
.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);}
.btn-lg{height:40px;padding:0 24px;font-size:var(--font-base);}
.btn[disabled]{opacity:0.5;cursor:not-allowed;pointer-events:none;}
.form-footer{display:flex;align-items:center;gap:12px;padding:16px 24px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
/* 子标题 */
.form-subtitle{font-size:var(--font-sm);font-weight:500;color:var(--text-secondary);margin:16px 0 12px;padding-top:12px;border-top:1px dashed #f0f0f0;}
.form-subtitle:first-child{margin-top:0;padding-top:0;border-top:none;}
/* 标签组 */
.tag-group{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}
.tag-item{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;background:var(--primary-light);color:var(--primary);border-radius:14px;font-size:var(--font-xs);}
.tag-item__del{cursor:pointer;font-size:14px;line-height:1;opacity:0.6;margin-left:2px;}.tag-item__del:hover{opacity:1;color:var(--danger);}
.tag-input-row{display:flex;gap:8px;align-items:center;margin-top:8px;}
.tag-input-row .form-input{flex:1;height:32px;}
.tag-input-row .btn{height:32px;padding:0 12px;font-size:var(--font-xs);}
/* 级联选择器 */
.cascade-picker{position:relative;width:100%;}
.cascade-trigger{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);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;cursor:pointer;display:flex;align-items:center;white-space:nowrap;color:var(--text-placeholder);width:100%;}
.cascade-trigger.has-value{color:var(--text-primary);}
.cascade-panel{display:none;position:fixed;z-index:200;background:#fff;border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-md);width:340px;}
.cascade-panel.show{display:flex;}
.cascade-col{width:50%;max-height:240px;overflow-y:auto;}
.cascade-col+.cascade-col{border-left:1px solid #f0f0f0;}
.cascade-col__title{font-size:var(--font-xs);color:var(--text-placeholder);padding:8px 12px;border-bottom:1px solid #f0f0f0;position:sticky;top:0;background:#fff;}
.cascade-opt{padding:7px 12px;font-size:var(--font-xs);cursor:pointer;transition:background .15s;display:flex;align-items:center;justify-content:space-between;}
.cascade-opt:hover{background:#F8FBFF;}
.cascade-opt.active{background:var(--primary-light);color:var(--primary);font-weight:500;}
.cascade-opt .arrow-r{font-size:10px;color:var(--text-placeholder);}
/* 图片上传 */
.upload-box{width:120px;height:120px;border:1px dashed var(--border);border-radius:var(--radius-md);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;cursor:pointer;transition:all .2s;position:relative;overflow:hidden;}
.upload-box:hover{border-color:var(--primary);background:var(--primary-light);}
.upload-box__icon{font-size:28px;color:var(--text-placeholder);}
.upload-box__text{font-size:var(--font-xs);color:var(--text-placeholder);}
.upload-box__preview{position:absolute;inset:0;object-fit:cover;width:100%;height:100%;display:none;}
.upload-box__preview.show{display:block;}
.upload-box__remove{position:absolute;top:4px;right:4px;width:20px;height:20px;border-radius:50%;background:rgba(0,0,0,0.5);color:#fff;font-size:12px;display:none;align-items:center;justify-content:center;cursor:pointer;z-index:2;}
.upload-box__remove.show{display:flex;}
/* 动态行 */
.dynamic-rows{display:flex;flex-direction:column;gap:10px;}
.dynamic-row{display:flex;align-items:center;gap:12px;}
.dynamic-row .form-input,.dynamic-row .form-select{flex:1;height:32px;}
.dynamic-row__del{width:24px;height:24px;border:none;background:none;color:var(--text-placeholder);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all .15s;flex-shrink:0;}
.dynamic-row__del:hover{color:var(--danger);background:#FFF1F0;}
.dynamic-row-add{display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border:1px dashed var(--primary);color:var(--primary);border-radius:var(--radius-sm);font-size:var(--font-xs);cursor:pointer;background:none;margin-top:8px;transition:all .2s;}
.dynamic-row-add:hover{background:var(--primary-light);}
</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 active"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.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>
<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="breadcrumb">
<a href="din-ingredient.html">食物成分</a>
<span class="breadcrumb__sep">/</span>
<span id="breadcrumbTitle">新增食物</span>
</div>
<!-- 选项卡 -->
<div class="steps">
<div class="step active" onclick="switchTab(0)"><span class="step__num">1</span><span class="step__label">基本信息</span></div>
<div class="step" onclick="switchTab(1)"><span class="step__num">2</span><span class="step__label">营养成分</span></div>
<div class="step" onclick="switchTab(2)"><span class="step__num">3</span><span class="step__label">味道档案</span></div>
<div class="step" onclick="switchTab(3)"><span class="step__num">4</span><span class="step__label">口感档案</span></div>
<div class="step" onclick="switchTab(4)"><span class="step__num">5</span><span class="step__label">色泽档案</span></div>
</div>
<!-- 表单区域 -->
<div class="form-card" id="stepContent">
<!-- JS动态渲染 -->
</div>
<!-- 底部操作 -->
<div class="form-footer">
<button class="btn btn-default btn-lg" id="btnPrev" onclick="prevStep()" style="display:none;">上一步</button>
<button class="btn btn-primary btn-lg" id="btnNext" onclick="nextStep()">下一步</button>
<button class="btn btn-primary btn-lg" id="btnSubmit" onclick="submitForm()" style="display:none;">提交</button>
<a href="din-ingredient.html" class="btn btn-default btn-lg">取消</a>
</div>
</main>
<script>
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
var currentStep = 0;
var totalSteps = 5;
var STEP_HTML = [
// Step 0: 基本信息
`<div class="form-card__title">📋 基本信息</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">食物编码<span class="required">*</span></label><input class="form-input" id="foodCode" placeholder="系统自动生成(分类+自编码)" readonly style="background:#f5f5f5;cursor:not-allowed;"></div>
<div class="form-group"><label class="form-label">食物名称<span class="required">*</span></label><input class="form-input" id="foodName" placeholder="请输入食物名称"></div>
<div class="form-group"><label class="form-label">别名</label><input class="form-input" id="foodAlias" placeholder="请输入别名"></div>
<div class="form-group"><label class="form-label">食物分类<span class="required">*</span></label>
<div class="cascade-picker" id="addCategoryPicker">
<div class="cascade-trigger" id="addCascadeTrigger" onclick="toggleAddCascade()">请选择食物分类</div>
<div class="cascade-panel" id="addCascadePanel">
<div class="cascade-col" id="addCascadeL1"></div>
<div class="cascade-col" id="addCascadeL2"></div>
</div>
</div>
</div>
<div class="form-group"><label class="form-label">可食部分(%)</label><input class="form-input" id="edible" placeholder="0~100"></div>
<div class="form-group"><label class="form-label">水分(%)</label><input class="form-input" id="waterRate" placeholder="0~100"></div>
<div class="form-group"><label class="form-label">数据来源</label><select class="form-select" id="dataSource"><option value="1">手动添加</option></select></div>
<div class="form-group full"><label class="form-label">净菜类型</label>
<div class="tag-group" id="vegTypeGroup"></div>
<div class="tag-input-row">
<input class="form-input" id="vegTypeInput" placeholder="输入净菜类型名称">
<button type="button" class="btn btn-primary" onclick="addVegType()">添加</button>
</div>
</div>
<div class="form-group"><label class="form-label">食材图片</label>
<div class="upload-box" id="uploadBox" onclick="triggerUpload()">
<input type="file" id="fileInput" accept="image/*" style="display:none;" onchange="previewImage(event)">
<img class="upload-box__preview" id="imgPreview">
<span class="upload-box__remove" id="imgRemove" onclick="removeImage(event)">×</span>
<span class="upload-box__icon">📷</span>
<span class="upload-box__text">点击上传</span>
</div>
</div>
</div>
<div class="form-subtitle">👥 适配人群</div>
<div class="dynamic-rows" id="crowdRows"></div>
<button type="button" class="dynamic-row-add" onclick="addCrowdRow()">+ 添加人群</button>`,
// Step 1: 营养成分
`<div class="form-card__title">🧪 营养成分(每100g可食部)</div>
<div class="form-subtitle">宏量营养素</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">能量(kcal)<span class="required">*</span></label><input class="form-input" id="calorie" placeholder="0"></div>
<div class="form-group"><label class="form-label">蛋白质(g)<span class="required">*</span></label><input class="form-input" id="protein" placeholder="0"></div>
<div class="form-group"><label class="form-label">脂肪(g)<span class="required">*</span></label><input class="form-input" id="fat" placeholder="0"></div>
<div class="form-group"><label class="form-label">碳水化合物(g)<span class="required">*</span></label><input class="form-input" id="carbohydrate" placeholder="0"></div>
<div class="form-group"><label class="form-label">膳食纤维(g)</label><input class="form-input" id="dietary_fiber" placeholder="0"></div>
</div>
<div class="form-subtitle">维生素</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">维生素A(μg RAE)</label><input class="form-input" id="vitamin_a" placeholder="0"></div>
<div class="form-group"><label class="form-label">硫胺素/B1(mg)</label><input class="form-input" id="vitamin_b1" placeholder="0"></div>
<div class="form-group"><label class="form-label">核黄素/B2(mg)</label><input class="form-input" id="vitamin_b2" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素B6(mg)</label><input class="form-input" id="vitamin_b6" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素B12(μg)</label><input class="form-input" id="vitamin_b12" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素C(mg)</label><input class="form-input" id="vitamin_c" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素D(μg)</label><input class="form-input" id="vitamin_d" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素E(mg α-TE)</label><input class="form-input" id="vitamin_e" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素K(μg)</label><input class="form-input" id="vitamin_k" placeholder="0"></div>
<div class="form-group"><label class="form-label">烟酸(mg)</label><input class="form-input" id="niacin" placeholder="0"></div>
<div class="form-group"><label class="form-label">叶酸(μg)</label><input class="form-input" id="folic_acid" placeholder="0"></div>
<div class="form-group"><label class="form-label">生物素(μg)</label><input class="form-input" id="biotin" placeholder="0"></div>
<div class="form-group"><label class="form-label">泛酸(mg)</label><input class="form-input" id="pantothenic_acid" placeholder="0"></div>
<div class="form-group"><label class="form-label">总胆碱(mg)</label><input class="form-input" id="total_choline" placeholder="0"></div>
</div>
<div class="form-subtitle">矿物质</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">钙(mg)</label><input class="form-input" id="ca" placeholder="0"></div>
<div class="form-group"><label class="form-label">磷(mg)</label><input class="form-input" id="phosphorus" placeholder="0"></div>
<div class="form-group"><label class="form-label">钾(mg)</label><input class="form-input" id="kalium" placeholder="0"></div>
<div class="form-group"><label class="form-label">镁(mg)</label><input class="form-input" id="mg_el" placeholder="0"></div>
<div class="form-group"><label class="form-label">钠(mg)</label><input class="form-input" id="na" placeholder="0"></div>
<div class="form-group"><label class="form-label">铁(mg)</label><input class="form-input" id="fe" placeholder="0"></div>
<div class="form-group"><label class="form-label">锌(mg)</label><input class="form-input" id="zn" placeholder="0"></div>
<div class="form-group"><label class="form-label">硒(μg)</label><input class="form-input" id="se" placeholder="0"></div>
<div class="form-group"><label class="form-label">铜(mg)</label><input class="form-input" id="cu" placeholder="0"></div>
<div class="form-group"><label class="form-label">锰(mg)</label><input class="form-input" id="mn" placeholder="0"></div>
<div class="form-group"><label class="form-label">碘(μg)</label><input class="form-input" id="iodine" placeholder="0"></div>
</div>
<div class="form-subtitle">脂质指标</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">胆固醇(mg)</label><input class="form-input" id="cholesterol" placeholder="0"></div>
<div class="form-group"><label class="form-label">饱和脂肪酸(g)</label><input class="form-input" id="sfa" placeholder="0"></div>
<div class="form-group"><label class="form-label">单不饱和脂肪酸(g)</label><input class="form-input" id="mufa" placeholder="0"></div>
<div class="form-group"><label class="form-label">多不饱和脂肪酸(g)</label><input class="form-input" id="pufa" placeholder="0"></div>
<div class="form-group"><label class="form-label">DHA(mg)</label><input class="form-input" id="dha" placeholder="0"></div>
</div>
<div class="form-subtitle">氨基酸</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">异亮氨酸(mg)</label><input class="form-input" id="ile" placeholder="0"></div>
<div class="form-group"><label class="form-label">亮氨酸(mg)</label><input class="form-input" id="leu" placeholder="0"></div>
<div class="form-group"><label class="form-label">酪氨酸(mg)</label><input class="form-input" id="tyr" placeholder="0"></div>
<div class="form-group"><label class="form-label">赖氨酸(mg)</label><input class="form-input" id="lys" placeholder="0"></div>
<div class="form-group"><label class="form-label">蛋氨酸(mg)</label><input class="form-input" id="met" placeholder="0"></div>
<div class="form-group"><label class="form-label">苯丙氨酸(mg)</label><input class="form-input" id="phe" placeholder="0"></div>
<div class="form-group"><label class="form-label">苏氨酸(mg)</label><input class="form-input" id="thr" placeholder="0"></div>
<div class="form-group"><label class="form-label">色氨酸(mg)</label><input class="form-input" id="trp" placeholder="0"></div>
<div class="form-group"><label class="form-label">缬氨酸(mg)</label><input class="form-input" id="val" placeholder="0"></div>
<div class="form-group"><label class="form-label">蛋氨酸+胱氨酸(mg)</label><input class="form-input" id="met_cys" placeholder="0"></div>
<div class="form-group"><label class="form-label">苯丙氨酸+酪氨酸(mg)</label><input class="form-input" id="phe_tyr" placeholder="0"></div>
<div class="form-group"><label class="form-label">胱氨酸(mg)</label><input class="form-input" id="cys" placeholder="0"></div>
</div>
<div class="form-subtitle">特殊指标</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">GI</label><input class="form-input" id="gi" placeholder="0"></div>
<div class="form-group"><label class="form-label">总嘌呤(mg)</label><input class="form-input" id="purine" placeholder="0"></div>
<div class="form-group"><label class="form-label">鸟嘌呤(mg)</label><input class="form-input" id="guanine" placeholder="0"></div>
<div class="form-group"><label class="form-label">腺嘌呤(mg)</label><input class="form-input" id="adenine" placeholder="0"></div>
<div class="form-group"><label class="form-label">次黄嘌呤(mg)</label><input class="form-input" id="hypoxanthine" placeholder="0"></div>
<div class="form-group"><label class="form-label">黄嘌呤(mg)</label><input class="form-input" id="xanthine" placeholder="0"></div>
</div>`,
// Step 2: 味道档案
`<div class="form-card__title">👅 味道档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主味型</label><input class="form-input" id="tasteMain" placeholder="如:淡味、鲜味、甜味"></div>
<div class="form-group"><label class="form-label">辅味型</label><input class="form-input" id="tasteSub" placeholder="如:微涩、豆香"></div>
<div class="form-group"><label class="form-label">味道强度</label><select class="form-select" id="tasteIntensity"><option value="">请选择</option><option>弱</option><option>中</option><option>强</option></select></div>
<div class="form-group full"><label class="form-label">味道描述</label><textarea class="form-textarea" id="tasteDesc" placeholder="请描述味道特征"></textarea></div>
</div>`,
// Step 3: 口感档案
`<div class="form-card__title">🫦 口感档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主质地</label><input class="form-input" id="textureMain" placeholder="如:软糯、嫩滑、细腻"></div>
<div class="form-group"><label class="form-label">咀嚼难度</label><select class="form-select" id="chewDiff"><option value="">请选择</option><option>易</option><option>中</option><option>难</option></select></div>
<div class="form-group"><label class="form-label">消化难度</label><select class="form-select" id="digestDiff"><option value="">请选择</option><option>易</option><option>中</option><option>难</option></select></div>
<div class="form-group full"><label class="form-label">口感描述</label><textarea class="form-textarea" id="textureDesc" placeholder="请描述口感特征"></textarea></div>
<div class="form-group full"><label class="form-label">烹饪建议</label><textarea class="form-textarea" id="cookAdvice" placeholder="请输入烹饪建议"></textarea></div>
</div>`,
// Step 4: 色泽档案
`<div class="form-card__title">🎨 色泽档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主色系</label><input class="form-input" id="colorMain" placeholder="如:白色、深绿色"></div>
<div class="form-group"><label class="form-label">感官评级</label><select class="form-select" id="colorGrade"><option value="">请选择</option><option>优</option><option>良</option><option>中</option><option>差</option></select></div>
<div class="form-group full"><label class="form-label">色泽描述</label><textarea class="form-textarea" id="colorDesc" placeholder="请描述色泽特征,如:乳白色,半透明,颗粒饱满"></textarea></div>
<div class="form-group full"><label class="form-label">色泽成因</label><textarea class="form-textarea" id="colorCause" placeholder="请描述色泽形成原因"></textarea></div>
</div>`
];
/* 表单数据暂存 */
var formData = {};
/* 净菜类型数据 */
var vegTypes = [];
/* 适配人群数据 */
var crowdList = [];
/* 图片数据 */
var imageData = '';
function saveCurrentStep(){
var inputs = document.querySelectorAll('#stepContent .form-input, #stepContent .form-select, #stepContent .form-textarea');
inputs.forEach(function(el){if(el.id) formData[el.id]=el.value;});
// 保存动态行数据
if(currentStep===0){
var rows = document.querySelectorAll('#crowdRows .dynamic-row');
crowdList = [];
rows.forEach(function(row){
var name = row.querySelector('.crowd-name');
var level = row.querySelector('.crowd-level');
if(name && level) crowdList.push({name:name.value,level:level.value});
});
}
}
function restoreCurrentStep(){
var inputs = document.querySelectorAll('#stepContent .form-input, #stepContent .form-select, #stepContent .form-textarea');
inputs.forEach(function(el){if(el.id && formData[el.id]!==undefined) el.value=formData[el.id];});
}
function renderStep(){
document.getElementById('stepContent').innerHTML = STEP_HTML[currentStep];
restoreCurrentStep();
if(currentStep===0){
renderVegTypes();
renderCrowdRows();
renderImagePreview();
}
updateStepUI();
}
function updateStepUI(){
var steps = document.querySelectorAll('.step');
steps.forEach(function(el,i){
el.classList.toggle('active',i===currentStep);
});
var btnSubmit = document.getElementById('btnSubmit');
if(pageMode==='edit'){
document.getElementById('btnPrev').style.display = 'none';
document.getElementById('btnNext').style.display = 'none';
btnSubmit.textContent = '保存修改';
btnSubmit.style.display = 'inline-flex';
} else {
document.getElementById('btnPrev').style.display = currentStep>0?'inline-flex':'none';
document.getElementById('btnNext').style.display = currentStep<totalSteps-1?'inline-flex':'none';
btnSubmit.textContent = '提交';
btnSubmit.style.display = currentStep===totalSteps-1?'inline-flex':'none';
}
}
function switchTab(idx){
if(idx===currentStep) return;
saveCurrentStep();
currentStep = idx;
renderStep();
window.scrollTo(0,0);
}
function nextStep(){
if(currentStep>=totalSteps-1) return;
saveCurrentStep();
currentStep++;
renderStep();
window.scrollTo(0,0);
}
function prevStep(){
if(currentStep<=0) return;
saveCurrentStep();
currentStep--;
renderStep();
window.scrollTo(0,0);
}
function submitForm(){
saveCurrentStep();
if(pageMode==='edit'){
alert('修改已保存!');
} else {
alert('食物信息已提交成功!');
}
window.location.href='din-ingredient.html';
}
/* === 净菜类型 === */
function renderVegTypes(){
var group = document.getElementById('vegTypeGroup');
if(!group) return;
var html = '';
vegTypes.forEach(function(t,i){
html += '<span class="tag-item">'+t+'<span class="tag-item__del" onclick="removeVegType('+i+')">×</span></span>';
});
group.innerHTML = html;
}
function addVegType(){
var input = document.getElementById('vegTypeInput');
if(!input) return;
var val = input.value.trim();
if(val){
vegTypes.push(val);
input.value = '';
renderVegTypes();
}
}
function removeVegType(idx){
vegTypes.splice(idx,1);
renderVegTypes();
}
/* === 食物分类级联选择器 === */
var ADD_CATEGORIES={'谷薯类':['稻米','小麦','玉米','薯类','杂粮'],'畜禽肉类':['猪肉','牛肉','羊肉','鸡肉','鸭肉'],'水产类':['淡水鱼','海水鱼','虾蟹','贝类'],'蛋类':['鸡蛋','鸭蛋','鹅蛋','鹌鹑蛋'],'奶及奶制品':['牛奶','酸奶','奶酪','奶粉'],'豆及豆制品':['大豆','豆制品','杂豆'],'蔬菜类':['叶菜类','根茎类','瓜果类','菌菇类','豆荚类'],'水果类':['仁果类','浆果类','柑橘类','瓜果类','热带水果'],'坚果种子类':['坚果','种子'],'油脂类':['植物油','动物油'],'调味品类':['酱料','香辛料','调味汁']};
var addCascadeL1Active='';
var selectedCategory={l1:'',l2:''};
function toggleAddCascade(){
var panel=document.getElementById('addCascadePanel');
var open=!panel.classList.contains('show');
if(open){
var rect=document.getElementById('addCascadeTrigger').getBoundingClientRect();
panel.style.left=rect.left+'px';
panel.style.top=(rect.bottom+4)+'px';
addCascadeL1Active=selectedCategory.l1||'';
renderAddCascadeL1();
if(addCascadeL1Active) renderAddCascadeL2(addCascadeL1Active);
else document.getElementById('addCascadeL2').innerHTML='';
}
panel.classList.toggle('show',open);
}
function renderAddCascadeL1(){
var col=document.getElementById('addCascadeL1');
var html='<div class="cascade-col__title">一级分类</div>';
for(var k in ADD_CATEGORIES){
html+='<div class="cascade-opt'+(addCascadeL1Active===k?' active':'')+'" onmouseenter="hoverAddL1(\''+k+'\')" onclick="selectAddCascade(\''+k+'\',\'\')">'+k+'<span class="arrow-r"></span></div>';
}
col.innerHTML=html;
}
function renderAddCascadeL2(l1){
var col=document.getElementById('addCascadeL2');
if(!l1){col.innerHTML='';return;}
var subs=ADD_CATEGORIES[l1]||[];
var html='<div class="cascade-col__title">二级分类</div>';
subs.forEach(function(s){
html+='<div class="cascade-opt" onclick="selectAddCascade(\''+l1+'\',\''+s+'\')">'+s+'</div>';
});
col.innerHTML=html;
}
function hoverAddL1(l1){
addCascadeL1Active=l1;
document.querySelectorAll('#addCascadeL1 .cascade-opt').forEach(function(el){
el.classList.toggle('active',el.textContent.replace('','').trim()===l1);
});
renderAddCascadeL2(l1);
}
function selectAddCascade(l1,l2){
selectedCategory.l1=l1;
selectedCategory.l2=l2;
var trig=document.getElementById('addCascadeTrigger');
if(!l2){trig.textContent=l1;}
else{trig.textContent=l1+' / '+l2;}
trig.classList.add('has-value');
document.getElementById('addCascadePanel').classList.remove('show');
}
document.addEventListener('click',function(e){
if(!e.target.closest('#addCategoryPicker')){
var p=document.getElementById('addCascadePanel');
if(p) p.classList.remove('show');
}
});
/* === 图片上传 === */
function triggerUpload(){
if(imageData) return;
document.getElementById('fileInput').click();
}
function previewImage(e){
var file = e.target.files[0];
if(!file) return;
var reader = new FileReader();
reader.onload = function(ev){
imageData = ev.target.result;
renderImagePreview();
};
reader.readAsDataURL(file);
}
function renderImagePreview(){
var preview = document.getElementById('imgPreview');
var remove = document.getElementById('imgRemove');
if(!preview) return;
if(imageData){
preview.src = imageData;
preview.classList.add('show');
remove.classList.add('show');
} else {
preview.classList.remove('show');
remove.classList.remove('show');
}
}
function removeImage(e){
e.stopPropagation();
imageData = '';
document.getElementById('fileInput').value = '';
renderImagePreview();
}
/* === 适配人群动态行 === */
var CROWD_OPTIONS=['成人','老年人','儿童','婴幼儿','孕妇','糖尿病人群','高血压人群','高血脂人群','肾病人群','痛风人群','肾结石人群','术后恢复','全人群'];
function renderCrowdRows(){
var container = document.getElementById('crowdRows');
if(!container) return;
if(crowdList.length===0) crowdList.push({name:'',level:''});
var html = '';
crowdList.forEach(function(item,i){
html += '<div class="dynamic-row">';
html += '<select class="form-select crowd-name"><option value="">请选择人群</option>';
CROWD_OPTIONS.forEach(function(opt){html+='<option'+(item.name===opt?' selected':'')+'>'+opt+'</option>';});
html += '</select>';
html += '<select class="form-select crowd-level"><option value="">适配等级</option><option'+(item.level==='推荐'?' selected':'')+'>推荐</option><option'+(item.level==='适量'?' selected':'')+'>适量</option><option'+(item.level==='慎食'?' selected':'')+'>慎食</option></select>';
html += '<button type="button" class="dynamic-row__del" onclick="removeCrowdRow('+i+')" title="删除">×</button>';
html += '</div>';
});
container.innerHTML = html;
}
function addCrowdRow(){
saveCrowdRows();
crowdList.push({name:'',level:''});
renderCrowdRows();
}
function removeCrowdRow(idx){
saveCrowdRows();
crowdList.splice(idx,1);
if(crowdList.length===0) crowdList.push({name:'',level:''});
renderCrowdRows();
}
function saveCrowdRows(){
var rows = document.querySelectorAll('#crowdRows .dynamic-row');
crowdList = [];
rows.forEach(function(row){
var name = row.querySelector('.crowd-name');
var level = row.querySelector('.crowd-level');
if(name && level) crowdList.push({name:name.value,level:level.value});
});
}
/* === 编辑模式支持 === */
var urlParams = new URLSearchParams(window.location.search);
var pageMode = urlParams.get('mode') || 'add';
var editCode = urlParams.get('code') || '';
var initTab = parseInt(urlParams.get('tab')) || 0;
if(initTab >= 0 && initTab < totalSteps) currentStep = initTab;
/* 编辑模式 mock 数据(与 din-ingredient.html 的 FOODS 结构一致) */
var EDIT_FOODS = {
'0101001':{name:'粳米(生)',code:'0101001',alias:'大米',firstClass:'谷薯类',secondClass:'稻米',edible:'97',waterRate:'13.3',dataSource:'0',vegTypes:['洗净米','免淘米'],
crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],
nutri:{calorie:346,protein:7.4,fat:0.6,carbohydrate:77.2,dietary_fiber:0.7,
vitamin_a:0,vitamin_b1:0.11,vitamin_b2:0.02,vitamin_b6:0.12,vitamin_b12:0,vitamin_c:0,vitamin_d:0,vitamin_e:1.01,vitamin_k:0,niacin:1.6,folic_acid:3.8,biotin:0,pantothenic_acid:0.6,total_choline:0,
ca:11,phosphorus:121,kalium:97,mg:34,na:2.4,fe:1.1,zn:1.45,se:2.23,cu:0.14,mn:1.29,iodine:0,
cholesterol:0,sfa:0.2,mufa:0.2,pufa:0.2,dha:0,
ile:280,leu:570,tyr:290,lys:250,met:160,phe:370,thr:250,trp:90,val:420,met_cys:310,phe_tyr:660,cys:150,
gi:83,purine:34.4,guanine:8.4,adenine:5.5,hypoxanthine:18.4,xanthine:2.1},
color:{main:'乳白色',grade:'优',desc:'色泽洁白,半透明,颗粒饱满',cause:'碾磨去皮后淀粉体暴露'},
taste:{main:'淡味',sub:'微涩',intensity:'弱',desc:'生米无明显味道,煮熟后清香'},
texture:{main:'软糯',chew:'易',digest:'中',desc:'米粒饱满,质地坚硬,煮后软糯',cook:'水米比1:1.2,浸泡30分钟口感更佳'}},
'0301001':{name:'猪里脊(生)',code:'0301001',alias:'里脊肉',firstClass:'畜禽肉类',secondClass:'猪肉',edible:'100',waterRate:'73.3',dataSource:'0',vegTypes:['去筋膜','切丁'],
crowd:[{name:'成人',level:'推荐'},{name:'儿童',level:'推荐'},{name:'高血脂人群',level:'适量'}],
nutri:{calorie:143,protein:20.2,fat:7.1,carbohydrate:0.7,dietary_fiber:0,
vitamin_a:6,vitamin_b1:0.54,vitamin_b2:0.1,vitamin_b6:0.27,vitamin_b12:0.6,vitamin_c:0,vitamin_d:0.2,vitamin_e:0.34,vitamin_k:0,niacin:5.3,folic_acid:5,biotin:0,pantothenic_acid:0.5,total_choline:0,
ca:6,phosphorus:189,kalium:305,mg:25,na:44.2,fe:1.5,zn:2.3,se:10.5,cu:0.07,mn:0.01,iodine:0,
cholesterol:55,sfa:2.6,mufa:3.0,pufa:0.8,dha:0,
ile:930,leu:1520,tyr:660,lys:1680,met:510,phe:780,thr:880,trp:230,val:1010,met_cys:860,phe_tyr:1440,cys:350,
gi:0,purine:150,guanine:45,adenine:55,hypoxanthine:40,xanthine:10},
color:{main:'粉红色',grade:'优',desc:'色泽粉嫩,纹理清晰',cause:'肌红蛋白含量适中'},
taste:{main:'鲜味',sub:'微甜',intensity:'中',desc:'肉质鲜嫩,口感清甜'},
texture:{main:'嫩滑',chew:'易',digest:'中',desc:'纤维细腻,口感软嫩',cook:'腌制后煎炒口感最佳'}},
'0501001':{name:'鸡蛋(生)',code:'0501001',alias:'鲜鸡蛋',firstClass:'蛋类',secondClass:'鸡蛋',edible:'88',waterRate:'74.1',dataSource:'0',vegTypes:[],
crowd:[{name:'全人群',level:'推荐'},{name:'婴幼儿',level:'推荐'},{name:'老年人',level:'推荐'}],
nutri:{calorie:144,protein:13.3,fat:8.8,carbohydrate:2.8,dietary_fiber:0,
vitamin_a:234,vitamin_b1:0.11,vitamin_b2:0.27,vitamin_b6:0.13,vitamin_b12:1.0,vitamin_c:0,vitamin_d:2.2,vitamin_e:1.84,vitamin_k:0,niacin:0.2,folic_acid:113,biotin:0,pantothenic_acid:1.3,total_choline:0,
ca:56,phosphorus:130,kalium:154,mg:10,na:131.5,fe:2.0,zn:1.1,se:14.3,cu:0.05,mn:0.04,iodine:0,
cholesterol:585,sfa:2.8,mufa:3.5,pufa:1.4,dha:0,
ile:680,leu:1080,tyr:500,lys:910,met:400,phe:690,thr:620,trp:170,val:770,met_cys:660,phe_tyr:1190,cys:260,
gi:0,purine:3.7,guanine:0.8,adenine:1.2,hypoxanthine:1.4,xanthine:0.3},
color:{main:'金黄色',grade:'优',desc:'蛋壳浅褐,蛋黄金黄饱满',cause:'蛋黄富含叶黄素和类胡萝卜素'},
taste:{main:'鲜味',sub:'蛋香',intensity:'中',desc:'蛋白柔和,蛋黄浓郁'},
texture:{main:'嫩滑',chew:'易',digest:'易',desc:'水煮蛋弹嫩,炒蛋软嫩,蒸蛋细腻',cook:'蒸蛋口感最软,适合各年龄段'}},
'0201001':{name:'菠菜(生)',code:'0201001',alias:'波斯菜',firstClass:'蔬菜类',secondClass:'叶菜类',edible:'89',waterRate:'91.2',dataSource:'0',vegTypes:['去根','切段'],
crowd:[{name:'全人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],
nutri:{calorie:28,protein:2.6,fat:0.3,carbohydrate:4.5,dietary_fiber:1.7,
vitamin_a:487,vitamin_b1:0.04,vitamin_b2:0.11,vitamin_b6:0.1,vitamin_b12:0,vitamin_c:32,vitamin_d:0,vitamin_e:1.74,vitamin_k:0,niacin:0.6,folic_acid:110,biotin:0,pantothenic_acid:0.2,total_choline:0,
ca:66,phosphorus:47,kalium:311,mg:58,na:85.2,fe:2.9,zn:0.85,se:0.97,cu:0.1,mn:0.66,iodine:0,
cholesterol:0,sfa:0.1,mufa:0,pufa:0.1,dha:0,
ile:130,leu:200,tyr:100,lys:150,met:50,phe:130,thr:120,trp:40,val:150,met_cys:80,phe_tyr:230,cys:30,
gi:15,purine:23.0,guanine:6.0,adenine:8.0,hypoxanthine:7.0,xanthine:2.0},
color:{main:'深绿色',grade:'优',desc:'叶片翠绿有光泽,根部紫红',cause:'叶绿素含量高'},
taste:{main:'清甜',sub:'微涩',intensity:'弱',desc:'清爽微甜带涩'},
texture:{main:'嫩脆',chew:'易',digest:'易',desc:'叶片嫩滑,茎部脆嫩',cook:'焯水去草酸后口感最佳'}},
'0601001':{name:'豆腐(北)',code:'0601001',alias:'北豆腐',firstClass:'豆及豆制品',secondClass:'豆制品',edible:'100',waterRate:'82.8',dataSource:'1',vegTypes:['切块','切丁'],
crowd:[{name:'全人群',level:'推荐'},{name:'高血压人群',level:'推荐'},{name:'痛风人群',level:'适量'},{name:'老年人',level:'推荐'},{name:'术后恢复',level:'推荐'}],
nutri:{calorie:98,protein:12.2,fat:4.8,carbohydrate:1.5,dietary_fiber:0.4,
vitamin_a:0,vitamin_b1:0.07,vitamin_b2:0.04,vitamin_b6:0.05,vitamin_b12:0,vitamin_c:0,vitamin_d:0,vitamin_e:2.71,vitamin_k:0,niacin:0.1,folic_acid:37,biotin:0,pantothenic_acid:0.1,total_choline:0,
ca:138,phosphorus:97,kalium:106,mg:63,na:7.2,fe:2.5,zn:1.11,se:2.3,cu:0.27,mn:0.47,iodine:0,
cholesterol:0,sfa:0.7,mufa:1.1,pufa:2.7,dha:0,
ile:580,leu:940,tyr:430,lys:760,met:150,phe:600,thr:470,trp:160,val:580,met_cys:310,phe_tyr:1030,cys:160,
gi:32,purine:68.6,guanine:15.2,adenine:22.1,hypoxanthine:24.8,xanthine:6.5},
color:{main:'乳白色',grade:'优',desc:'洁白细腻,质地均匀,有光泽',cause:'大豆蛋白凝固,含水量高'},
taste:{main:'淡味',sub:'豆香',intensity:'弱',desc:'清淡豆香,口感温和,无刺激性'},
texture:{main:'细腻',chew:'易',digest:'易',desc:'质地细腻滑嫩,入口即化,无渣感',cook:'细腻易消化,适合消化功能弱者'}}
};
function initEditMode(){
if(pageMode!=='edit' || !editCode) return;
var food = EDIT_FOODS[editCode];
if(!food) return;
// 更新页面标题
document.getElementById('breadcrumbTitle').textContent = '编辑食物';
document.getElementById('btnSubmit').textContent = '保存修改';
// 填充基本信息
formData['foodCode'] = food.code;
formData['foodName'] = food.name;
formData['foodAlias'] = food.alias || '';
formData['edible'] = food.edible || '';
formData['waterRate'] = food.waterRate || '';
formData['dataSource'] = food.dataSource === 0 ? '0' : String(food.dataSource);
// 填充食物分类
selectedCategory.l1 = food.firstClass;
selectedCategory.l2 = food.secondClass;
// 填充净菜类型
vegTypes = food.vegTypes ? food.vegTypes.slice() : [];
// 填充适配人群
crowdList = food.crowd ? food.crowd.map(function(c){return {name:c.name,level:c.level};}) : [];
// 填充营养成分
if(food.nutri){
var n = food.nutri;
for(var key in n){ formData[key] = String(n[key]); }
// mg 矿物质字段特殊处理(id 为 mg_el 避免与 mg 单位冲突)
if(n.mg !== undefined){ formData['mg_el'] = String(n.mg); }
}
// 填充色泽档案
if(food.color){
formData['colorMain'] = food.color.main || '';
formData['colorGrade'] = food.color.grade || '';
formData['colorDesc'] = food.color.desc || '';
formData['colorCause'] = food.color.cause || '';
}
// 填充味道档案
if(food.taste){
formData['tasteMain'] = food.taste.main || '';
formData['tasteSub'] = food.taste.sub || '';
formData['tasteIntensity'] = food.taste.intensity || '';
formData['tasteDesc'] = food.taste.desc || '';
}
// 填充口感档案
if(food.texture){
formData['textureMain'] = food.texture.main || '';
formData['chewDiff'] = food.texture.chew || '';
formData['digestDiff'] = food.texture.digest || '';
formData['textureDesc'] = food.texture.desc || '';
formData['cookAdvice'] = food.texture.cook || '';
}
}
// 初始化
initEditMode();
renderStep();
// 编辑模式下,级联选择器需在渲染后回显
if(pageMode==='edit' && selectedCategory.l1){
var trig = document.getElementById('addCascadeTrigger');
if(trig){
trig.textContent = selectedCategory.l1 + (selectedCategory.l2 ? ' / '+selectedCategory.l2 : '');
trig.classList.add('has-value');
}
}
</script>
</body>
</html>
@@ -1,345 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>营养管理 - 食物成分详情 | 健康长庆原型</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);}
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;}
.breadcrumb a{color:var(--primary);text-decoration:none;}.breadcrumb a:hover{text-decoration:underline;}
.breadcrumb__sep{color:var(--text-placeholder);}
.form-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:24px;margin-bottom:16px;}
.form-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:20px;padding-bottom:12px;border-bottom:1px solid var(--border);}
.form-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px 24px;}
.form-group{display:flex;flex-direction:column;gap:6px;}
.form-group.full{grid-column:1/-1;}
.form-label{font-size:var(--font-sm);color:var(--text-secondary);}
.form-label .required{color:var(--danger);margin-left:2px;}
.form-input,.form-select,.form-textarea{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;width:100%;}
.form-input:focus,.form-select:focus,.form-textarea:focus{border-color:var(--primary);}
.form-input[readonly],.form-select[disabled],.form-textarea[readonly]{background:#f5f5f5;color:var(--text-secondary);cursor:not-allowed;}
.form-textarea{height:80px;padding:8px 12px;resize:vertical;}
.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;}
.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;}
.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);}
.btn-lg{height:40px;padding:0 24px;font-size:var(--font-base);}
.form-footer{display:flex;align-items:center;gap:12px;margin-top:24px;padding-top:16px;border-top:1px solid var(--border);}
</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-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 active"><span class="mi">🧪</span>食物成分</a>
<a href="din-dish-all.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>
<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="breadcrumb">
<a href="din-ingredient.html">食物成分</a>
<span class="breadcrumb__sep">/</span>
<span id="breadcrumbTitle">新增食物</span>
</div>
<!-- 基本信息 -->
<div class="form-card">
<div class="form-card__title">基本信息</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">食物编码<span class="required">*</span></label><input class="form-input" id="foodCode" placeholder="系统自动生成(一级分类+二级分类+自编码)" readonly></div>
<div class="form-group"><label class="form-label">食物名称<span class="required">*</span></label><input class="form-input" id="foodName" placeholder="请输入食物名称"></div>
<div class="form-group"><label class="form-label">别名</label><input class="form-input" id="foodAlias" placeholder="请输入别名"></div>
<div class="form-group"><label class="form-label">一级分类<span class="required">*</span></label><select class="form-select" id="firstClass"><option value="">请选择</option><option>谷薯类</option><option>畜禽肉类</option><option>水产类</option><option>蛋类</option><option>奶及奶制品</option><option>豆及豆制品</option><option>蔬菜类</option><option>水果类</option><option>坚果种子类</option><option>油脂类</option><option>调味品类</option></select></div>
<div class="form-group"><label class="form-label">二级分类<span class="required">*</span></label><input class="form-input" id="secondClass" placeholder="请输入二级分类"></div>
<div class="form-group"><label class="form-label">可食部分(%)</label><input class="form-input" id="edible" placeholder="0~100"></div>
<div class="form-group"><label class="form-label">水分(%)</label><input class="form-input" id="waterRate" placeholder="0~100"></div>
<div class="form-group"><label class="form-label">数据来源</label><select class="form-select" id="dataSource"><option value="0">系统内置</option><option value="1">手动添加</option></select></div>
<div class="form-group"><label class="form-label">食材图片</label><input class="form-input" id="foodUrl" placeholder="图片URL或上传"></div>
</div>
</div>
<!-- 营养成分:宏量营养素 -->
<div class="form-card">
<div class="form-card__title">宏量营养素(每100g可食部)</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">能量(kcal)<span class="required">*</span></label><input class="form-input" id="calorie" placeholder="0"></div>
<div class="form-group"><label class="form-label">蛋白质(g)<span class="required">*</span></label><input class="form-input" id="protein" placeholder="0"></div>
<div class="form-group"><label class="form-label">脂肪(g)<span class="required">*</span></label><input class="form-input" id="fat" placeholder="0"></div>
<div class="form-group"><label class="form-label">碳水化合物(g)<span class="required">*</span></label><input class="form-input" id="carbohydrate" placeholder="0"></div>
<div class="form-group"><label class="form-label">膳食纤维(g)</label><input class="form-input" id="dietary_fiber" placeholder="0"></div>
</div>
</div>
<!-- 营养成分:维生素 -->
<div class="form-card">
<div class="form-card__title">维生素</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">维生素A(μg RAE)</label><input class="form-input" id="vitamin_a" placeholder="0"></div>
<div class="form-group"><label class="form-label">硫胺素/B1(mg)</label><input class="form-input" id="vitamin_b1" placeholder="0"></div>
<div class="form-group"><label class="form-label">核黄素/B2(mg)</label><input class="form-input" id="vitamin_b2" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素B6(mg)</label><input class="form-input" id="vitamin_b6" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素B12(μg)</label><input class="form-input" id="vitamin_b12" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素C(mg)</label><input class="form-input" id="vitamin_c" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素D(μg)</label><input class="form-input" id="vitamin_d" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素E(mg α-TE)</label><input class="form-input" id="vitamin_e" placeholder="0"></div>
<div class="form-group"><label class="form-label">维生素K(μg)</label><input class="form-input" id="vitamin_k" placeholder="0"></div>
<div class="form-group"><label class="form-label">烟酸(mg)</label><input class="form-input" id="niacin" placeholder="0"></div>
<div class="form-group"><label class="form-label">叶酸(μg)</label><input class="form-input" id="folic_acid" placeholder="0"></div>
<div class="form-group"><label class="form-label">生物素(μg)</label><input class="form-input" id="biotin" placeholder="0"></div>
<div class="form-group"><label class="form-label">泛酸(mg)</label><input class="form-input" id="pantothenic_acid" placeholder="0"></div>
<div class="form-group"><label class="form-label">总胆碱(mg)</label><input class="form-input" id="total_choline" placeholder="0"></div>
</div>
</div>
<!-- 营养成分:矿物质 -->
<div class="form-card">
<div class="form-card__title">矿物质</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">钙(mg)</label><input class="form-input" id="ca" placeholder="0"></div>
<div class="form-group"><label class="form-label">磷(mg)</label><input class="form-input" id="phosphorus" placeholder="0"></div>
<div class="form-group"><label class="form-label">钾(mg)</label><input class="form-input" id="kalium" placeholder="0"></div>
<div class="form-group"><label class="form-label">镁(mg)</label><input class="form-input" id="mg_el" placeholder="0"></div>
<div class="form-group"><label class="form-label">钠(mg)</label><input class="form-input" id="na" placeholder="0"></div>
<div class="form-group"><label class="form-label">铁(mg)</label><input class="form-input" id="fe" placeholder="0"></div>
<div class="form-group"><label class="form-label">锌(mg)</label><input class="form-input" id="zn" placeholder="0"></div>
<div class="form-group"><label class="form-label">硒(μg)</label><input class="form-input" id="se" placeholder="0"></div>
<div class="form-group"><label class="form-label">铜(mg)</label><input class="form-input" id="cu" placeholder="0"></div>
<div class="form-group"><label class="form-label">锰(mg)</label><input class="form-input" id="mn" placeholder="0"></div>
<div class="form-group"><label class="form-label">碘(μg)</label><input class="form-input" id="iodine" placeholder="0"></div>
</div>
</div>
<!-- 营养成分:脂质指标 -->
<div class="form-card">
<div class="form-card__title">脂质指标</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">胆固醇(mg)</label><input class="form-input" id="cholesterol" placeholder="0"></div>
<div class="form-group"><label class="form-label">饱和脂肪酸(g)</label><input class="form-input" id="sfa" placeholder="0"></div>
<div class="form-group"><label class="form-label">单不饱和脂肪酸(g)</label><input class="form-input" id="mufa" placeholder="0"></div>
<div class="form-group"><label class="form-label">多不饱和脂肪酸(g)</label><input class="form-input" id="pufa" placeholder="0"></div>
<div class="form-group"><label class="form-label">DHA(mg)</label><input class="form-input" id="dha" placeholder="0"></div>
</div>
</div>
<!-- 营养成分:氨基酸 -->
<div class="form-card">
<div class="form-card__title">氨基酸</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">异亮氨酸(mg)</label><input class="form-input" id="ile" placeholder="0"></div>
<div class="form-group"><label class="form-label">亮氨酸(mg)</label><input class="form-input" id="leu" placeholder="0"></div>
<div class="form-group"><label class="form-label">酪氨酸(mg)</label><input class="form-input" id="tyr" placeholder="0"></div>
<div class="form-group"><label class="form-label">赖氨酸(mg)</label><input class="form-input" id="lys" placeholder="0"></div>
<div class="form-group"><label class="form-label">蛋氨酸(mg)</label><input class="form-input" id="met" placeholder="0"></div>
<div class="form-group"><label class="form-label">苯丙氨酸(mg)</label><input class="form-input" id="phe" placeholder="0"></div>
<div class="form-group"><label class="form-label">苏氨酸(mg)</label><input class="form-input" id="thr" placeholder="0"></div>
<div class="form-group"><label class="form-label">色氨酸(mg)</label><input class="form-input" id="trp" placeholder="0"></div>
<div class="form-group"><label class="form-label">缬氨酸(mg)</label><input class="form-input" id="val" placeholder="0"></div>
<div class="form-group"><label class="form-label">蛋氨酸+胱氨酸(mg)</label><input class="form-input" id="met_cys" placeholder="0"></div>
<div class="form-group"><label class="form-label">苯丙氨酸+酪氨酸(mg)</label><input class="form-input" id="phe_tyr" placeholder="0"></div>
<div class="form-group"><label class="form-label">胱氨酸(mg)</label><input class="form-input" id="cys" placeholder="0"></div>
</div>
</div>
<!-- 营养成分:特殊指标 -->
<div class="form-card">
<div class="form-card__title">特殊指标</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">GI</label><input class="form-input" id="gi" placeholder="0"></div>
<div class="form-group"><label class="form-label">总嘌呤(mg)</label><input class="form-input" id="purine" placeholder="0"></div>
<div class="form-group"><label class="form-label">鸟嘌呤(mg)</label><input class="form-input" id="guanine" placeholder="0"></div>
<div class="form-group"><label class="form-label">腺嘌呤(mg)</label><input class="form-input" id="adenine" placeholder="0"></div>
<div class="form-group"><label class="form-label">次黄嘌呤(mg)</label><input class="form-input" id="hypoxanthine" placeholder="0"></div>
<div class="form-group"><label class="form-label">黄嘌呤(mg)</label><input class="form-input" id="xanthine" placeholder="0"></div>
</div>
</div>
<!-- 色泽档案 -->
<div class="form-card">
<div class="form-card__title">🎨 色泽档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主色系</label><input class="form-input" id="colorMain" placeholder="如:白色、深绿色"></div>
<div class="form-group"><label class="form-label">感官评级</label><select class="form-select" id="colorGrade"><option value="">请选择</option><option></option><option></option><option></option><option></option></select></div>
<div class="form-group full"><label class="form-label">色泽描述</label><textarea class="form-textarea" id="colorDesc" placeholder="请描述色泽特征,如:乳白色,半透明,颗粒饱满"></textarea></div>
<div class="form-group full"><label class="form-label">色泽成因</label><textarea class="form-textarea" id="colorCause" placeholder="请描述色泽形成原因"></textarea></div>
<div class="form-group"><label class="form-label">适配人群</label><input class="form-input" id="colorCrowd" placeholder="如:成人、老年人、儿童"></div>
<div class="form-group"><label class="form-label">适配等级</label><select class="form-select" id="colorLevel"><option value="">请选择</option><option>推荐</option><option>适量</option><option>慎食</option></select></div>
<div class="form-group full"><label class="form-label">备注</label><input class="form-input" id="colorNote" placeholder="如:高血脂人群每日不超过1个"></div>
</div>
</div>
<!-- 味道档案 -->
<div class="form-card">
<div class="form-card__title">👅 味道档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主味型</label><input class="form-input" id="tasteMain" placeholder="如:淡味、鲜味、甜味"></div>
<div class="form-group"><label class="form-label">辅味型</label><input class="form-input" id="tasteSub" placeholder="如:微涩、豆香"></div>
<div class="form-group"><label class="form-label">味道强度</label><select class="form-select" id="tasteIntensity"><option value="">请选择</option><option></option><option></option><option></option></select></div>
<div class="form-group full"><label class="form-label">味道描述</label><textarea class="form-textarea" id="tasteDesc" placeholder="请描述味道特征"></textarea></div>
<div class="form-group"><label class="form-label">适配人群</label><input class="form-input" id="tasteCrowd" placeholder="如:全人群、糖尿病人群"></div>
<div class="form-group"><label class="form-label">适配等级</label><select class="form-select" id="tasteLevel"><option value="">请选择</option><option>推荐</option><option>适量</option><option>慎食</option></select></div>
<div class="form-group full"><label class="form-label">饮食禁忌说明</label><input class="form-input" id="tasteNote" placeholder="如:糖尿病人群注意控制量"></div>
</div>
</div>
<!-- 口感档案 -->
<div class="form-card">
<div class="form-card__title">🫦 口感档案</div>
<div class="form-grid">
<div class="form-group"><label class="form-label">主质地</label><input class="form-input" id="textureMain" placeholder="如:软糯、嫩滑、细腻"></div>
<div class="form-group"><label class="form-label">咀嚼难度</label><select class="form-select" id="chewDiff"><option value="">请选择</option><option></option><option></option><option></option></select></div>
<div class="form-group"><label class="form-label">消化难度</label><select class="form-select" id="digestDiff"><option value="">请选择</option><option></option><option></option><option></option></select></div>
<div class="form-group full"><label class="form-label">口感描述</label><textarea class="form-textarea" id="textureDesc" placeholder="请描述口感特征"></textarea></div>
<div class="form-group full"><label class="form-label">烹饪建议</label><textarea class="form-textarea" id="cookAdvice" placeholder="请输入烹饪建议"></textarea></div>
<div class="form-group"><label class="form-label">适配人群</label><input class="form-input" id="textureCrowd" placeholder="如:全人群、老年人、术后恢复"></div>
<div class="form-group"><label class="form-label">适配等级</label><select class="form-select" id="textureLevel"><option value="">请选择</option><option>推荐</option><option>适量</option><option>慎食</option></select></div>
</div>
</div>
<!-- 底部操作 -->
<div class="form-footer" id="formFooter">
<button class="btn btn-primary btn-lg" id="btnSave">保存</button>
<a href="din-ingredient.html" class="btn btn-default btn-lg">返回列表</a>
</div>
</main>
<script>
// 侧边栏子组展开/收起
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 解析URL参数
var params=new URLSearchParams(window.location.search);
var code=params.get('code');
var mode=params.get('mode');
// 根据模式设置页面标题和状态
if(mode==='view'){
document.getElementById('breadcrumbTitle').textContent='食物详情';
document.title='营养管理 - 食物详情 | 健康长庆原型';
document.querySelectorAll('.form-input,.form-select,.form-textarea').forEach(function(el){
if(el.tagName==='SELECT'){el.disabled=true;}
else{el.readOnly=true;}
});
document.getElementById('btnSave').style.display='none';
}else if(code){
document.getElementById('breadcrumbTitle').textContent='编辑食物';
document.title='营养管理 - 编辑食物 | 健康长庆原型';
}
// 模拟数据填充
if(code){
var mockData={
'0101001':{foodName:'粳米(生)',foodAlias:'大米',firstClass:'谷薯类',secondClass:'稻米',edible:'100',waterRate:'13.3',dataSource:'0',
calorie:'346',protein:'7.4',fat:'0.8',carbohydrate:'77.2',dietary_fiber:'0.6',
vitamin_a:'0',vitamin_b1:'0.16',vitamin_b2:'0.08',vitamin_b6:'0.16',vitamin_b12:'0',vitamin_c:'0',vitamin_d:'0',vitamin_e:'0.46',vitamin_k:'0',niacin:'1.9',folic_acid:'3.8',biotin:'0',pantothenic_acid:'0.6',total_choline:'0',
ca:'13',phosphorus:'110',kalium:'103',mg_el:'34',na:'3.8',fe:'2.3',zn:'1.7',se:'2.23',cu:'0.3',mn:'1.29',iodine:'0',
cholesterol:'0',sfa:'0.18',mufa:'0.17',pufa:'0.22',dha:'0',
ile:'292',leu:'594',tyr:'332',lys:'255',met:'163',phe:'372',thr:'244',trp:'93',val:'420',met_cys:'276',phe_tyr:'704',cys:'113',
gi:'83',purine:'18.4',guanine:'0',adenine:'0',hypoxanthine:'0',xanthine:'0',
colorMain:'白色',colorGrade:'优',colorDesc:'乳白色,半透明,颗粒饱满',colorCause:'淀粉主体,含少量蛋白质',colorCrowd:'成人、老年人、儿童',colorLevel:'推荐',colorNote:'',
tasteMain:'淡味',tasteSub:'—',tasteIntensity:'弱',tasteDesc:'清淡,煮熟后微甜,无异味',tasteCrowd:'全人群、糖尿病人群',tasteLevel:'适量',tasteNote:'糖尿病人群注意控制量',
textureMain:'软糯',chewDiff:'易',digestDiff:'易',textureDesc:'煮熟后软糯有嚼劲,易于消化',cookAdvice:'熬粥质地更软,适合老年人',textureCrowd:'全人群、老年人、术后恢复',textureLevel:'推荐'}
};
var d=mockData[code];
if(d){
document.getElementById('foodCode').value=code;
Object.keys(d).forEach(function(key){
var el=document.getElementById(key);
if(el) el.value=d[key];
});
}
}
</script>
</body>
</html>
+128 -30
View File
@@ -119,6 +119,23 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.profile-field__label{font-size:var(--font-xs);color:var(--text-placeholder);}
.profile-field__value{font-size:var(--font-sm);color:var(--text-primary);line-height:1.5;}
.profile-field--full{grid-column:1/-1;}
/* 级联选择器 */
.cascade-picker{position:relative;flex:1;}
.cascade-trigger{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 10px;font-size:var(--font-xs);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;cursor:pointer;display:flex;align-items:center;white-space:nowrap;color:var(--text-placeholder);width:100%;}
.cascade-trigger.has-value{color:var(--text-primary);}
.cascade-panel{display:none;position:fixed;z-index:200;background:#fff;border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-md);width:320px;}
.cascade-panel.show{display:flex;}
.cascade-col{width:50%;max-height:240px;overflow-y:auto;}
.cascade-col+.cascade-col{border-left:1px solid #f0f0f0;}
.cascade-col__title{font-size:var(--font-xs);color:var(--text-placeholder);padding:8px 12px;border-bottom:1px solid #f0f0f0;position:sticky;top:0;background:#fff;}
.cascade-opt{padding:7px 12px;font-size:var(--font-xs);cursor:pointer;transition:background .15s;display:flex;align-items:center;justify-content:space-between;}
.cascade-opt:hover{background:#F8FBFF;}
.cascade-opt.active{background:var(--primary-light);color:var(--primary);font-weight:500;}
.cascade-opt .arrow-r{font-size:10px;color:var(--text-placeholder);}
/* 来源标签 */
.food-item__source{font-size:10px;padding:1px 6px;border-radius:8px;flex-shrink:0;white-space:nowrap;}
.food-item__source--sys{background:var(--primary-light);color:var(--primary);}
.food-item__source--manual{background:#FFF7E6;color:var(--warning);}
</style>
</head>
<body>
@@ -147,6 +164,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<!-- 侧边栏:营养用餐 -->
<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 active"><span class="mi">🧪</span>食物成分</a>
@@ -204,34 +222,46 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<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><option>豆及豆制品</option><option>蔬菜类</option><option>水果类</option><option>坚果种子类</option><option>油脂类</option><option>调味品类</option></select>
<div class="cascade-picker" id="categoryPicker">
<div class="cascade-trigger" id="cascadeTrigger" onclick="toggleCascade()">食物分类</div>
<div class="cascade-panel" id="cascadePanel">
<div class="cascade-col" id="cascadeL1"></div>
<div class="cascade-col" id="cascadeL2"></div>
</div>
</div>
<select><option>全部来源</option><option>系统内置</option><option>手动添加</option></select>
</div>
</div>
<div class="panel-left__actions">
<a href="din-ingredient-detail.html" class="btn btn-primary"> 新增食物</a>
<a href="din-ingredient-add.html" class="btn btn-primary"> 新增食物</a>
<a class="btn btn-primary">🖥 分类管理</a>
<button class="btn btn-default">🔄 同步国标库</button>
</div>
<div class="food-list">
<div class="food-item active" onclick="selectFood(0)">
<div class="food-item__icon">🌾</div>
<div class="food-item__info"><div class="food-item__name">粳米(生)</div><div class="food-item__meta"><span>0101001</span><span>谷薯类/稻米</span><span>346 kcal</span></div></div>
<span class="food-item__source food-item__source--sys">系统内置</span>
</div>
<div class="food-item" onclick="selectFood(1)">
<div class="food-item__icon">🥩</div>
<div class="food-item__info"><div class="food-item__name">猪里脊肉</div><div class="food-item__meta"><span>0201001</span><span>畜禽肉类/猪肉</span><span>155 kcal</span></div></div>
<span class="food-item__source food-item__source--sys">系统内置</span>
</div>
<div class="food-item" onclick="selectFood(2)">
<div class="food-item__icon">🥬</div>
<div class="food-item__info"><div class="food-item__name">菠菜</div><div class="food-item__meta"><span>0701001</span><span>蔬菜类/叶菜类</span><span>24 kcal</span></div></div>
<span class="food-item__source food-item__source--sys">系统内置</span>
</div>
<div class="food-item" onclick="selectFood(3)">
<div class="food-item__icon">🥚</div>
<div class="food-item__info"><div class="food-item__name">鸡蛋</div><div class="food-item__meta"><span>0401001</span><span>蛋类/鸡蛋</span><span>144 kcal</span></div></div>
<span class="food-item__source food-item__source--sys">系统内置</span>
</div>
<div class="food-item" onclick="selectFood(4)">
<div class="food-item__icon">🧈</div>
<div class="food-item__info"><div class="food-item__name">豆腐(北)</div><div class="food-item__meta"><span>0601001</span><span>豆及豆制品/豆制品</span><span>98 kcal</span></div></div>
<span class="food-item__source food-item__source--manual">手动添加</span>
</div>
</div>
<div class="panel-left__footer">
@@ -249,14 +279,13 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<h2 id="detail-food-name">粳米(生)</h2>
<span class="code" id="detail-food-code">0101001</span>
<span class="category" id="detail-food-category">谷薯类 / 稻米</span>
<a class="link" href="din-ingredient-detail.html?code=0101001" style="margin-left:auto;">编辑</a>
<a class="link" href="din-ingredient-detail.html?code=0101001&mode=view">查看完整详情</a>
</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 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>
</div>
<div class="panel-right__body" id="detail-body">
@@ -269,7 +298,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<script>
// 食物数据(对齐 yx_mater_base_material 表结构)
const FOODS = [
{name:'粳米(生)',code:'0101001',alias:'大米',firstClass:'谷薯类',secondClass:'稻米',icon:'🌾',edible:'100',waterRate:'13.3',dataSource:0,
{name:'粳米(生)',code:'0101001',alias:'大米',firstClass:'谷薯类',secondClass:'稻米',icon:'🌾',edible:'100',waterRate:'13.3',dataSource:0,vegTypes:['洗净米','免淘米'],
nutri:{calorie:346,protein:7.4,fat:0.8,carbohydrate:77.2,dietary_fiber:0.6,
vitamin_a:0,vitamin_b1:0.16,vitamin_b2:0.08,vitamin_b6:0.16,vitamin_b12:0,vitamin_c:0,vitamin_d:0,vitamin_e:0.46,vitamin_k:0,niacin:1.9,folic_acid:3.8,biotin:0,pantothenic_acid:0.6,total_choline:0,
ca:13,phosphorus:110,kalium:103,mg:34,na:3.8,fe:2.3,zn:1.7,se:2.23,cu:0.3,mn:1.29,iodine:0,
@@ -279,7 +308,7 @@ const FOODS = [
color:{main:'白色',desc:'乳白色,半透明,颗粒饱满',cause:'淀粉主体,含少量蛋白质',grade:'优',crowd:[{name:'成人',level:'推荐'},{name:'老年人',level:'推荐'},{name:'儿童',level:'推荐'}],note:'—'},
taste:{main:'淡味',sub:'—',intensity:'弱',desc:'清淡,煮熟后微甜,无异味',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],note:'糖尿病人群注意控制量'},
texture:{main:'软糯',chew:'易',digest:'易',desc:'煮熟后软糯有嚼劲,易于消化',crowd:[{name:'全人群',level:'推荐'},{name:'老年人',level:'推荐'},{name:'术后恢复',level:'推荐'}],cook:'熬粥质地更软,适合老年人'}},
{name:'猪里脊肉',code:'0201001',alias:'里脊',firstClass:'畜禽肉类',secondClass:'猪肉',icon:'🥩',edible:'100',waterRate:'71.4',dataSource:0,
{name:'猪里脊肉',code:'0201001',alias:'里脊',firstClass:'畜禽肉类',secondClass:'猪肉',icon:'🥩',edible:'100',waterRate:'71.4',dataSource:0,vegTypes:['肉片','肉丁','肉块'],
nutri:{calorie:155,protein:20.2,fat:7.9,carbohydrate:0.7,dietary_fiber:0,
vitamin_a:6,vitamin_b1:0.54,vitamin_b2:0.1,vitamin_b6:0.33,vitamin_b12:0.6,vitamin_c:0,vitamin_d:0,vitamin_e:0.34,vitamin_k:0,niacin:5.3,folic_acid:3,biotin:0,pantothenic_acid:0.8,total_choline:0,
ca:6,phosphorus:196,kalium:305,mg:25,na:43.2,fe:1.5,zn:2.3,se:11.97,cu:0.09,mn:0.01,iodine:0,
@@ -289,7 +318,7 @@ const FOODS = [
color:{main:'粉红色',desc:'鲜嫩粉红,有光泽,纹理细腻',cause:'肌红蛋白含量适中',grade:'优',crowd:[{name:'成人',level:'推荐'},{name:'老年人',level:'适量'}],note:'老年人消化负担稍重'},
taste:{main:'鲜味',sub:'淡咸',intensity:'中',desc:'肉香鲜美,脂肪少,口感纯正',crowd:[{name:'成人',level:'推荐'},{name:'儿童',level:'推荐'},{name:'肾病人群',level:'慎食'}],note:'肾病人群需控制蛋白质摄入'},
texture:{main:'嫩滑',chew:'中',digest:'中',desc:'肌纤维细腻,炒制后滑嫩,少筋膜',crowd:[{name:'成人',level:'推荐'},{name:'老年人',level:'适量'},{name:'儿童',level:'推荐'}],cook:'老年人建议炖煮至软烂,避免大块'}},
{name:'菠菜',code:'0701001',alias:'菠菜',firstClass:'蔬菜类',secondClass:'叶菜类',icon:'🥬',edible:'89',waterRate:'91.2',dataSource:0,
{name:'菠菜',code:'0701001',alias:'菠菜',firstClass:'蔬菜类',secondClass:'叶菜类',icon:'🥬',edible:'89',waterRate:'91.2',dataSource:0,vegTypes:[],
nutri:{calorie:24,protein:2.6,fat:0.3,carbohydrate:3.1,dietary_fiber:1.7,
vitamin_a:487,vitamin_b1:0.04,vitamin_b2:0.11,vitamin_b6:0.1,vitamin_b12:0,vitamin_c:32,vitamin_d:0,vitamin_e:1.74,vitamin_k:483,niacin:0.6,folic_acid:110,biotin:0,pantothenic_acid:0.2,total_choline:0,
ca:66,phosphorus:47,kalium:311,mg:58,na:85.2,fe:2.9,zn:0.85,se:0.97,cu:0.1,mn:0.66,iodine:0,
@@ -299,7 +328,7 @@ const FOODS = [
color:{main:'深绿色',desc:'翠绿鲜亮,叶片厚实,颜色均匀',cause:'叶绿素含量丰富,含胡萝卜素',grade:'优',crowd:[{name:'成人',level:'推荐'},{name:'孕妇',level:'推荐'},{name:'贫血人群',level:'推荐'}],note:'富含叶酸,孕妇适宜'},
taste:{main:'淡味',sub:'微涩',intensity:'弱',desc:'轻微涩感(草酸),汆烫后消失',crowd:[{name:'全人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],note:'肾结石人群慎食,草酸含量高'},
texture:{main:'嫩软',chew:'易',digest:'易',desc:'叶片薄嫩,焯水后柔软,汁水丰富',crowd:[{name:'全人群',level:'推荐'},{name:'老年人',level:'推荐'},{name:'婴幼儿',level:'适量'}],cook:'婴幼儿建议打泥处理,去除纤维'}},
{name:'鸡蛋',code:'0401001',alias:'鸡蛋',firstClass:'蛋类',secondClass:'鸡蛋',icon:'🥚',edible:'88',waterRate:'74.1',dataSource:0,
{name:'鸡蛋',code:'0401001',alias:'鸡蛋',firstClass:'蛋类',secondClass:'鸡蛋',icon:'🥚',edible:'88',waterRate:'74.1',dataSource:0,vegTypes:[],
nutri:{calorie:144,protein:13.3,fat:8.8,carbohydrate:2.8,dietary_fiber:0,
vitamin_a:234,vitamin_b1:0.11,vitamin_b2:0.27,vitamin_b6:0.11,vitamin_b12:1.0,vitamin_c:0,vitamin_d:2.2,vitamin_e:1.06,vitamin_k:0,niacin:0.2,folic_acid:70,biotin:20,pantothenic_acid:1.3,total_choline:294,
ca:56,phosphorus:195,kalium:154,mg:10,na:131.5,fe:2.0,zn:1.1,se:14.34,cu:0.15,mn:0.04,iodine:27.2,
@@ -309,7 +338,7 @@ const FOODS = [
color:{main:'米黄色',desc:'蛋黄橙黄,蛋白透明,整体色泽亮',cause:'蛋黄富含类胡萝卜素',grade:'优',crowd:[{name:'成人',level:'推荐'},{name:'儿童',level:'推荐'},{name:'高血脂人群',level:'慎食'}],note:'高血脂人群每日不超过1个'},
taste:{main:'鲜味',sub:'淡味',intensity:'中',desc:'蛋黄浓郁,蛋白清淡,整体鲜香',crowd:[{name:'成人',level:'推荐'},{name:'儿童',level:'推荐'}],note:'优质蛋白,味道适中'},
texture:{main:'嫩滑',chew:'易',digest:'易',desc:'水煮蛋弹嫩,炒蛋软嫩,蒸蛋细腻',crowd:[{name:'全人群',level:'推荐'},{name:'婴幼儿',level:'推荐'},{name:'老年人',level:'推荐'}],cook:'蒸蛋口感最软,适合各年龄段'}},
{name:'豆腐(北)',code:'0601001',alias:'北豆腐',firstClass:'豆及豆制品',secondClass:'豆制品',icon:'🧈',edible:'100',waterRate:'82.8',dataSource:0,
{name:'豆腐(北)',code:'0601001',alias:'北豆腐',firstClass:'豆及豆制品',secondClass:'豆制品',icon:'🧈',edible:'100',waterRate:'82.8',dataSource:1,vegTypes:['豆腐块'],
nutri:{calorie:98,protein:12.2,fat:4.8,carbohydrate:1.5,dietary_fiber:0.4,
vitamin_a:0,vitamin_b1:0.07,vitamin_b2:0.04,vitamin_b6:0.05,vitamin_b12:0,vitamin_c:0,vitamin_d:0,vitamin_e:2.71,vitamin_k:0,niacin:0.1,folic_acid:37,biotin:0,pantothenic_acid:0.1,total_choline:0,
ca:138,phosphorus:97,kalium:106,mg:63,na:7.2,fe:2.5,zn:1.11,se:2.3,cu:0.27,mn:0.47,iodine:0,
@@ -331,6 +360,7 @@ function selectFood(idx){
document.getElementById('detail-food-name').textContent = f.name;
document.getElementById('detail-food-code').textContent = f.code;
document.getElementById('detail-food-category').textContent = f.firstClass + ' / ' + f.secondClass;
document.getElementById('link-detail').href = 'din-ingredient-detail.html?code=' + f.code + '&mode=view';
renderDetail();
}
@@ -348,26 +378,50 @@ function levelTag(level){
function renderDetail(){
const f = FOODS[currentFood];
const body = document.getElementById('detail-body');
if(currentTab===0) body.innerHTML = renderNutri(f);
else if(currentTab===1) body.innerHTML = renderColor(f);
else if(currentTab===2) body.innerHTML = renderTaste(f);
else body.innerHTML = renderTexture(f);
const editLink = `<div style="text-align:right;margin-bottom:12px;"><a class="link" href="din-ingredient-add.html?mode=edit&code=${f.code}&tab=${currentTab}">编辑</a></div>`;
let content = '';
if(currentTab===0) content = renderBasicInfo(f);
else if(currentTab===1) content = renderNutri(f);
else if(currentTab===2) content = renderTaste(f);
else if(currentTab===3) content = renderTexture(f);
else content = renderColor(f);
body.innerHTML = editLink + content;
}
function renderBasicInfo(f){
const sourceLabel = f.dataSource===0?'系统内置':'手动添加';
let html = `<div class="profile-section"><div class="profile-section__title">基础信息</div><div class="profile-fields">`;
html += `<div class="profile-field"><div class="profile-field__label">食物名称</div><div class="profile-field__value">${f.name}</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">食物编码</div><div class="profile-field__value">${f.code}</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">别名</div><div class="profile-field__value">${f.alias}</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">食物分类</div><div class="profile-field__value">${f.firstClass} / ${f.secondClass}</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">可食部分</div><div class="profile-field__value">${f.edible}%</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">水分</div><div class="profile-field__value">${f.waterRate}%</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">数据来源</div><div class="profile-field__value">${sourceLabel}</div></div>`;
const vegTypesHtml = f.vegTypes && f.vegTypes.length > 0 ? f.vegTypes.map(t=>`<span class="tag tag-blue">${t}</span>`).join(' ') : '—';
html += `<div class="profile-field"><div class="profile-field__label">净菜类型</div><div class="profile-field__value">${vegTypesHtml}</div></div>`;
html += `</div></div>`;
// 适配人群(汇总色泽/味道/口感档案)
let allCrowd = [];
let allNotes = [];
if(f.color && f.color.crowd) f.color.crowd.forEach(c=>{allCrowd.push(c);});
if(f.taste && f.taste.crowd) f.taste.crowd.forEach(c=>{allCrowd.push(c);});
if(f.texture && f.texture.crowd) f.texture.crowd.forEach(c=>{allCrowd.push(c);});
if(f.color && f.color.note && f.color.note!=='—') allNotes.push(f.color.note);
if(f.taste && f.taste.note && f.taste.note!=='—') allNotes.push(f.taste.note);
if(f.texture && f.texture.cook) allNotes.push(f.texture.cook);
// 去重
let seen = {};
let uniqueCrowd = [];
allCrowd.forEach(c=>{let key=c.name+'_'+c.level;if(!seen[key]){seen[key]=1;uniqueCrowd.push(c);}});
html += renderCrowd(uniqueCrowd, allNotes.join(''));
return html;
}
function renderNutri(f){
const n = f.nutri;
const sourceLabel = f.dataSource===0?'系统内置':'手动添加';
// 基础信息
let html = `<div class="profile-section"><div class="profile-section__title">基础信息</div><div class="profile-fields">`;
html += `<div class="profile-field"><div class="profile-field__label">别名</div><div class="profile-field__value">${f.alias}</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">一级分类</div><div class="profile-field__value">${f.firstClass}</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">二级分类</div><div class="profile-field__value">${f.secondClass}</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">可食部分</div><div class="profile-field__value">${f.edible}%</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">水分</div><div class="profile-field__value">${f.waterRate}%</div></div>`;
html += `<div class="profile-field"><div class="profile-field__label">数据来源</div><div class="profile-field__value">${sourceLabel}</div></div>`;
html += `</div></div>`;
// 宏量营养素
html += renderNutriGroup('宏量营养素', [
let html = renderNutriGroup('宏量营养素', [
{label:'能量',value:n.calorie,unit:'kcal'},{label:'蛋白质',value:n.protein,unit:'g'},
{label:'脂肪',value:n.fat,unit:'g'},{label:'碳水化合物',value:n.carbohydrate,unit:'g'},
{label:'膳食纤维',value:n.dietary_fiber,unit:'g'}
@@ -433,7 +487,6 @@ function renderColor(f){
html += `<div class="profile-field profile-field--full"><div class="profile-field__label">色泽描述</div><div class="profile-field__value">${c.desc}</div></div>`;
html += `<div class="profile-field profile-field--full"><div class="profile-field__label">色泽成因</div><div class="profile-field__value">${c.cause}</div></div>`;
html += `</div></div>`;
html += renderCrowd(c.crowd, c.note);
return html;
}
@@ -445,7 +498,6 @@ function renderTaste(f){
html += `<div class="profile-field"><div class="profile-field__label">味道强度</div><div class="profile-field__value">${t.intensity}</div></div>`;
html += `<div class="profile-field profile-field--full"><div class="profile-field__label">味道描述</div><div class="profile-field__value">${t.desc}</div></div>`;
html += `</div></div>`;
html += renderCrowd(t.crowd, t.note);
return html;
}
@@ -458,19 +510,65 @@ function renderTexture(f){
html += `<div class="profile-field profile-field--full"><div class="profile-field__label">口感描述</div><div class="profile-field__value">${t.desc}</div></div>`;
html += `<div class="profile-field profile-field--full"><div class="profile-field__label">烹饪建议</div><div class="profile-field__value">${t.cook}</div></div>`;
html += `</div></div>`;
html += renderCrowd(t.crowd, '');
return html;
}
function renderCrowd(crowd, note){
let html = `<div class="profile-section"><div class="profile-section__title">适配人群</div><div style="display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;">`;
crowd.forEach(c=>{html+=`<div style="display:flex;align-items:center;gap:4px;">${levelTag(c.level)}<span style="font-size:var(--font-sm);">${c.name}</span></div>`;});
crowd.forEach(c=>{html+=`<div style="display:flex;align-items:center;gap:4px;"><span style="font-size:var(--font-sm);">${c.name}</span>${levelTag(c.level)}</div>`;});
html += `</div>`;
if(note && note!=='—') html += `<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;">⚠️ ${note}</div>`;
html += `</div>`;
return html;
}
/* 级联分类选择器 */
var CATEGORIES={'谷薯类':['稻米','小麦','玉米','薯类','杂粮'],'畜禽肉类':['猪肉','牛肉','羊肉','鸡肉','鸭肉'],'水产类':['淡水鱼','海水鱼','虾蟹','贝类'],'蛋类':['鸡蛋','鸭蛋','鹅蛋','鹌鹑蛋'],'奶及奶制品':['牛奶','酸奶','奶酪','奶粉'],'豆及豆制品':['大豆','豆制品','杂豆'],'蔬菜类':['叶菜类','根茎类','瓜果类','菌菇类','豆荚类'],'水果类':['仁果类','浆果类','柑橘类','瓜果类','热带水果'],'坚果种子类':['坚果','种子'],'油脂类':['植物油','动物油'],'调味品类':['酱料','香辛料','调味汁']};
var cascadeL1Active='';
function toggleCascade(){
var panel=document.getElementById('cascadePanel');
var open=!panel.classList.contains('show');
if(open){
var rect=document.getElementById('cascadeTrigger').getBoundingClientRect();
panel.style.left=rect.left+'px';
panel.style.top=(rect.bottom+4)+'px';
cascadeL1Active='';renderCascadeL1();document.getElementById('cascadeL2').innerHTML='';
}
panel.classList.toggle('show',open);
}
function renderCascadeL1(){
var col=document.getElementById('cascadeL1');
var html=`<div class="cascade-col__title">一级分类</div>`;
html+=`<div class="cascade-opt${cascadeL1Active?'':' active'}" onclick="selectCascade('','')">全部分类</div>`;
for(var k in CATEGORIES){html+=`<div class="cascade-opt${cascadeL1Active===k?' active':''}" onmouseenter="hoverL1('${k}')" onclick="selectCascade('${k}','')">${k}<span class="arrow-r"></span></div>`;}
col.innerHTML=html;
}
function renderCascadeL2(l1){
var col=document.getElementById('cascadeL2');
if(!l1){col.innerHTML='';return;}
var subs=CATEGORIES[l1]||[];
var html=`<div class="cascade-col__title">二级分类</div>`;
html+=`<div class="cascade-opt" onclick="selectCascade('${l1}','')">全部${l1}</div>`;
subs.forEach(function(s){html+=`<div class="cascade-opt" onclick="selectCascade('${l1}','${s}')">${s}</div>`;});
col.innerHTML=html;
}
function hoverL1(l1){
cascadeL1Active=l1;
document.querySelectorAll('#cascadeL1 .cascade-opt').forEach(function(el,i){
if(i===0) el.classList.toggle('active',!l1);
else el.classList.toggle('active',el.textContent.replace('','').trim()===l1);
});
renderCascadeL2(l1);
}
function selectCascade(l1,l2){
var trig=document.getElementById('cascadeTrigger');
if(!l1){trig.textContent='食物分类';trig.classList.remove('has-value');}
else if(!l2){trig.textContent=l1;trig.classList.add('has-value');}
else{trig.textContent=l1+' / '+l2;trig.classList.add('has-value');}
document.getElementById('cascadePanel').classList.remove('show');
}
document.addEventListener('click',function(e){if(!e.target.closest('#categoryPicker'))document.getElementById('cascadePanel').classList.remove('show');});
// 初始化
renderDetail();
+1
View File
@@ -115,6 +115,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</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>
+1
View File
@@ -126,6 +126,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+1
View File
@@ -147,6 +147,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+1
View File
@@ -152,6 +152,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+1
View File
@@ -137,6 +137,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
@@ -145,6 +145,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+1
View File
@@ -115,6 +115,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+1
View File
@@ -163,6 +163,7 @@ tbody tr:hover{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+1
View File
@@ -182,6 +182,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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>
+69 -5
View File
@@ -114,6 +114,13 @@ tbody tr:hover{background:#F8FBFF;}
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;}
/* 详情弹窗 */
.detail-modal{width:720px;}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:0;}
.detail-item{display:flex;padding:12px 16px;border-bottom:1px solid #f0f0f0;}
.detail-item.full{grid-column:1/-1;}
.detail-label{width:120px;flex-shrink:0;color:var(--text-secondary);font-size:var(--font-sm);}
.detail-value{flex:1;font-size:var(--font-sm);color:var(--text-primary);}
</style>
</head>
<body>
@@ -162,6 +169,26 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 主内容区 -->
<div 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">186</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">930 kg</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">78%</div><div class="stat-card__label">A级占比</div></div>
</div>
<div class="stat-card">
<div class="stat-card__icon blue">🌱</div>
<div class="stat-card__info"><div class="stat-card__value">4</div><div class="stat-card__label">今日入库批次</div></div>
</div>
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
@@ -226,7 +253,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td>2026-04-15</td>
<td>李采收</td>
<td>RPT-20260415-001</td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="openModal()">详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>ST-FH-FB01-VG-HLUB-420K-20260415-002</td>
@@ -239,7 +266,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td>2026-04-15</td>
<td>王采收</td>
<td>RPT-20260415-002</td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="openModal()">详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>ST-FH-FC01-VG-XGUO-068K-20260414-003</td>
@@ -252,7 +279,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td>2026-04-14</td>
<td>李采收</td>
<td>RPT-20260414-003</td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="openModal()">详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>ST-FH-FD01-VG-SJJD-156K-20260414-004</td>
@@ -265,7 +292,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td>2026-04-14</td>
<td>张采收</td>
<td>RPT-20260414-004</td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="openModal()">详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
</tbody>
</table>
@@ -365,16 +392,53 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
</div>
<!-- 详情弹窗 -->
<div class="modal-overlay" id="detailModal" style="display:none;">
<div class="modal detail-modal">
<div class="modal-header">
<span class="modal-title">收成入库详情</span>
<span class="modal-close" onclick="closeDetailModal()">&times;</span>
</div>
<div class="modal-body" style="padding:0;">
<div class="detail-grid" id="detailGrid"></div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeDetailModal()">关闭</button>
</div>
</div>
</div>
<!-- 侧边栏交互脚本 -->
<script>
// 模态弹窗控制
function openModal(){document.getElementById('modalOverlay').style.display='flex';}
function openModal(mode){
document.getElementById('modalOverlay').style.display='flex';
var title=mode==='edit'?'编辑收成入库批次':'新增收成入库批次';
document.querySelector('#modalOverlay .modal-title').textContent=title;
}
function closeModal(){document.getElementById('modalOverlay').style.display='none';}
document.getElementById('modalOverlay').addEventListener('click',function(e){if(e.target===this)closeModal();});
// 视频弹窗控制
function openVideoModal(){document.getElementById('videoModal').style.display='flex';}
function closeVideoModal(){document.getElementById('videoModal').style.display='none';}
document.getElementById('videoModal').addEventListener('click',function(e){if(e.target===this)closeVideoModal();});
// 详情弹窗
function showDetail(el){
var tr=el.closest('tr');
var tds=tr.querySelectorAll('td');
var ths=tr.closest('table').querySelectorAll('thead th');
var grid=document.getElementById('detailGrid');
grid.innerHTML='';
for(var i=0;i<tds.length-1;i++){
var div=document.createElement('div');
div.className='detail-item';
div.innerHTML='<div class="detail-label">'+ths[i].textContent+'</div><div class="detail-value">'+tds[i].innerHTML+'</div>';
grid.appendChild(div);
}
document.getElementById('detailModal').style.display='flex';
}
function closeDetailModal(){document.getElementById('detailModal').style.display='none';}
document.getElementById('detailModal').addEventListener('click',function(e){if(e.target===this)closeDetailModal();});
// 切换二级菜单分组的展开/收起
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 初始化:若激活项位于子分组内,则自动展开
+578 -40
View File
@@ -93,6 +93,49 @@ tbody tr:hover{background:#F8FBFF;}
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;}
.td-actions .link{margin-right:8px;}
.td-actions .link:last-child{margin-right:0;}
/* 二级表头 */
table thead th[colspan]{text-align:center;background:#E6F7FF;color:var(--primary);font-weight:600;border-bottom:2px solid var(--primary);}
table thead .sub-header th{font-size:12px;font-weight:500;}
/* 弹窗样式 */
.modal-overlay{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;}
.modal{background:#fff;border-radius:var(--radius-md);width:680px;max-height:85vh;display:flex;flex-direction:column;box-shadow:0 8px 24px rgba(0,0,0,0.15);}
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.modal-title{font-size:var(--font-base);font-weight:600;}
.modal-close{font-size:22px;cursor:pointer;color:#999;line-height:1;}.modal-close:hover{color:#333;}
.modal-body{padding:24px;overflow-y:auto;flex:1;}
.modal-footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.form-item{display:flex;flex-direction:column;gap:6px;}
.form-item.full{grid-column:1/-1;}
.form-item label{font-size:var(--font-sm);color:var(--text-secondary);}
.form-item .required{color:var(--danger);margin-left:2px;}
.form-item input,.form-item select,.form-item textarea{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;}
.form-item input:focus,.form-item select:focus,.form-item textarea:focus{border-color:var(--primary);}
.form-item textarea{height:80px;padding:8px 12px;resize:vertical;}
.form-item select{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;appearance:none;padding-right:28px;}
/* 搜索选择框 */
.search-select{position:relative;}
.search-select input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 30px 0 12px;font-size:var(--font-sm);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Ccircle cx='5' cy='5' r='3.5' stroke='%23999' fill='none' stroke-width='1.2'/%3E%3Cpath d='M8 8l2.5 2.5' stroke='%23999' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 10px center;}
.search-select input:focus{border-color:var(--primary);}
.search-select__dropdown{display:none;position:absolute;top:100%;left:0;right:0;max-height:200px;overflow-y:auto;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);z-index:10;margin-top:4px;}
.search-select__dropdown.show{display:block;}
.search-select__option{padding:8px 12px;font-size:var(--font-sm);cursor:pointer;transition:background .15s;}
.search-select__option:hover,.search-select__option.highlighted{background:var(--primary-light);color:var(--primary);}
.search-select__empty{padding:8px 12px;font-size:var(--font-sm);color:var(--text-placeholder);text-align:center;}
/* 详情弹窗 */
.detail-modal{width:720px;}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:0;}
.detail-item{display:flex;padding:12px 16px;border-bottom:1px solid #f0f0f0;}
.detail-item.full{grid-column:1/-1;}
.detail-label{width:120px;flex-shrink:0;color:var(--text-secondary);font-size:var(--font-sm);}
.detail-value{flex:1;font-size:var(--font-sm);color:var(--text-primary);}
/* 详情分模块 */
.detail-section{padding:16px 24px;border-bottom:1px solid #f0f0f0;}
.detail-section:last-child{border-bottom:none;}
.detail-section__title{font-size:var(--font-sm);font-weight:600;color:var(--primary);margin-bottom:12px;padding-left:8px;border-left:3px solid var(--primary);}
.detail-section .detail-grid{gap:0;}
</style>
</head>
<body>
@@ -170,14 +213,14 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item"><label>地块名称</label><input class="filter-input" placeholder="请输入地块名称"></div>
<div class="filter-item"><label>作物类型</label>
<div class="filter-item"><label>种植作物</label>
<select class="filter-select"><option>全部</option><option>菠菜</option><option>白菜</option><option>胡萝卜</option><option>香菇</option><option>四季豆</option></select>
</div>
<div class="filter-item"><label>地块状态</label>
<select class="filter-select"><option>全部</option><option>种植中</option><option>休耕中</option></select>
</div>
<div class="filter-item"><label>预警状态</label>
<select class="filter-select"><option>全部</option><option>正常</option><option>湿度偏高</option></select>
<div class="filter-item"><label>预警等级</label>
<select class="filter-select"><option>全部</option><option>正常</option><option></option><option></option><option></option><option>紧急</option></select>
</div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
@@ -185,7 +228,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary"> 新增地块</button>
<button class="btn btn-primary" onclick="openPlotModal('add')"> 新增地块</button>
<button class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
</div>
@@ -196,25 +239,31 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<div class="table-card__content">
<table>
<thead><tr>
<th>地块编号</th><th>地块名称</th><th>面积(亩)</th><th>种植作物</th><th>地块状态</th>
<th>当前温度(℃)</th><th>当前湿度(%RH)</th><th>土壤pH值</th>
<th>土壤氮(mg/kg)</th><th>土壤磷(mg/kg)</th><th>土壤钾(mg/kg)</th>
<th>农药残留指数</th><th>灌溉水质等级</th><th>最近采集时间</th><th>预警状态</th><th>操作</th>
</tr></thead>
<thead>
<tr>
<th rowspan="2">地块编号</th><th rowspan="2">地块名称</th><th rowspan="2">面积(亩)</th><th rowspan="2">种植作物</th><th rowspan="2">灌溉方式</th><th rowspan="2">土壤类型</th><th rowspan="2">负责人</th><th rowspan="2">地块状态</th>
<th colspan="10">最新监测数据</th>
<th rowspan="2">操作</th>
</tr>
<tr class="sub-header">
<th>温度(℃)</th><th>湿度(%RH)</th><th>土壤pH</th>
<th>氮(mg/kg)</th><th>磷(mg/kg)</th><th>钾(mg/kg)</th>
<th>农药残留</th><th>水质等级</th><th>采集时间</th><th>预警等级</th>
</tr>
</thead>
<tbody>
<tr><td>LAND-001</td><td>A区蔬菜基地</td><td>12.5</td><td>菠菜/白菜</td><td><span class="tag tag-green">种植中</span></td>
<tr><td>LAND-001</td><td>A区蔬菜基地</td><td>12.5</td><td>菠菜/白菜</td><td>滴灌</td><td>壤土</td><td>张三</td><td><span class="tag tag-green">种植中</span></td>
<td>17.2</td><td>72</td><td>6.8</td><td>142</td><td>38</td><td>186</td><td>0.02</td>
<td><span class="tag tag-green"></span></td><td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<tr><td>LAND-002</td><td>B区根茎基地</td><td>8.0</td><td>胡萝卜</td><td><span class="tag tag-green">种植中</span></td>
<td><span class="tag tag-green"></span></td><td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" onclick="showPlotDetail(this)">详情</a><a class="link" onclick="openPlotModal('edit','LAND-001','A区蔬菜基地','12.5','菠菜/白菜','种植中')">编辑</a><a class="link" onclick="openAlarmLog('LAND-001','A区蔬菜基地','plant')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除地块 LAND-001')){}">删除</a></td></tr>
<tr><td>LAND-002</td><td>B区根茎基地</td><td>8.0</td><td>胡萝卜</td><td>喷灌</td><td>砂土</td><td>李四</td><td><span class="tag tag-green">种植中</span></td>
<td>18.4</td><td>65</td><td>7.1</td><td>128</td><td>42</td><td>210</td><td>0.01</td>
<td><span class="tag tag-blue"></span></td><td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<tr><td>LAND-003</td><td>C区菌菇大棚</td><td>3.2</td><td>香菇</td><td><span class="tag tag-green">种植中</span></td>
<td><span class="tag tag-blue"></span></td><td>2026-04-22 10:05</td><td><span class="tag tag-orange"></span></td><td class="td-actions"><a class="link" onclick="showPlotDetail(this)">详情</a><a class="link" onclick="openPlotModal('edit','LAND-002','B区根茎基地','8.0','胡萝卜','种植中')">编辑</a><a class="link" onclick="openAlarmLog('LAND-002','B区根茎基地','plant')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除地块 LAND-002')){}">删除</a></td></tr>
<tr><td>LAND-003</td><td>C区菌菇大棚</td><td>3.2</td><td>香菇</td><td>滴灌</td><td>壤土</td><td>李四</td><td><span class="tag tag-green">种植中</span></td>
<td>22.1</td><td>85</td><td>6.5</td><td>168</td><td>52</td><td>224</td><td>0.00</td>
<td><span class="tag tag-green"></span></td><td>2026-04-22 10:04</td><td><span class="tag tag-orange">湿度偏高</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<tr><td>LAND-004</td><td>D区豆类基地</td><td>6.8</td><td>四季豆</td><td><span class="tag tag-gray">休耕中</span></td>
<td><span class="tag tag-green"></span></td><td>2026-04-22 10:04</td><td><span class="tag tag-blue"></span></td><td class="td-actions"><a class="link" onclick="showPlotDetail(this)">详情</a><a class="link" onclick="openPlotModal('edit','LAND-003','C区菌菇大棚','3.2','香菇','种植中')">编辑</a><a class="link" onclick="openAlarmLog('LAND-003','C区菌菇大棚','plant')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除地块 LAND-003')){}">删除</a></td></tr>
<tr><td>LAND-004</td><td>D区豆类基地</td><td>6.8</td><td>四季豆</td><td>漫灌</td><td>黏土</td><td>王五</td><td><span class="tag tag-gray">休耕中</span></td>
<td>16.8</td><td>58</td><td>6.9</td><td>96</td><td>28</td><td>158</td><td>0.03</td>
<td><span class="tag tag-blue"></span></td><td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<td><span class="tag tag-blue"></span></td><td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" onclick="showPlotDetail(this)">详情</a><a class="link" onclick="openPlotModal('edit','LAND-004','D区豆类基地','6.8','四季豆','休耕中')">编辑</a><a class="link" onclick="openAlarmLog('LAND-004','D区豆类基地','plant')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除地块 LAND-004')){}">删除</a></td></tr>
</tbody>
</table>
</div>
@@ -243,8 +292,8 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<div class="filter-item"><label>养殖状态</label>
<select class="filter-select"><option>全部</option><option>养殖中</option><option>空置</option></select>
</div>
<div class="filter-item"><label>预警状态</label>
<select class="filter-select"><option>全部</option><option>正常</option><option>氨气偏高</option></select>
<div class="filter-item"><label>预警等级</label>
<select class="filter-select"><option>全部</option><option>正常</option><option></option><option></option><option></option><option>紧急</option></select>
</div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
@@ -252,7 +301,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary"> 新增养殖区</button>
<button class="btn btn-primary" onclick="openBreedModal('add')"> 新增养殖区</button>
<button class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
</div>
@@ -263,29 +312,34 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<div class="table-card__content">
<table>
<thead><tr>
<th>养殖区编号</th><th>养殖区名称</th><th>面积(㎡)</th><th>动物种类</th><th>养殖方式</th><th>养殖状态</th>
<th>存栏数(头/只)</th><th>当前温度(℃)</th><th>当前湿度(%RH)</th>
<th>氨气浓度(ppm)</th><th>CO₂浓度(ppm)</th><th>光照强度(lux)</th>
<th>通风状态</th><th>饮水水质</th><th>粪污处理状态</th><th>最近采集时间</th><th>预警状态</th><th>操作</th>
</tr></thead>
<thead>
<tr>
<th rowspan="2">养殖区编号</th><th rowspan="2">养殖区名称</th><th rowspan="2">面积(㎡)</th><th rowspan="2">动物种类</th><th rowspan="2">养殖方式</th><th rowspan="2">负责人</th><th rowspan="2">养殖状态</th>
<th colspan="10">最新监测数据</th>
<th rowspan="2">操作</th>
</tr>
<tr class="sub-header">
<th>温度(℃)</th><th>湿度(%RH)</th><th>氨气(ppm)</th><th>CO₂(ppm)</th><th>光照(lux)</th>
<th>通风状态</th><th>饮水水质</th><th>粪污处理</th><th>采集时间</th><th>预警等级</th>
</tr>
</thead>
<tbody>
<tr><td>BREED-A1</td><td>A1-猪舍</td><td>1,200</td><td>猪(杜洛克)</td><td><span class="tag tag-gray">圈养</span></td><td><span class="tag tag-green">养殖中</span></td>
<td>312</td><td>22.4</td><td>68</td><td>8.2</td><td>1,240</td><td>180</td>
<tr><td>BREED-A1</td><td>A1-猪舍</td><td>1,200</td><td>猪(杜洛克)</td><td>圈养</td><td>王建国</td><td><span class="tag tag-green">养殖中</span></td>
<td>22.4</td><td>68</td><td>8.2</td><td>1,240</td><td>180</td>
<td><span class="tag tag-green">正常</span></td><td><span class="tag tag-green"></span></td><td><span class="tag tag-green">正常</span></td>
<td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<tr><td>BREED-B3</td><td>B3-鸡舍</td><td>860</td><td>鸡(黄羽鸡)</td><td><span class="tag tag-green">散养</span></td><td><span class="tag tag-green">养殖中</span></td>
<td>1,176</td><td>24.6</td><td>62</td><td>12.5</td><td>1,580</td><td>320</td>
<td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" onclick="showBreedDetail(this)">详情</a><a class="link" onclick="openBreedModal('edit','BREED-A1','A1-猪舍','1200','猪','圈养','养殖中')">编辑</a><a class="link" onclick="openAlarmLog('BREED-A1','A1-猪舍','breed')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除养殖区 BREED-A1')){}">删除</a></td></tr>
<tr><td>BREED-B3</td><td>B3-鸡舍</td><td>860</td><td>鸡(黄羽鸡)</td><td>散养</td><td>李秀芳</td><td><span class="tag tag-green">养殖中</span></td>
<td>24.6</td><td>62</td><td>12.5</td><td>1,580</td><td>320</td>
<td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue"></span></td><td><span class="tag tag-green">正常</span></td>
<td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<tr><td>BREED-C2</td><td>C2-鸭舍</td><td>640</td><td>鸭(麻鸭)</td><td><span class="tag tag-green">散养</span></td><td><span class="tag tag-green">养殖中</span></td>
<td>665</td><td>23.8</td><td>70</td><td>18.6</td><td>1,820</td><td>210</td>
<td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" onclick="showBreedDetail(this)">详情</a><a class="link" onclick="openBreedModal('edit','BREED-B3','B3-鸡舍','860','鸡','散养','养殖中')">编辑</a><a class="link" onclick="openAlarmLog('BREED-B3','B3-鸡舍','breed')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除养殖区 BREED-B3')){}">删除</a></td></tr>
<tr><td>BREED-C2</td><td>C2-鸭舍</td><td>640</td><td>鸭(麻鸭)</td><td>散养</td><td>陈大明</td><td><span class="tag tag-green">养殖中</span></td>
<td>23.8</td><td>70</td><td>18.6</td><td>1,820</td><td>210</td>
<td><span class="tag tag-orange">告警</span></td><td><span class="tag tag-blue"></span></td><td><span class="tag tag-red">异常</span></td>
<td>2026-04-22 10:04</td><td><span class="tag tag-orange">氨气偏</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<tr><td>BREED-D1</td><td>D1-羊舍</td><td>480</td><td>羊(湖羊)</td><td><span class="tag tag-gray">圈养</span></td><td><span class="tag tag-gray">空置</span></td>
<td>0</td><td>20.2</td><td>55</td><td>6.4</td><td>860</td><td>150</td>
<td>2026-04-22 10:04</td><td><span class="tag tag-red"></span></td><td class="td-actions"><a class="link" onclick="showBreedDetail(this)">详情</a><a class="link" onclick="openBreedModal('edit','BREED-C2','C2-鸭舍','640','鸭','散养','养殖中')">编辑</a><a class="link" onclick="openAlarmLog('BREED-C2','C2-鸭舍','breed')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除养殖区 BREED-C2')){}">删除</a></td></tr>
<tr><td>BREED-D1</td><td>D1-羊舍</td><td>480</td><td>羊(湖羊)</td><td>圈养</td><td>赵志强</td><td><span class="tag tag-gray">空置</span></td>
<td>20.2</td><td>55</td><td>6.4</td><td>860</td><td>150</td>
<td><span class="tag tag-green">正常</span></td><td><span class="tag tag-green"></span></td><td><span class="tag tag-green">正常</span></td>
<td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><span class="link">查看详情</span></td></tr>
<td>2026-04-22 10:05</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" onclick="showBreedDetail(this)">详情</a><a class="link" onclick="openBreedModal('edit','BREED-D1','D1-羊舍','480','羊','圈养','空置')">编辑</a><a class="link" onclick="openAlarmLog('BREED-D1','D1-羊舍','breed')">采集记录</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除养殖区 BREED-D1')){}">删除</a></td></tr>
</tbody>
</table>
</div>
@@ -300,6 +354,170 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
</main>
<!-- 新增/编辑地块弹窗 -->
<div class="modal-overlay" id="plotModal">
<div class="modal">
<div class="modal-header">
<span class="modal-title" id="plotModalTitle">新增地块</span>
<span class="modal-close" onclick="closePlotModal()">&times;</span>
</div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item"><label>地块编号<span class="required">*</span></label><input id="plotId" type="text" placeholder="如 LAND-005"></div>
<div class="form-item"><label>地块名称<span class="required">*</span></label><input id="plotName" type="text" placeholder="请输入地块名称"></div>
<div class="form-item"><label>面积(亩)<span class="required">*</span></label><input id="plotArea" type="number" placeholder="请输入面积"></div>
<div class="form-item"><label>种植作物<span class="required">*</span></label>
<div class="search-select" id="plotCropSelect">
<input id="plotCrop" type="text" placeholder="搜索或选择作物" autocomplete="off">
<div class="search-select__dropdown" id="plotCropDropdown"></div>
</div>
</div>
<div class="form-item"><label>地块状态<span class="required">*</span></label>
<select id="plotStatus"><option>种植中</option><option>休耕中</option></select>
</div>
<div class="form-item"><label>灌溉方式</label>
<select><option>滴灌</option><option>喷灌</option><option>漫灌</option></select>
</div>
<div class="form-item"><label>土壤类型</label>
<select><option>壤土</option><option>砂土</option><option>黏土</option></select>
</div>
<div class="form-item"><label>负责人</label><input type="text" placeholder="请输入负责人"></div>
<div class="form-item full"><label>备注</label><textarea placeholder="请输入备注信息"></textarea></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closePlotModal()">取消</button>
<button class="btn btn-primary">确定</button>
</div>
</div>
</div>
<!-- 新增/编辑养殖区弹窗 -->
<div class="modal-overlay" id="breedModal">
<div class="modal">
<div class="modal-header">
<span class="modal-title" id="breedModalTitle">新增养殖区</span>
<span class="modal-close" onclick="closeBreedModal()">&times;</span>
</div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item"><label>养殖区编号<span class="required">*</span></label><input id="breedId" type="text" placeholder="如 BREED-E1"></div>
<div class="form-item"><label>养殖区名称<span class="required">*</span></label><input id="breedName" type="text" placeholder="请输入养殖区名称"></div>
<div class="form-item"><label>面积(㎡)<span class="required">*</span></label><input id="breedArea" type="number" placeholder="请输入面积"></div>
<div class="form-item"><label>动物种类<span class="required">*</span></label>
<select id="breedAnimal"><option></option><option></option><option></option><option></option><option></option></select>
</div>
<div class="form-item"><label>养殖方式<span class="required">*</span></label>
<select id="breedMethod"><option>圈养</option><option>散养</option></select>
</div>
<div class="form-item"><label>养殖状态<span class="required">*</span></label>
<select id="breedStatus"><option>养殖中</option><option>空置</option></select>
</div>
<div class="form-item"><label>通风系统</label>
<select><option>自动</option><option>半自动</option><option>自然通风</option></select>
</div>
<div class="form-item"><label>负责人</label><input type="text" placeholder="请输入负责人"></div>
<div class="form-item full"><label>备注</label><textarea placeholder="请输入备注信息"></textarea></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeBreedModal()">取消</button>
<button class="btn btn-primary">确定</button>
</div>
</div>
</div>
<!-- 地块详情弹窗(只读) -->
<div class="modal-overlay" id="plotDetailModal">
<div class="modal detail-modal" style="width:760px;">
<div class="modal-header">
<span class="modal-title">地块详情</span>
<span class="modal-close" onclick="closePlotDetail()">&times;</span>
</div>
<div class="modal-body" style="padding:0;" id="plotDetailContent"></div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closePlotDetail()">关闭</button>
</div>
</div>
</div>
<!-- 养殖区详情弹窗(只读) -->
<div class="modal-overlay" id="breedDetailModal">
<div class="modal detail-modal" style="width:760px;">
<div class="modal-header">
<span class="modal-title">养殖区详情</span>
<span class="modal-close" onclick="closeBreedDetail()">&times;</span>
</div>
<div class="modal-body" style="padding:0;" id="breedDetailContent"></div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeBreedDetail()">关闭</button>
</div>
</div>
</div>
<!-- 采集记录弹窗 -->
<div class="modal-overlay" id="alarmLogModal">
<div class="modal" style="width:960px;">
<div class="modal-header">
<span class="modal-title" id="alarmLogTitle">采集记录 - </span>
<span class="modal-close" onclick="closeAlarmLog()">&times;</span>
</div>
<div class="modal-body" style="padding:16px 24px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<span style="font-size:var(--font-sm);color:var(--text-secondary);">历史采集记录列表(外部列表仅显示最新一条)</span>
<button class="btn btn-primary" onclick="openAlarmEdit('add')"> 新增记录</button>
</div>
<div style="overflow-x:auto;">
<table style="width:100%;font-size:var(--font-sm);min-width:900px;">
<thead id="alarmLogHead">
<!-- JS根据类型动态填充 -->
</thead>
<tbody id="alarmLogBody">
<!-- JS动态填充 -->
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeAlarmLog()">关闭</button>
</div>
</div>
</div>
<!-- 新增/编辑采集记录弹窗 -->
<div class="modal-overlay" id="alarmEditModal" style="z-index:210;">
<div class="modal" style="width:680px;">
<div class="modal-header">
<span class="modal-title" id="alarmEditTitle">新增记录</span>
<span class="modal-close" onclick="closeAlarmEdit()">&times;</span>
</div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item"><label>采集时间<span class="required">*</span></label><input id="alarmTime" type="datetime-local"></div>
<div class="form-item"><label>预警等级<span class="required">*</span></label>
<select id="alarmLevel"><option>正常</option><option></option><option></option><option></option><option>紧急</option></select>
</div>
<div class="form-item"><label>处理状态<span class="required">*</span></label>
<select id="alarmStatus"><option>待处理</option><option>处理中</option><option>已处理</option><option>已忽略</option></select>
</div>
<div class="form-item"><label>处理人</label><input id="alarmHandler" type="text" placeholder="请输入处理人"></div>
<div class="form-item full"><label>描述</label><textarea id="alarmDesc" placeholder="请输入预警详细描述"></textarea></div>
</div>
<!-- 监测数据区域 -->
<div style="margin-top:16px;padding-top:16px;border-top:1px solid var(--border);">
<div style="font-size:var(--font-sm);font-weight:600;margin-bottom:12px;color:var(--text-primary);">📊 预警时监测数据</div>
<div class="form-grid" id="alarmMonitorFields">
<!-- JS根据类型动态渲染 -->
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeAlarmEdit()">取消</button>
<button class="btn btn-primary" onclick="saveAlarmRecord()">确定</button>
</div>
</div>
</div>
<script>
// Tab 切换
function switchTab(idx){
@@ -308,8 +526,328 @@ function switchTab(idx){
}
// 侧边栏子组展开/收起
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 默认展开包含 active 项的子组
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
// --- 地块弹窗 ---
function openPlotModal(mode, id, name, area, crop, status){
document.getElementById('plotModalTitle').textContent = mode==='edit' ? '编辑地块' : '新增地块';
if(mode==='edit'){
document.getElementById('plotId').value=id||'';
document.getElementById('plotName').value=name||'';
document.getElementById('plotArea').value=area||'';
document.getElementById('plotCrop').value=crop||'';
document.getElementById('plotStatus').value=status||'种植中';
document.getElementById('plotId').readOnly=true;
} else {
document.getElementById('plotId').value='';document.getElementById('plotName').value='';
document.getElementById('plotArea').value='';document.getElementById('plotCrop').value='';
document.getElementById('plotId').readOnly=false;
}
document.getElementById('plotModal').style.display='flex';
}
function closePlotModal(){document.getElementById('plotModal').style.display='none';}
document.getElementById('plotModal').addEventListener('click',function(e){if(e.target===this)closePlotModal();});
// --- 养殖区弹窗 ---
function openBreedModal(mode, id, name, area, animal, method, status){
document.getElementById('breedModalTitle').textContent = mode==='edit' ? '编辑养殖区' : '新增养殖区';
if(mode==='edit'){
document.getElementById('breedId').value=id||'';
document.getElementById('breedName').value=name||'';
document.getElementById('breedArea').value=area||'';
document.getElementById('breedAnimal').value=animal||'猪';
document.getElementById('breedMethod').value=method||'圈养';
document.getElementById('breedStatus').value=status||'养殖中';
document.getElementById('breedId').readOnly=true;
} else {
document.getElementById('breedId').value='';document.getElementById('breedName').value='';
document.getElementById('breedArea').value='';document.getElementById('breedId').readOnly=false;
}
document.getElementById('breedModal').style.display='flex';
}
function closeBreedModal(){document.getElementById('breedModal').style.display='none';}
document.getElementById('breedModal').addEventListener('click',function(e){if(e.target===this)closeBreedModal();});
// --- 地块详情 ---
function showPlotDetail(el){
var tr=el.closest('tr');
var cells=tr.querySelectorAll('td');
var id=cells[0].textContent, name=cells[1].textContent, area=cells[2].textContent;
var crop=cells[3].textContent, irrigation=cells[4].textContent, soil=cells[5].textContent;
var person=cells[6].textContent, status=cells[7].textContent;
var temp=cells[8].textContent, hum=cells[9].textContent, ph=cells[10].textContent;
var n=cells[11].textContent, p=cells[12].textContent, k=cells[13].textContent;
var pest=cells[14].textContent, water=cells[15].textContent;
var time=cells[16].textContent, warn=cells[17].textContent;
// 模拟备注数据
var remarks={'LAND-001':'有机种植示范区,禁止使用化学农药','LAND-002':'适合根茎类作物,排水良好','LAND-003':'恒温恒湿大棚,需定期通风','LAND-004':'休耕期进行土壤改良,计划下季种植大豆'};
var remark=remarks[id]||'暂无备注';
var html='<div class="detail-section"><div class="detail-section__title">基本信息</div><div class="detail-grid">';
html+='<div class="detail-item"><span class="detail-label">地块编号</span><span class="detail-value">'+id+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">地块名称</span><span class="detail-value">'+name+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">面积(亩)</span><span class="detail-value">'+area+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">地块状态</span><span class="detail-value">'+status+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">负责人</span><span class="detail-value">'+person+'</span></div>';
html+='</div></div>';
html+='<div class="detail-section"><div class="detail-section__title">种植信息</div><div class="detail-grid">';
html+='<div class="detail-item"><span class="detail-label">种植作物</span><span class="detail-value">'+crop+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">灌溉方式</span><span class="detail-value">'+irrigation+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">土壤类型</span><span class="detail-value">'+soil+'</span></div>';
html+='</div></div>';
html+='<div class="detail-section"><div class="detail-section__title">最新监测数据</div><div class="detail-grid">';
html+='<div class="detail-item"><span class="detail-label">温度(℃)</span><span class="detail-value">'+temp+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">湿度(%RH)</span><span class="detail-value">'+hum+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">土壤pH</span><span class="detail-value">'+ph+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">氮(mg/kg)</span><span class="detail-value">'+n+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">磷(mg/kg)</span><span class="detail-value">'+p+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">钾(mg/kg)</span><span class="detail-value">'+k+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">农药残留</span><span class="detail-value">'+pest+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">水质等级</span><span class="detail-value">'+water+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">采集时间</span><span class="detail-value">'+time+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">预警等级</span><span class="detail-value">'+warn+'</span></div>';
html+='</div></div>';
html+='<div class="detail-section"><div class="detail-section__title">备注</div><div class="detail-grid">';
html+='<div class="detail-item full"><span class="detail-label">备注信息</span><span class="detail-value">'+remark+'</span></div>';
html+='</div></div>';
document.getElementById('plotDetailContent').innerHTML=html;
document.getElementById('plotDetailModal').style.display='flex';
}
function closePlotDetail(){document.getElementById('plotDetailModal').style.display='none';}
document.getElementById('plotDetailModal').addEventListener('click',function(e){if(e.target===this)closePlotDetail();});
// --- 养殖区详情 ---
function showBreedDetail(el){
var tr=el.closest('tr');
var cells=tr.querySelectorAll('td');
var id=cells[0].textContent, name=cells[1].textContent, area=cells[2].textContent;
var animal=cells[3].textContent, method=cells[4].textContent, person=cells[5].textContent;
var status=cells[6].textContent, temp=cells[7].textContent, hum=cells[8].textContent;
var nh3=cells[9].textContent, co2=cells[10].textContent, lux=cells[11].textContent;
var vent=cells[12].textContent, water=cells[13].textContent, waste=cells[14].textContent;
var time=cells[15].textContent, warn=cells[16].textContent;
// 模拟备注数据
var remarks={'BREED-A1':'标准化猪舍,配备自动喂料系统','BREED-B3':'散养鸡舍,林下养殖模式','BREED-C2':'水禽养殖区,需加强通风管理','BREED-D1':'空置中,计划下月引进新批次湖羊'};
var remark=remarks[id]||'暂无备注';
var html='<div class="detail-section"><div class="detail-section__title">基本信息</div><div class="detail-grid">';
html+='<div class="detail-item"><span class="detail-label">养殖区编号</span><span class="detail-value">'+id+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">养殖区名称</span><span class="detail-value">'+name+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">面积(㎡)</span><span class="detail-value">'+area+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">负责人</span><span class="detail-value">'+person+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">养殖状态</span><span class="detail-value">'+status+'</span></div>';
html+='</div></div>';
html+='<div class="detail-section"><div class="detail-section__title">养殖信息</div><div class="detail-grid">';
html+='<div class="detail-item"><span class="detail-label">动物种类</span><span class="detail-value">'+animal+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">养殖方式</span><span class="detail-value">'+method+'</span></div>';
html+='</div></div>';
html+='<div class="detail-section"><div class="detail-section__title">最新监测数据</div><div class="detail-grid">';
html+='<div class="detail-item"><span class="detail-label">温度(℃)</span><span class="detail-value">'+temp+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">湿度(%RH)</span><span class="detail-value">'+hum+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">氨气(ppm)</span><span class="detail-value">'+nh3+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">CO₂(ppm)</span><span class="detail-value">'+co2+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">光照(lux)</span><span class="detail-value">'+lux+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">通风状态</span><span class="detail-value">'+vent+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">饮水水质</span><span class="detail-value">'+water+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">粪污处理</span><span class="detail-value">'+waste+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">采集时间</span><span class="detail-value">'+time+'</span></div>';
html+='<div class="detail-item"><span class="detail-label">预警等级</span><span class="detail-value">'+warn+'</span></div>';
html+='</div></div>';
html+='<div class="detail-section"><div class="detail-section__title">备注</div><div class="detail-grid">';
html+='<div class="detail-item full"><span class="detail-label">备注信息</span><span class="detail-value">'+remark+'</span></div>';
html+='</div></div>';
document.getElementById('breedDetailContent').innerHTML=html;
document.getElementById('breedDetailModal').style.display='flex';
}
function closeBreedDetail(){document.getElementById('breedDetailModal').style.display='none';}
document.getElementById('breedDetailModal').addEventListener('click',function(e){if(e.target===this)closeBreedDetail();});
// --- 种植作物搜索选择框 ---
(function(){
var cropOptions=['菠菜','白菜','胡萝卜','香菇','四季豆','番茄','黄瓜','茄子','辣椒','西兰花','芹菜','生菜','土豆','红薯','玉米','小麦','水稻','大豆','花生','油菜'];
var input=document.getElementById('plotCrop');
var dropdown=document.getElementById('plotCropDropdown');
function renderOptions(keyword){
var filtered=keyword?cropOptions.filter(function(c){return c.indexOf(keyword)>-1;}):cropOptions;
if(filtered.length===0){
dropdown.innerHTML='<div class="search-select__empty">无匹配结果</div>';
} else {
dropdown.innerHTML=filtered.map(function(c){return '<div class="search-select__option">'+c+'</div>';}).join('');
}
dropdown.classList.add('show');
}
input.addEventListener('focus',function(){renderOptions(this.value);});
input.addEventListener('input',function(){renderOptions(this.value);});
dropdown.addEventListener('click',function(e){
if(e.target.classList.contains('search-select__option')){
input.value=e.target.textContent;
dropdown.classList.remove('show');
}
});
document.addEventListener('click',function(e){
if(!document.getElementById('plotCropSelect').contains(e.target)){
dropdown.classList.remove('show');
}
});
})();
// --- 预警记录弹窗逻辑 ---
var alarmData={
'LAND-001':[],
'LAND-002':[
{time:'2026-04-22 09:30',level:'中',desc:'大棚内温度持续超过30℃达2小时',status:'已处理',handler:'张三',monitor:{temp:'31.2',hum:'65',ph:'7.1',n:'128',p:'42',k:'210',pesticide:'0.01',water:'良'}}
],
'LAND-003':[
{time:'2026-04-22 08:15',level:'低',desc:'菌菇大棚湿度达85%,超出适宜范围',status:'处理中',handler:'李四',monitor:{temp:'22.1',hum:'85',ph:'6.5',n:'168',p:'52',k:'224',pesticide:'0.00',water:'优'}},
{time:'2026-04-20 14:22',level:'中',desc:'连续降雨导致棚内湿度异常升高至92%',status:'已处理',handler:'李四',monitor:{temp:'20.8',hum:'92',ph:'6.4',n:'165',p:'50',k:'220',pesticide:'0.00',water:'优'}}
],
'LAND-004':[],
'BREED-A1':[],
'BREED-B3':[],
'BREED-C2':[
{time:'2026-04-22 07:45',level:'高',desc:'鸭舍氨气浓度达18.6ppm,超出安全阈值',status:'待处理',handler:'',monitor:{temp:'23.8',hum:'70',nh3:'18.6',co2:'1820',lux:'210',vent:'告警',water:'良',waste:'异常'}},
{time:'2026-04-19 16:30',level:'中',desc:'通风设备故障导致空气流通不畅',status:'已处理',handler:'王五',monitor:{temp:'25.2',hum:'74',nh3:'15.8',co2:'1960',lux:'180',vent:'故障',water:'良',waste:'正常'}}
],
'BREED-D1':[]
};
var currentAlarmId='';
var currentAlarmType='';
var editingAlarmIdx=-1;
function getLevelTag(level){
var map={'低':'tag-blue','中':'tag-orange','高':'tag-red','紧急':'tag-red'};
return '<span class="tag '+(map[level]||'tag-gray')+'">'+level+'</span>';
}
function getStatusTag(status){
var map={'待处理':'tag-orange','处理中':'tag-blue','已处理':'tag-green','已忽略':'tag-gray'};
return '<span class="tag '+(map[status]||'tag-gray')+'">'+status+'</span>';
}
function renderAlarmLog(){
var records=alarmData[currentAlarmId]||[];
var thead=document.getElementById('alarmLogHead');
var tbody=document.getElementById('alarmLogBody');
// 根据类型渲染不同表头
if(currentAlarmType==='plant'){
thead.innerHTML='<tr><th>采集时间</th><th>预警等级</th><th>温度(℃)</th><th>湿度(%RH)</th><th>土壤pH</th><th></th><th></th><th></th><th>农药残留</th><th>水质</th><th>处理状态</th><th>处理人</th><th>操作</th></tr>';
} else {
thead.innerHTML='<tr><th>采集时间</th><th>预警等级</th><th>温度(℃)</th><th>湿度(%RH)</th><th>氨气(ppm)</th><th>CO₂(ppm)</th><th>光照(lux)</th><th>通风</th><th>水质</th><th>粪污</th><th>处理状态</th><th>处理人</th><th>操作</th></tr>';
}
var colCount=13;
if(records.length===0){
tbody.innerHTML='<tr><td colspan="'+colCount+'" style="text-align:center;padding:24px;color:var(--text-placeholder);">暂无记录</td></tr>';
return;
}
tbody.innerHTML=records.map(function(r,i){
var m=r.monitor||{};
var monitorCells='';
if(currentAlarmType==='plant'){
monitorCells='<td>'+(m.temp||'-')+'</td><td>'+(m.hum||'-')+'</td><td>'+(m.ph||'-')+'</td><td>'+(m.n||'-')+'</td><td>'+(m.p||'-')+'</td><td>'+(m.k||'-')+'</td><td>'+(m.pesticide||'-')+'</td><td>'+(m.water||'-')+'</td>';
} else {
monitorCells='<td>'+(m.temp||'-')+'</td><td>'+(m.hum||'-')+'</td><td>'+(m.nh3||'-')+'</td><td>'+(m.co2||'-')+'</td><td>'+(m.lux||'-')+'</td><td>'+(m.vent||'-')+'</td><td>'+(m.water||'-')+'</td><td>'+(m.waste||'-')+'</td>';
}
return '<tr><td>'+r.time+'</td><td>'+getLevelTag(r.level)+'</td>'+monitorCells+'<td>'+getStatusTag(r.status)+'</td><td>'+(r.handler||'-')+'</td><td class="td-actions"><a class="link" onclick="openAlarmEdit(\'edit\','+i+')">编辑</a><a class="link" style="color:var(--danger)" onclick="deleteAlarm('+i+')">删除</a></td></tr>';
}).join('');
}
function openAlarmLog(id,name,type){
currentAlarmId=id;
currentAlarmType=type;
document.getElementById('alarmLogTitle').textContent='采集记录 - '+name+''+id+'';
renderAlarmLog();
document.getElementById('alarmLogModal').style.display='flex';
}
function closeAlarmLog(){document.getElementById('alarmLogModal').style.display='none';}
document.getElementById('alarmLogModal').addEventListener('click',function(e){if(e.target===this)closeAlarmLog();});
function openAlarmEdit(mode,idx){
editingAlarmIdx=(mode==='edit')?idx:-1;
document.getElementById('alarmEditTitle').textContent=mode==='edit'?'编辑':'新增';
// 渲染监测数据字段
var mf=document.getElementById('alarmMonitorFields');
if(currentAlarmType==='plant'){
mf.innerHTML='<div class="form-item"><label>温度(℃)</label><input id="am_temp" type="number" step="0.1" placeholder="如 22.1"></div><div class="form-item"><label>湿度(%RH)</label><input id="am_hum" type="number" placeholder="如 72"></div><div class="form-item"><label>土壤pH</label><input id="am_ph" type="number" step="0.1" placeholder="如 6.8"></div><div class="form-item"><label>氮(mg/kg)</label><input id="am_n" type="number" placeholder="如 142"></div><div class="form-item"><label>磷(mg/kg)</label><input id="am_p" type="number" placeholder="如 38"></div><div class="form-item"><label>钾(mg/kg)</label><input id="am_k" type="number" placeholder="如 186"></div><div class="form-item"><label>农药残留指数</label><input id="am_pesticide" type="number" step="0.01" placeholder="如 0.02"></div><div class="form-item"><label>水质等级</label><select id="am_water"><option></option><option></option><option>一般</option><option></option></select></div>';
} else {
mf.innerHTML='<div class="form-item"><label>温度(℃)</label><input id="am_temp" type="number" step="0.1" placeholder="如 22.4"></div><div class="form-item"><label>湿度(%RH)</label><input id="am_hum" type="number" placeholder="如 68"></div><div class="form-item"><label>氨气(ppm)</label><input id="am_nh3" type="number" step="0.1" placeholder="如 8.2"></div><div class="form-item"><label>CO₂(ppm)</label><input id="am_co2" type="number" placeholder="如 1240"></div><div class="form-item"><label>光照(lux)</label><input id="am_lux" type="number" placeholder="如 180"></div><div class="form-item"><label>通风状态</label><select id="am_vent"><option>正常</option><option>告警</option><option>故障</option></select></div><div class="form-item"><label>饮水水质</label><select id="am_water"><option></option><option></option><option>一般</option><option></option></select></div><div class="form-item"><label>粪污处理</label><select id="am_waste"><option>正常</option><option>异常</option></select></div>';
}
if(mode==='edit'){
var r=alarmData[currentAlarmId][idx];
document.getElementById('alarmTime').value=r.time.replace(' ','T');
document.getElementById('alarmLevel').value=r.level;
document.getElementById('alarmStatus').value=r.status;
document.getElementById('alarmHandler').value=r.handler||'';
document.getElementById('alarmDesc').value=r.desc||'';
// 填充监测数据
var m=r.monitor||{};
if(currentAlarmType==='plant'){
if(m.temp)document.getElementById('am_temp').value=m.temp;
if(m.hum)document.getElementById('am_hum').value=m.hum;
if(m.ph)document.getElementById('am_ph').value=m.ph;
if(m.n)document.getElementById('am_n').value=m.n;
if(m.p)document.getElementById('am_p').value=m.p;
if(m.k)document.getElementById('am_k').value=m.k;
if(m.pesticide)document.getElementById('am_pesticide').value=m.pesticide;
if(m.water)document.getElementById('am_water').value=m.water;
} else {
if(m.temp)document.getElementById('am_temp').value=m.temp;
if(m.hum)document.getElementById('am_hum').value=m.hum;
if(m.nh3)document.getElementById('am_nh3').value=m.nh3;
if(m.co2)document.getElementById('am_co2').value=m.co2;
if(m.lux)document.getElementById('am_lux').value=m.lux;
if(m.vent)document.getElementById('am_vent').value=m.vent;
if(m.water)document.getElementById('am_water').value=m.water;
if(m.waste)document.getElementById('am_waste').value=m.waste;
}
} else {
document.getElementById('alarmTime').value='';
document.getElementById('alarmLevel').value='低';
document.getElementById('alarmStatus').value='待处理';
document.getElementById('alarmHandler').value='';
document.getElementById('alarmDesc').value='';
}
document.getElementById('alarmEditModal').style.display='flex';
}
function closeAlarmEdit(){document.getElementById('alarmEditModal').style.display='none';}
document.getElementById('alarmEditModal').addEventListener('click',function(e){if(e.target===this)closeAlarmEdit();});
function saveAlarmRecord(){
var time=document.getElementById('alarmTime').value.replace('T',' ');
var level=document.getElementById('alarmLevel').value;
var status=document.getElementById('alarmStatus').value;
var handler=document.getElementById('alarmHandler').value;
var desc=document.getElementById('alarmDesc').value;
if(!time){alert('请填写采集时间');return;}
// 收集监测数据
var monitor={};
if(currentAlarmType==='plant'){
monitor={temp:document.getElementById('am_temp').value,hum:document.getElementById('am_hum').value,ph:document.getElementById('am_ph').value,n:document.getElementById('am_n').value,p:document.getElementById('am_p').value,k:document.getElementById('am_k').value,pesticide:document.getElementById('am_pesticide').value,water:document.getElementById('am_water').value};
} else {
monitor={temp:document.getElementById('am_temp').value,hum:document.getElementById('am_hum').value,nh3:document.getElementById('am_nh3').value,co2:document.getElementById('am_co2').value,lux:document.getElementById('am_lux').value,vent:document.getElementById('am_vent').value,water:document.getElementById('am_water').value,waste:document.getElementById('am_waste').value};
}
var record={time:time,level:level,desc:desc,status:status,handler:handler,monitor:monitor};
if(!alarmData[currentAlarmId])alarmData[currentAlarmId]=[];
if(editingAlarmIdx>=0){
alarmData[currentAlarmId][editingAlarmIdx]=record;
} else {
alarmData[currentAlarmId].unshift(record);
}
closeAlarmEdit();
renderAlarmLog();
}
function deleteAlarm(idx){
if(!confirm('确认删除该条记录?'))return;
alarmData[currentAlarmId].splice(idx,1);
renderAlarmLog();
}
</script>
</body>
</html>
+49 -5
View File
@@ -115,6 +115,13 @@ tbody tr:hover{background:#F8FBFF;}
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;}
/* 详情弹窗 */
.detail-modal{width:720px;}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:0;}
.detail-item{display:flex;padding:12px 16px;border-bottom:1px solid #f0f0f0;}
.detail-item.full{grid-column:1/-1;}
.detail-label{width:120px;flex-shrink:0;color:var(--text-secondary);font-size:var(--font-sm);}
.detail-value{flex:1;font-size:var(--font-sm);color:var(--text-primary);}
</style>
</head>
<body>
@@ -271,7 +278,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td></td>
<td>2026-04-24</td>
<td>李巡检</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>CROP-202604-002</td>
@@ -285,7 +292,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td></td>
<td>2026-04-28</td>
<td>王巡检</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>CROP-202604-003</td>
@@ -299,7 +306,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td>2026-04-21 喷施苦参碱,待复查</td>
<td>待定</td>
<td>李巡检</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>CROP-202604-004</td>
@@ -313,7 +320,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td></td>
<td>2026-04-26</td>
<td>张巡检</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
</tbody>
</table>
@@ -390,12 +397,49 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
</div>
<!-- 详情弹窗 -->
<div class="modal-overlay" id="detailModal" style="display:none;">
<div class="modal detail-modal">
<div class="modal-header">
<span class="modal-title">巡检记录详情</span>
<span class="modal-close" onclick="closeDetailModal()">&times;</span>
</div>
<div class="modal-body" style="padding:0;">
<div class="detail-grid" id="detailGrid"></div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeDetailModal()">关闭</button>
</div>
</div>
</div>
<!-- 侧边栏交互脚本 -->
<script>
// 模态弹窗控制
function openModal(){document.getElementById('modalOverlay').style.display='flex';}
function openModal(mode){
document.getElementById('modalOverlay').style.display='flex';
var title=mode==='edit'?'编辑巡检记录':'新增巡检记录';
document.querySelector('#modalOverlay .modal-title').textContent=title;
}
function closeModal(){document.getElementById('modalOverlay').style.display='none';}
document.getElementById('modalOverlay').addEventListener('click',function(e){if(e.target===this)closeModal();});
// 详情弹窗
function showDetail(el){
var tr=el.closest('tr');
var tds=tr.querySelectorAll('td');
var ths=tr.closest('table').querySelectorAll('thead th');
var grid=document.getElementById('detailGrid');
grid.innerHTML='';
for(var i=0;i<tds.length-1;i++){
var div=document.createElement('div');
div.className='detail-item';
div.innerHTML='<div class="detail-label">'+ths[i].textContent+'</div><div class="detail-value">'+tds[i].innerHTML+'</div>';
grid.appendChild(div);
}
document.getElementById('detailModal').style.display='flex';
}
function closeDetailModal(){document.getElementById('detailModal').style.display='none';}
document.getElementById('detailModal').addEventListener('click',function(e){if(e.target===this)closeDetailModal();});
// 折叠/展开子分组
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 初始化:若当前激活项在子分组内,展开其父分组
@@ -114,6 +114,13 @@ tbody tr:hover{background:#F8FBFF;}
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;}
/* 详情弹窗 */
.detail-modal{width:720px;}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:0;}
.detail-item{display:flex;padding:12px 16px;border-bottom:1px solid #f0f0f0;}
.detail-item.full{grid-column:1/-1;}
.detail-label{width:140px;flex-shrink:0;color:var(--text-secondary);font-size:var(--font-sm);}
.detail-value{flex:1;font-size:var(--font-sm);color:var(--text-primary);}
</style>
</head>
<body>
@@ -236,7 +243,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary" onclick="openModal()"> 新增养殖批次</button>
<button class="btn btn-default">💉 记录疫苗用药</button>
<button class="btn btn-default" onclick="openVaccineModal()">💉 记录疫苗用药</button>
<button class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
</div>
@@ -277,7 +284,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td><span class="tag tag-green"></span></td>
<td><span class="tag tag-green">正常</span></td>
<td>2026-05-20</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>ST-FH-FL02-MT-HWJJ-1200-20260315-001</td>
@@ -291,7 +298,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td><span class="tag tag-blue"></span></td>
<td><span class="tag tag-green">正常</span></td>
<td>2026-05-05</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>ST-FH-FL03-MT-MAYU-680H-20260320-001</td>
@@ -305,7 +312,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td><span class="tag tag-blue"></span></td>
<td><span class="tag tag-orange">轻微异常</span></td>
<td>2026-05-12</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
<tr>
<td>ST-FH-FL04-MT-HUYA-180H-20260405-001</td>
@@ -319,7 +326,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<td><span class="tag tag-green"></span></td>
<td><span class="tag tag-green">正常</span></td>
<td>2026-06-01</td>
<td class="td-actions"><a class="link" onclick="openModal()">查看详情</a> <a class="link" onclick="openModal()">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
<td class="td-actions"><a class="link" onclick="showDetail(this)">详情</a> <a class="link" onclick="openModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td>
</tr>
</tbody>
</table>
@@ -403,12 +410,90 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
</div>
<!-- 疫苗用药记录弹窗 -->
<div class="modal-overlay" id="vaccineModal" style="display:none;">
<div class="modal">
<div class="modal-header">
<span class="modal-title">记录疫苗/用药</span>
<span class="modal-close" onclick="closeVaccineModal()">&times;</span>
</div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item"><label>养殖批次<span class="required">*</span></label>
<select><option>请选择</option><option>ST-FH-FL01-MT-DLLZ(杜洛克-A1猪舍)</option><option>ST-FH-FL02-MT-HWJJ(黄羽鸡-B3鸡舍)</option><option>ST-FH-FL03-MT-MAYU(麻鸭-C2鸭舍)</option><option>ST-FH-FL04-MT-HUYA(湖羊-D1羊舍)</option></select>
</div>
<div class="form-item"><label>类型<span class="required">*</span></label>
<select><option>预防接种</option><option>疾病治疗</option><option>驱虫</option></select>
</div>
<div class="form-item"><label>药物/疫苗名称<span class="required">*</span></label><input type="text" placeholder="请输入药物或疫苗名称"></div>
<div class="form-item"><label>用药日期<span class="required">*</span></label><input type="date"></div>
<div class="form-item"><label>用药剂量</label><input type="text" placeholder="如:2ml/头"></div>
<div class="form-item"><label>停药期(天)</label><input type="number" placeholder="请输入停药期天数"></div>
<div class="form-item"><label>操作人<span class="required">*</span></label><input type="text" placeholder="请输入操作人"></div>
<div class="form-item"><label>用药原因</label>
<select><option>常规免疫</option><option>疫情防控</option><option>个体治疗</option></select>
</div>
<div class="form-item full"><label>备注</label><textarea placeholder="请输入备注信息"></textarea></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeVaccineModal()">取消</button>
<button class="btn btn-primary">确定</button>
</div>
</div>
</div>
<!-- 养殖批次详情弹窗(只读) -->
<div class="modal-overlay" id="detailModal" style="display:none;">
<div class="modal detail-modal">
<div class="modal-header">
<span class="modal-title">养殖批次详情</span>
<span class="modal-close" onclick="closeDetailModal()">&times;</span>
</div>
<div class="modal-body" style="padding:0;">
<div class="detail-grid" id="detailGrid"></div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeDetailModal()">关闭</button>
</div>
</div>
</div>
<!-- 侧边栏交互脚本 -->
<script>
// 模态弹窗控制
function openModal(){document.getElementById('modalOverlay').style.display='flex';}
// 模态弹窗控制(新增/编辑)
function openModal(mode){
var overlay=document.getElementById('modalOverlay');
var title=overlay.querySelector('.modal-title');
title.textContent=mode==='edit'?'编辑养殖批次':'新增畜禽养殖批次';
overlay.style.display='flex';
}
function closeModal(){document.getElementById('modalOverlay').style.display='none';}
document.getElementById('modalOverlay').addEventListener('click',function(e){if(e.target===this)closeModal();});
// 疫苗用药弹窗
function openVaccineModal(){document.getElementById('vaccineModal').style.display='flex';}
function closeVaccineModal(){document.getElementById('vaccineModal').style.display='none';}
document.getElementById('vaccineModal').addEventListener('click',function(e){if(e.target===this)closeVaccineModal();});
// 详情弹窗
var detailLabels=['养殖批次号','动物种类','品种','养殖区/舍编号','入栏数量(头/只)','入栏日期','当前存栏数','平均体重(kg)','长势评级','健康状态','预计出栏日期'];
function showDetail(el){
var tr=el.closest('tr');
var tds=tr.querySelectorAll('td');
var grid=document.getElementById('detailGrid');
grid.innerHTML='';
for(var i=0;i<detailLabels.length;i++){
var div=document.createElement('div');
div.className='detail-item'+(i===0?' full':'');
div.innerHTML='<span class="detail-label">'+detailLabels[i]+'</span><span class="detail-value">'+tds[i].innerHTML+'</span>';
grid.appendChild(div);
}
document.getElementById('detailModal').style.display='flex';
}
function closeDetailModal(){document.getElementById('detailModal').style.display='none';}
document.getElementById('detailModal').addEventListener('click',function(e){if(e.target===this)closeDetailModal();});
// 切换子分组展开/收起状态
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 初始化:若 active 项在子分组内,则默认展开该分组
+132 -13
View File
@@ -102,6 +102,22 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
.modal-close:hover{color:#262626;}
.modal-body{padding:24px;}
.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid #e8e8e8;}
.td-actions .link{margin-right:8px;}
.td-actions .link:last-child{margin-right:0;}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.form-item{display:flex;flex-direction:column;gap:6px;}
.form-item.full{grid-column:1/-1;}
.form-item label{font-size:var(--font-sm);color:var(--text-secondary);}
.form-item .required{color:var(--danger);margin-left:2px;}
.form-item input,.form-item select,.form-item textarea{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;}
.form-item input:focus,.form-item select:focus,.form-item textarea:focus{border-color:var(--primary);}
.form-item textarea{height:80px;padding:8px 12px;resize:vertical;}
.form-item select{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;appearance:none;padding-right:28px;}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:0;}
.detail-item{display:flex;padding:12px 16px;border-bottom:1px solid #f0f0f0;}
.detail-item.full{grid-column:1/-1;}
.detail-label{width:120px;flex-shrink:0;color:var(--text-secondary);font-size:var(--font-sm);}
.detail-value{flex:1;font-size:var(--font-sm);color:var(--text-primary);}
</style>
</head>
<body>
@@ -150,6 +166,26 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 主内容区 -->
<div 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">286</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">5单</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">¥19,373</div><div class="stat-card__label">本月采购金额</div></div>
</div>
<div class="stat-card">
<div class="stat-card__icon blue">🏭</div>
<div class="stat-card__info"><div class="stat-card__value">4家</div><div class="stat-card__label">本月供应商</div></div>
</div>
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
@@ -184,7 +220,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary"> 新建采购单</button>
<button class="btn btn-primary" onclick="openPurchaseModal('add')"> 新建采购单</button>
<button class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
</div>
@@ -204,18 +240,18 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</tr>
</thead>
<tbody>
<tr><td>PO-20260415-001</td><td>ST-EX-LY01-VG-BOCA-050K-20260415-002</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>菠菜</td><td>蔬菜类</td><td>50</td><td>¥2.5</td><td>¥125</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-001</td><td>ST-EX-LY01-VG-BCAI-080K-20260415-003</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>白菜</td><td>蔬菜类</td><td>80</td><td>¥1.8</td><td>¥144</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-001</td><td>ST-EX-LY01-VG-HLUB-120K-20260415-004</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>胡萝卜</td><td>蔬菜类</td><td>120</td><td>¥1.8</td><td>¥216</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-002</td><td>ST-EX-XR01-MT-ZLRJ-080K-20260415-005</td><td><span class="tag tag-blue">食谱搭配</span></td><td>鲜肉直供公司</td><td>猪里脊</td><td>肉禽类</td><td>80</td><td>¥28</td><td>¥2,240</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-002</td><td>ST-EX-XR01-MT-ZWUA-240K-20260415-006</td><td><span class="tag tag-blue">食谱搭配</span></td><td>鲜肉直供公司</td><td>猪五花</td><td>肉禽类</td><td>240</td><td>¥26</td><td>¥6,240</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260414-003</td><td>ST-EX-HS01-SF-CAYO-060K-20260414-001</td><td><span class="tag tag-gray">手动创建</span></td><td>海鲜水产配送</td><td>草鱼</td><td>水产类</td><td>60</td><td>¥18</td><td>¥1,080</td><td>2026-04-14</td><td>2026-04-15</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260414-003</td><td>ST-EX-HS01-SF-JIYU-080K-20260414-002</td><td><span class="tag tag-gray">手动创建</span></td><td>海鲜水产配送</td><td>鲫鱼</td><td>水产类</td><td>80</td><td>¥16</td><td>¥1,280</td><td>2026-04-14</td><td>2026-04-15</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260414-003</td><td>ST-EX-HS01-SF-XIAA-050K-20260414-003</td><td><span class="tag tag-gray">手动创建</span></td><td>海鲜水产配送</td><td></td><td>水产类</td><td>50</td><td>¥30</td><td>¥1,500</td><td>2026-04-14</td><td>2026-04-15</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260413-004</td><td>ST-EX-LY02-DG-DAMI-1000-20260413-001</td><td><span class="tag tag-gray">手动创建</span></td><td>粮油批发商行</td><td>大米</td><td>粮油类</td><td>1000</td><td>¥3.5</td><td>¥3,500</td><td>2026-04-13</td><td>2026-04-14</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260413-004</td><td>ST-EX-LY02-DG-HSYO-168K-20260413-002</td><td><span class="tag tag-gray">手动创建</span></td><td>粮油批发商行</td><td>花生油</td><td>粮油类</td><td>168</td><td>¥16</td><td>¥2,688</td><td>2026-04-13</td><td>2026-04-14</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260416-005</td><td>ST-EX-LY01-VG-XLHU-080K-20260416-001</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>西兰花</td><td>蔬菜类</td><td>80</td><td>¥4.5</td><td>¥360</td><td>2026-04-16</td><td>2026-04-17</td><td><span class="tag tag-orange">待处理</span></td><td><span class="tag tag-gray"></span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260416-005</td><td>ST-EX-LY01-VG-QCAI-120K-20260416-002</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>芹菜</td><td>蔬菜类</td><td>120</td><td>¥2.5</td><td>¥300</td><td>2026-04-16</td><td>2026-04-17</td><td><span class="tag tag-orange">待处理</span></td><td><span class="tag tag-gray"></span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-001</td><td>ST-EX-LY01-VG-BOCA-050K-20260415-002</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>菠菜</td><td>蔬菜类</td><td>50</td><td>¥2.5</td><td>¥125</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-001</td><td>ST-EX-LY01-VG-BCAI-080K-20260415-003</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>白菜</td><td>蔬菜类</td><td>80</td><td>¥1.8</td><td>¥144</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-001</td><td>ST-EX-LY01-VG-HLUB-120K-20260415-004</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>胡萝卜</td><td>蔬菜类</td><td>120</td><td>¥1.8</td><td>¥216</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-002</td><td>ST-EX-XR01-MT-ZLRJ-080K-20260415-005</td><td><span class="tag tag-blue">食谱搭配</span></td><td>鲜肉直供公司</td><td>猪里脊</td><td>肉禽类</td><td>80</td><td>¥28</td><td>¥2,240</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260415-002</td><td>ST-EX-XR01-MT-ZWUA-240K-20260415-006</td><td><span class="tag tag-blue">食谱搭配</span></td><td>鲜肉直供公司</td><td>猪五花</td><td>肉禽类</td><td>240</td><td>¥26</td><td>¥6,240</td><td>2026-04-15</td><td>2026-04-16</td><td><span class="tag tag-blue">进行中</span></td><td><span class="tag tag-orange">待收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260414-003</td><td>ST-EX-HS01-SF-CAYO-060K-20260414-001</td><td><span class="tag tag-gray">手动创建</span></td><td>海鲜水产配送</td><td>草鱼</td><td>水产类</td><td>60</td><td>¥18</td><td>¥1,080</td><td>2026-04-14</td><td>2026-04-15</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260414-003</td><td>ST-EX-HS01-SF-JIYU-080K-20260414-002</td><td><span class="tag tag-gray">手动创建</span></td><td>海鲜水产配送</td><td>鲫鱼</td><td>水产类</td><td>80</td><td>¥16</td><td>¥1,280</td><td>2026-04-14</td><td>2026-04-15</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260414-003</td><td>ST-EX-HS01-SF-XIAA-050K-20260414-003</td><td><span class="tag tag-gray">手动创建</span></td><td>海鲜水产配送</td><td></td><td>水产类</td><td>50</td><td>¥30</td><td>¥1,500</td><td>2026-04-14</td><td>2026-04-15</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260413-004</td><td>ST-EX-LY02-DG-DAMI-1000-20260413-001</td><td><span class="tag tag-gray">手动创建</span></td><td>粮油批发商行</td><td>大米</td><td>粮油类</td><td>1000</td><td>¥3.5</td><td>¥3,500</td><td>2026-04-13</td><td>2026-04-14</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260413-004</td><td>ST-EX-LY02-DG-HSYO-168K-20260413-002</td><td><span class="tag tag-gray">手动创建</span></td><td>粮油批发商行</td><td>花生油</td><td>粮油类</td><td>168</td><td>¥16</td><td>¥2,688</td><td>2026-04-13</td><td>2026-04-14</td><td><span class="tag tag-green">已完成</span></td><td><span class="tag tag-green">已收货</span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260416-005</td><td>ST-EX-LY01-VG-XLHU-080K-20260416-001</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>西兰花</td><td>蔬菜类</td><td>80</td><td>¥4.5</td><td>¥360</td><td>2026-04-16</td><td>2026-04-17</td><td><span class="tag tag-orange">待处理</span></td><td><span class="tag tag-gray"></span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
<tr><td>PO-20260416-005</td><td>ST-EX-LY01-VG-QCAI-120K-20260416-002</td><td><span class="tag tag-blue">食谱搭配</span></td><td>绿源蔬菜配送</td><td>芹菜</td><td>蔬菜类</td><td>120</td><td>¥2.5</td><td>¥300</td><td>2026-04-16</td><td>2026-04-17</td><td><span class="tag tag-orange">待处理</span></td><td><span class="tag tag-gray"></span></td><td class="td-actions"><a class="link" onclick="openVideoModal()">查看视频</a> <a class="link" onclick="showPurchaseDetail(this)">详情</a> <a class="link" onclick="openPurchaseModal('edit')">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录?')){}">删除</a></td></tr>
</tbody>
</table>
</div>
@@ -259,11 +295,94 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
</div>
<!-- 新建/编辑采购单弹窗 -->
<div class="modal-overlay" id="purchaseModal" style="display:none;">
<div class="modal" style="width:680px;">
<div class="modal-header">
<span class="modal-title" id="purchaseModalTitle">新建采购单</span>
<span class="modal-close" onclick="closePurchaseModal()">&times;</span>
</div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item"><label>供应商<span class="required">*</span></label><select><option value="">请选择</option><option>绿源蔬菜配送</option><option>鲜肉直供公司</option><option>海鲜水产配送</option><option>粮油批发商行</option></select></div>
<div class="form-item"><label>食材名称<span class="required">*</span></label><input type="text" placeholder="请输入食材名称"></div>
<div class="form-item"><label>食材分类<span class="required">*</span></label><select><option value="">请选择</option><option>蔬菜类</option><option>肉禽类</option><option>水产类</option><option>粮油类</option></select></div>
<div class="form-item"><label>采购数量(kg)<span class="required">*</span></label><input type="number" placeholder="请输入数量"></div>
<div class="form-item"><label>单价(元/kg)<span class="required">*</span></label><input type="number" placeholder="请输入单价"></div>
<div class="form-item"><label>预计到货日期<span class="required">*</span></label><input type="date"></div>
<div class="form-item"><label>来源</label><select><option>食谱搭配</option><option>手动创建</option></select></div>
<div class="form-item full"><label>备注</label><textarea placeholder="请输入备注信息"></textarea></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closePurchaseModal()">取消</button>
<button class="btn btn-primary" onclick="closePurchaseModal()">确定</button>
</div>
</div>
</div>
<!-- 采购详情弹窗 -->
<div class="modal-overlay" id="purchaseDetailModal" style="display:none;">
<div class="modal" style="width:680px;">
<div class="modal-header">
<span class="modal-title">采购详情</span>
<span class="modal-close" onclick="closePurchaseDetail()">&times;</span>
</div>
<div class="modal-body" style="padding:0;">
<div class="detail-grid" id="purchaseDetailGrid">
<div class="detail-item"><span class="detail-label">订单号</span><span class="detail-value" id="d-order"></span></div>
<div class="detail-item"><span class="detail-label">溯源码</span><span class="detail-value" id="d-trace"></span></div>
<div class="detail-item"><span class="detail-label">来源</span><span class="detail-value" id="d-source"></span></div>
<div class="detail-item"><span class="detail-label">供应商</span><span class="detail-value" id="d-supplier"></span></div>
<div class="detail-item"><span class="detail-label">食材名称</span><span class="detail-value" id="d-name"></span></div>
<div class="detail-item"><span class="detail-label">食材分类</span><span class="detail-value" id="d-category"></span></div>
<div class="detail-item"><span class="detail-label">采购数量</span><span class="detail-value" id="d-qty"></span></div>
<div class="detail-item"><span class="detail-label">单价</span><span class="detail-value" id="d-price"></span></div>
<div class="detail-item"><span class="detail-label">小计金额</span><span class="detail-value" id="d-total"></span></div>
<div class="detail-item"><span class="detail-label">下单日期</span><span class="detail-value" id="d-date"></span></div>
<div class="detail-item"><span class="detail-label">预计到货</span><span class="detail-value" id="d-arrival"></span></div>
<div class="detail-item"><span class="detail-label">订单状态</span><span class="detail-value" id="d-status"></span></div>
<div class="detail-item full"><span class="detail-label">收货状态</span><span class="detail-value" id="d-receive"></span></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closePurchaseDetail()">关闭</button>
</div>
</div>
</div>
<script>
// 视频弹窗控制
function openVideoModal(){document.getElementById('videoModal').style.display='flex';}
function closeVideoModal(){document.getElementById('videoModal').style.display='none';}
document.getElementById('videoModal').addEventListener('click',function(e){if(e.target===this)closeVideoModal();});
// 采购单弹窗
function openPurchaseModal(mode){
document.getElementById('purchaseModalTitle').textContent=mode==='edit'?'编辑采购单':'新建采购单';
document.getElementById('purchaseModal').style.display='flex';
}
function closePurchaseModal(){document.getElementById('purchaseModal').style.display='none';}
document.getElementById('purchaseModal').addEventListener('click',function(e){if(e.target===this)closePurchaseModal();});
// 采购详情弹窗
function showPurchaseDetail(el){
var tr=el.closest('tr');var cells=tr.querySelectorAll('td');
document.getElementById('d-order').textContent=cells[0].textContent;
document.getElementById('d-trace').textContent=cells[1].textContent;
document.getElementById('d-source').textContent=cells[2].textContent;
document.getElementById('d-supplier').textContent=cells[3].textContent;
document.getElementById('d-name').textContent=cells[4].textContent;
document.getElementById('d-category').textContent=cells[5].textContent;
document.getElementById('d-qty').textContent=cells[6].textContent+' kg';
document.getElementById('d-price').textContent=cells[7].textContent;
document.getElementById('d-total').textContent=cells[8].textContent;
document.getElementById('d-date').textContent=cells[9].textContent;
document.getElementById('d-arrival').textContent=cells[10].textContent;
document.getElementById('d-status').textContent=cells[11].textContent;
document.getElementById('d-receive').textContent=cells[12].textContent;
document.getElementById('purchaseDetailModal').style.display='flex';
}
function closePurchaseDetail(){document.getElementById('purchaseDetailModal').style.display='none';}
document.getElementById('purchaseDetailModal').addEventListener('click',function(e){if(e.target===this)closePurchaseDetail();});
// 侧边栏子组折叠
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 初始化:若当前激活项位于子组中,自动展开其所在子组
+145 -13
View File
@@ -99,6 +99,27 @@ tbody tr:hover{background:#F8FBFF;}
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;}
.td-actions .link{margin-right:8px;}
.td-actions .link:last-child{margin-right:0;}
.modal-overlay{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;}
.modal{background:#fff;border-radius:var(--radius-md);width:680px;max-height:85vh;display:flex;flex-direction:column;box-shadow:0 8px 24px rgba(0,0,0,0.15);}
.modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
.modal-title{font-size:var(--font-base);font-weight:600;}
.modal-close{font-size:22px;cursor:pointer;color:#999;line-height:1;}.modal-close:hover{color:#333;}
.modal-body{padding:24px;overflow-y:auto;flex:1;}
.modal-footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
.form-item{display:flex;flex-direction:column;gap:6px;}
.form-item.full{grid-column:1/-1;}
.form-item label{font-size:var(--font-sm);color:var(--text-secondary);}
.form-item .required{color:var(--danger);margin-left:2px;}
.form-item input,.form-item select,.form-item textarea{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;}
.form-item input:focus,.form-item select:focus,.form-item textarea:focus{border-color:var(--primary);}
.form-item textarea{height:80px;padding:8px 12px;resize:vertical;}
.form-item select{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;appearance:none;padding-right:28px;}
/* 评分星星 */
.star-rating{display:flex;gap:4px;font-size:24px;cursor:pointer;}
.star-rating .star{color:#ddd;transition:color .2s;}.star-rating .star.active{color:#FAAD14;}
</style>
</head>
<body>
@@ -210,7 +231,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary"> 新增内部供应商</button>
<button class="btn btn-primary" onclick="openInternalModal('add')"> 新增内部供应商</button>
<button class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
</div>
@@ -228,12 +249,12 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</tr>
</thead>
<tbody>
<tr><td>SUP-IN-001</td><td>A区蔬菜基地</td><td>蔬菜</td><td>李负责人</td><td>LAND-001/LAND-002</td><td>2,400</td><td><span class="tag tag-green">运营中</span></td><td>今日</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>SUP-IN-002</td><td>B区根茎基地</td><td>蔬菜</td><td>王负责人</td><td>LAND-002</td><td>1,200</td><td><span class="tag tag-green">运营中</span></td><td>今日</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>SUP-IN-003</td><td>C区菌菇大棚</td><td>蔬菜</td><td>张负责人</td><td>LAND-003</td><td>480</td><td><span class="tag tag-green">运营中</span></td><td>昨日</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>SUP-IN-004</td><td>D区豆类基地</td><td>蔬菜</td><td>李负责人</td><td>LAND-004</td><td>860</td><td><span class="tag tag-gray">休耕中</span></td><td>3天前</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>SUP-IN-005</td><td>A1-猪舍</td><td>肉禽</td><td>赵负责人</td><td>BREED-A1</td><td>3,200</td><td><span class="tag tag-green">运营中</span></td><td>本周</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>SUP-IN-006</td><td>B3-鸡舍</td><td>肉禽</td><td>陈负责人</td><td>BREED-B3</td><td>1,800</td><td><span class="tag tag-green">运营中</span></td><td>本周</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>SUP-IN-001</td><td>A区蔬菜基地</td><td>蔬菜</td><td>李负责人</td><td>LAND-001/LAND-002</td><td>2,400</td><td><span class="tag tag-green">运营中</span></td><td>今日</td><td class="td-actions"><a class="link" onclick="openInternalModal('edit','SUP-IN-001','A区蔬菜基地','蔬菜','李负责人')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>SUP-IN-002</td><td>B区根茎基地</td><td>蔬菜</td><td>王负责人</td><td>LAND-002</td><td>1,200</td><td><span class="tag tag-green">运营中</span></td><td>今日</td><td class="td-actions"><a class="link" onclick="openInternalModal('edit','SUP-IN-002','B区根茎基地','蔬菜','王负责人')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>SUP-IN-003</td><td>C区菌菇大棚</td><td>蔬菜</td><td>张负责人</td><td>LAND-003</td><td>480</td><td><span class="tag tag-green">运营中</span></td><td>昨日</td><td class="td-actions"><a class="link" onclick="openInternalModal('edit','SUP-IN-003','C区菌菇大棚','蔬菜','张负责人')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>SUP-IN-004</td><td>D区豆类基地</td><td>蔬菜</td><td>李负责人</td><td>LAND-004</td><td>860</td><td><span class="tag tag-gray">休耕中</span></td><td>3天前</td><td class="td-actions"><a class="link" onclick="openInternalModal('edit','SUP-IN-004','D区豆类基地','蔬菜','李负责人')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>SUP-IN-005</td><td>A1-猪舍</td><td>肉禽</td><td>赵负责人</td><td>BREED-A1</td><td>3,200</td><td><span class="tag tag-green">运营中</span></td><td>本周</td><td class="td-actions"><a class="link" onclick="openInternalModal('edit','SUP-IN-005','A1-猪舍','肉禽','赵负责人')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>SUP-IN-006</td><td>B3-鸡舍</td><td>肉禽</td><td>陈负责人</td><td>BREED-B3</td><td>1,800</td><td><span class="tag tag-green">运营中</span></td><td>本周</td><td class="td-actions"><a class="link" onclick="openInternalModal('edit','SUP-IN-006','B3-鸡舍','肉禽','陈负责人')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
</tbody>
</table>
</div>
@@ -271,8 +292,8 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary"> 新增外部供应商</button>
<button class="btn btn-default">⭐ 评价供应商</button>
<button class="btn btn-primary" onclick="openExternalModal('add')"> 新增外部供应商</button>
<button class="btn btn-default" onclick="openRateModal()">⭐ 评价供应商</button>
<button class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 10v2h10v-2M7 2v7M4.5 6.5L7 9l2.5-2.5" stroke="#fff" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>导出列表数据</button>
<button class="btn btn-default"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="2" y="2" width="10" height="10" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M4 5h6M4 7.5h4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>查看导出任务</button>
</div>
@@ -291,10 +312,10 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</tr>
</thead>
<tbody>
<tr><td>绿源蔬菜配送</td><td>蔬菜</td><td>138-0001-0001</td><td>本市郊区蔬菜基地</td><td><span class="tag tag-green">正常</span></td><td>4.6</td><td>3年</td><td>128单</td><td>今日</td><td>质量优/准时</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>鲜肉直供公司</td><td>肉类</td><td>139-0002-0002</td><td>本市农贸市场A区</td><td><span class="tag tag-green">正常</span></td><td>4.4</td><td>2年</td><td>96单</td><td>今日</td><td>质量良/准时</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>海鲜水产配送</td><td>水产</td><td>136-0003-0003</td><td>滨海水产批发市场</td><td><span class="tag tag-orange">即将到期</span></td><td>4.1</td><td>1年</td><td>48单</td><td>昨日</td><td>质量良/延迟</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>粮油批发商行</td><td>粮油</td><td>135-0004-0004</td><td>本市粮油批发中心</td><td><span class="tag tag-green">正常</span></td><td>4.5</td><td>4年</td><td>156单</td><td>3天前</td><td>质量优/准时</td><td class="td-actions"><a class="link">查看详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除该记录')){}">删除</a></td></tr>
<tr><td>绿源蔬菜配送</td><td>蔬菜</td><td>138-0001-0001</td><td>本市郊区蔬菜基地</td><td><span class="tag tag-green">正常</span></td><td>4.6</td><td>3年</td><td>128单</td><td>今日</td><td>质量优/准时</td><td class="td-actions"><a class="link" onclick="openExternalModal('edit','绿源蔬菜配送','蔬菜','138-0001-0001','本市郊区蔬菜基地')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>鲜肉直供公司</td><td>肉类</td><td>139-0002-0002</td><td>本市农贸市场A区</td><td><span class="tag tag-green">正常</span></td><td>4.4</td><td>2年</td><td>96单</td><td>今日</td><td>质量良/准时</td><td class="td-actions"><a class="link" onclick="openExternalModal('edit','鲜肉直供公司','肉类','139-0002-0002','本市农贸市场A区')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>海鲜水产配送</td><td>水产</td><td>136-0003-0003</td><td>滨海水产批发市场</td><td><span class="tag tag-orange">即将到期</span></td><td>4.1</td><td>1年</td><td>48单</td><td>昨日</td><td>质量良/延迟</td><td class="td-actions"><a class="link" onclick="openExternalModal('edit','海鲜水产配送','水产','136-0003-0003','滨海水产批发市场')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
<tr><td>粮油批发商行</td><td>粮油</td><td>135-0004-0004</td><td>本市粮油批发中心</td><td><span class="tag tag-green">正常</span></td><td>4.5</td><td>4年</td><td>156单</td><td>3天前</td><td>质量优/准时</td><td class="td-actions"><a class="link" onclick="openExternalModal('edit','粮油批发商行','粮油','135-0004-0004','本市粮油批发中心')">编辑</a><a class="link" style="color:var(--danger)" onclick="if(confirm('确认删除?')){}">删除</a></td></tr>
</tbody>
</table>
</div>
@@ -311,6 +332,62 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<!-- 弹窗:新增/编辑内部供应商 -->
<div class="modal-overlay" id="internalModal" onclick="if(event.target===this)closeInternalModal()">
<div class="modal">
<div class="modal-header"><span class="modal-title" id="internalModalTitle">新增内部供应商</span><span class="modal-close" onclick="closeInternalModal()">&times;</span></div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item"><label>基地/场所名称<span class="required">*</span></label><input id="int_name" placeholder="请输入基地或场所名称"></div>
<div class="form-item"><label>供应类别<span class="required">*</span></label><select id="int_category"><option value="">请选择</option><option>蔬菜</option><option>肉禽</option><option>水产</option><option>粮油</option></select></div>
<div class="form-item"><label>负责人<span class="required">*</span></label><input id="int_manager" placeholder="请输入负责人姓名"></div>
<div class="form-item"><label>关联地块/养殖区</label><input placeholder="如 LAND-001"></div>
<div class="form-item"><label>当前产能(kg/月)</label><input type="number" placeholder="请输入数字"></div>
<div class="form-item"><label>运营状态<span class="required">*</span></label><select><option>运营中</option><option>休耕中</option></select></div>
<div class="form-item"><label>联系电话</label><input placeholder="请输入联系电话"></div>
<div class="form-item full"><label>备注</label><textarea placeholder="请输入备注信息"></textarea></div>
</div>
</div>
<div class="modal-footer"><button class="btn btn-default" onclick="closeInternalModal()">取消</button><button class="btn btn-primary">确认保存</button></div>
</div>
</div>
<!-- 弹窗:新增/编辑外部供应商 -->
<div class="modal-overlay" id="externalModal" onclick="if(event.target===this)closeExternalModal()">
<div class="modal">
<div class="modal-header"><span class="modal-title" id="externalModalTitle">新增外部供应商</span><span class="modal-close" onclick="closeExternalModal()">&times;</span></div>
<div class="modal-body">
<div class="form-grid">
<div class="form-item"><label>供应商名称<span class="required">*</span></label><input id="ext_name" placeholder="请输入供应商名称"></div>
<div class="form-item"><label>供应类别<span class="required">*</span></label><select id="ext_category"><option value="">请选择</option><option>蔬菜</option><option>肉类</option><option>水产</option><option>粮油</option></select></div>
<div class="form-item"><label>联系电话<span class="required">*</span></label><input id="ext_phone" placeholder="请输入联系电话"></div>
<div class="form-item"><label>地址<span class="required">*</span></label><input id="ext_address" placeholder="请输入地址"></div>
<div class="form-item"><label>资质到期日期</label><input type="date"></div>
<div class="form-item"><label>营业执照编号</label><input placeholder="请输入营业执照编号"></div>
<div class="form-item"><label>合作开始日期</label><input type="date"></div>
<div class="form-item full"><label>备注</label><textarea placeholder="请输入备注信息"></textarea></div>
</div>
</div>
<div class="modal-footer"><button class="btn btn-default" onclick="closeExternalModal()">取消</button><button class="btn btn-primary">确认保存</button></div>
</div>
</div>
<!-- 弹窗:评价供应商 -->
<div class="modal-overlay" id="rateModal" onclick="if(event.target===this)closeRateModal()">
<div class="modal" style="width:520px;">
<div class="modal-header"><span class="modal-title">评价供应商</span><span class="modal-close" onclick="closeRateModal()">&times;</span></div>
<div class="modal-body">
<div class="form-grid" style="grid-template-columns:1fr;">
<div class="form-item"><label>选择供应商<span class="required">*</span></label><select><option value="">请选择供应商</option><option>绿源蔬菜配送</option><option>鲜肉直供公司</option><option>海鲜水产配送</option><option>粮油批发商行</option></select></div>
<div class="form-item"><label>质量评分<span class="required">*</span></label><div class="star-rating" data-field="quality"><span class="star" data-v="1"></span><span class="star" data-v="2"></span><span class="star" data-v="3"></span><span class="star" data-v="4"></span><span class="star" data-v="5"></span></div></div>
<div class="form-item"><label>时效评分<span class="required">*</span></label><div class="star-rating" data-field="timeliness"><span class="star" data-v="1"></span><span class="star" data-v="2"></span><span class="star" data-v="3"></span><span class="star" data-v="4"></span><span class="star" data-v="5"></span></div></div>
<div class="form-item"><label>评价内容</label><textarea placeholder="请输入评价内容"></textarea></div>
</div>
</div>
<div class="modal-footer"><button class="btn btn-default" onclick="closeRateModal()">取消</button><button class="btn btn-primary">提交评价</button></div>
</div>
</div>
<script>
// 页签切换
function switchTab(idx){
@@ -321,6 +398,61 @@ function switchTab(idx){
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
// 初始化:若当前激活项位于子组中,自动展开其所在子组
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
// 弹窗控制 - 内部供应商
function openInternalModal(mode, id, name, category, manager){
var m=document.getElementById('internalModal');
document.getElementById('internalModalTitle').textContent=mode==='edit'?'编辑内部供应商':'新增内部供应商';
if(mode==='edit'){
document.getElementById('int_name').value=name||'';
document.getElementById('int_category').value=category||'';
document.getElementById('int_manager').value=manager||'';
}else{
m.querySelectorAll('input,textarea').forEach(function(el){el.value='';});
m.querySelectorAll('select').forEach(function(el){el.selectedIndex=0;});
}
m.style.display='flex';
}
function closeInternalModal(){document.getElementById('internalModal').style.display='none';}
// 弹窗控制 - 外部供应商
function openExternalModal(mode, name, category, phone, address){
var m=document.getElementById('externalModal');
document.getElementById('externalModalTitle').textContent=mode==='edit'?'编辑外部供应商':'新增外部供应商';
if(mode==='edit'){
document.getElementById('ext_name').value=name||'';
document.getElementById('ext_category').value=category||'';
document.getElementById('ext_phone').value=phone||'';
document.getElementById('ext_address').value=address||'';
}else{
m.querySelectorAll('input,textarea').forEach(function(el){el.value='';});
m.querySelectorAll('select').forEach(function(el){el.selectedIndex=0;});
}
m.style.display='flex';
}
function closeExternalModal(){document.getElementById('externalModal').style.display='none';}
// 弹窗控制 - 评价供应商
function openRateModal(){
var m=document.getElementById('rateModal');
m.querySelectorAll('.star').forEach(function(s){s.classList.remove('active');});
m.querySelectorAll('select').forEach(function(el){el.selectedIndex=0;});
m.querySelectorAll('textarea').forEach(function(el){el.value='';});
m.style.display='flex';
}
function closeRateModal(){document.getElementById('rateModal').style.display='none';}
// 星星评分交互
document.querySelectorAll('.star-rating').forEach(function(rating){
rating.addEventListener('click',function(e){
var star=e.target.closest('.star');
if(!star)return;
var val=parseInt(star.dataset.v);
rating.querySelectorAll('.star').forEach(function(s){
s.classList.toggle('active',parseInt(s.dataset.v)<=val);
});
});
});
</script>
</body>
</html>
+100 -33
View File
@@ -112,6 +112,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<!-- 侧边栏 -->
<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 active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
@@ -163,26 +164,12 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="form-row">
<div class="form-group">
<label>检验单号</label>
<input class="form-input" type="text" value="NUT-N004" readonly style="background:#F5F7FA;color:var(--text-secondary);">
</div>
<div class="form-group">
<label>检验菜品<span class="required">*</span></label>
<select class="form-select" id="dishSelect">
<option value="">请选择菜品</option>
<option value="DISH001">红烧肉 (DISH001)</option>
<option value="DISH002">清炒时蔬 (DISH002)</option>
<option value="DISH003">番茄鸡蛋汤 (DISH003)</option>
<option value="DISH004">宫保鸡丁 (DISH004)</option>
<option value="DISH005">蒜蓉西兰花 (DISH005)</option>
<option value="DISH006">糖醋排骨 (DISH006)</option>
</select>
<input class="form-input" type="text" value="系统自动生成" readonly style="background:#F5F7FA;color:var(--text-secondary);">
</div>
<div class="form-group">
<label>检验时间<span class="required">*</span></label>
<input class="form-input" type="datetime-local" id="inspectTime">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>检验人员<span class="required">*</span></label>
<select class="form-select">
@@ -192,20 +179,43 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<option value="3">王质检员</option>
</select>
</div>
</div>
</div>
<!-- 检验菜品选择 -->
<div class="form-card">
<div class="form-card__title">检验菜品选择</div>
<div class="form-row" style="grid-template-columns:repeat(4,1fr);">
<div class="form-group">
<label>检验批次</label>
<input class="form-input" type="text" placeholder="如:第3批次">
<label>选择食堂<span class="required">*</span></label>
<select class="form-select" id="canteenSelect" onchange="onCanteenChange()">
<option value="">请选择食堂</option>
<option value="1">总部一号食堂</option>
<option value="2">总部二号食堂</option>
<option value="3">采油一厂职工食堂</option>
<option value="4">采油二厂综合食堂</option>
<option value="5">采气一厂营养餐厅</option>
</select>
</div>
<div class="form-group">
<label>样品来源</label>
<select class="form-select">
<option value="">请选择</option>
<option value="line1">一号餐线</option>
<option value="line2">二号餐线</option>
<option value="line3">三号餐线</option>
<label>选择餐线<span class="required">*</span></label>
<select class="form-select" id="lineSelect" onchange="onLineChange()" disabled>
<option value="">选择食堂</option>
</select>
</div>
<div class="form-group">
<label>选择日期<span class="required">*</span></label>
<input class="form-input" type="date" id="dateSelect" onchange="onDateChange()" disabled>
</div>
<div class="form-group">
<label>选择菜品<span class="required">*</span></label>
<select class="form-select" id="dishSelect" disabled>
<option value="">请先选择日期</option>
</select>
</div>
</div>
<div id="dishHint" style="font-size:var(--font-xs);color:var(--text-placeholder);margin-top:4px;">提示:依次选择食堂、餐线、日期后,将加载该餐线当日的菜品列表</div>
</div>
<!-- 检验数据 -->
@@ -231,10 +241,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="metric-item"><label>膳食纤维 (g)</label><input class="form-input" type="number" placeholder="0.0"></div>
<div class="metric-item"><label>钠 (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
</div>
<div class="form-group" style="max-width:50%;">
<label>微量元素备注</label>
<textarea class="form-textarea" placeholder="如:铁2.4mg、锌1.8mg..."></textarea>
</div>
</div>
<!-- 味道检验面板 -->
@@ -246,8 +253,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="metric-item"><label>辣度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>咸度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>气味评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="metric-item"><label>综合风味评分 (1-5)</label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="自动计算" readonly style="background:#F5F7FA;"></div>
<div class="metric-item"><label>综合评级</label><input class="form-input" type="text" placeholder="自动评定" readonly style="background:#F5F7FA;"></div>
<div class="metric-item"><label>综合风味评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="metric-item"><label>综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
</div>
</div>
@@ -259,8 +266,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="metric-item"><label>酥脆度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>嫩滑度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>弹性 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>综合口感评分 (1-5)</label><input class="form-input" type="number" placeholder="自动计算" readonly style="background:#F5F7FA;"></div>
<div class="metric-item"><label>综合评级</label><input class="form-input" type="text" placeholder="自动评定" readonly style="background:#F5F7FA;"></div>
<div class="metric-item"><label>综合口感评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="metric-item"><label>综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
</div>
</div>
@@ -272,8 +279,8 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="metric-item"><label>光泽度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>新鲜度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>卖相评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
<div class="metric-item"><label>综合色泽评分 (1-5)</label><input class="form-input" type="number" placeholder="自动计算" readonly style="background:#F5F7FA;"></div>
<div class="metric-item"><label>综合评级</label><input class="form-input" type="text" placeholder="自动评定" readonly style="background:#F5F7FA;"></div>
<div class="metric-item"><label>综合色泽评分 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" step="0.1" placeholder="0.0"></div>
<div class="metric-item"><label>综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
</div>
</div>
</div>
@@ -328,6 +335,66 @@ function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
var dt=now.getFullYear()+'-'+pad(now.getMonth()+1)+'-'+pad(now.getDate())+'T'+pad(now.getHours())+':'+pad(now.getMinutes());
document.getElementById('inspectTime').value=dt;
})();
/* 级联选择:食堂 → 餐线 → 日期 → 菜品 */
var lineData={
'1':[{id:'A',name:'A餐线(自选餐)'},{id:'B',name:'B餐线(营养点餐)'},{id:'C',name:'C餐线(清真)'}],
'2':[{id:'D',name:'A餐线(主食窗口)'},{id:'E',name:'B餐线(炒菜窗口)'}],
'3':[{id:'F',name:'A餐线(综合餐线)'}],
'4':[{id:'G',name:'A餐线(自选)'},{id:'H',name:'B餐线(面食)'}],
'5':[{id:'I',name:'A餐线(营养餐线)'}]
};
var dishData={
'A':['红烧肉','清炒时蔬','番茄鸡蛋汤','凉拌黄瓜','米饭'],
'B':['宫保鸡丁','蒜蓉西兰花','紫菜蛋花汤','糙米饭','水果拼盘'],
'C':['牛肉炖土豆','手抓饭','羊肉串','酸奶','馕饼'],
'D':['馒头','花卷','小米粥','油条','茶叶蛋'],
'E':['鱼香肉丝','麻婆豆腐','干锅花菜','酸辣汤'],
'F':['红烧排骨','炒三丁','冬瓜汤','蒸南瓜','米饭'],
'G':['糖醋排骨','清蒸鲈鱼','白灼生菜','紫菜汤'],
'H':['牛肉拉面','油泼面','炸酱面','酸汤水饺'],
'I':['营养套餐A','营养套餐B','营养套餐C']
};
function onCanteenChange(){
var cid=document.getElementById('canteenSelect').value;
var lineSel=document.getElementById('lineSelect');
var dateSel=document.getElementById('dateSelect');
var dishSel=document.getElementById('dishSelect');
lineSel.innerHTML='<option value="">请选择餐线</option>';
dateSel.value='';dateSel.disabled=true;
dishSel.innerHTML='<option value="">请先选择日期</option>';dishSel.disabled=true;
if(!cid){lineSel.disabled=true;return;}
lineSel.disabled=false;
(lineData[cid]||[]).forEach(function(l){
lineSel.innerHTML+='<option value="'+l.id+'">'+l.name+'</option>';
});
document.getElementById('dishHint').textContent='提示:请继续选择餐线和日期';
}
function onLineChange(){
var lid=document.getElementById('lineSelect').value;
var dateSel=document.getElementById('dateSelect');
var dishSel=document.getElementById('dishSelect');
dateSel.value='';dishSel.innerHTML='<option value="">请先选择日期</option>';dishSel.disabled=true;
if(!lid){dateSel.disabled=true;return;}
dateSel.disabled=false;
document.getElementById('dishHint').textContent='提示:请选择日期以加载当日菜品';
}
function onDateChange(){
var lid=document.getElementById('lineSelect').value;
var dateVal=document.getElementById('dateSelect').value;
var dishSel=document.getElementById('dishSelect');
if(!dateVal||!lid){dishSel.disabled=true;return;}
dishSel.disabled=false;
dishSel.innerHTML='<option value="">请选择菜品</option>';
var dishes=dishData[lid]||['菜品A','菜品B','菜品C'];
dishes.forEach(function(d,i){
dishSel.innerHTML+='<option value="DISH'+String(i+1).padStart(3,'0')+'">'+d+'</option>';
});
document.getElementById('dishHint').textContent='✅ 已加载 '+dishes.length+' 道菜品,请选择需要检验的菜品';
}
</script>
</body>
</html>
@@ -0,0 +1,622 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>营养管理 - 同步记录 | 健康长庆原型</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);}
.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);white-space:nowrap;}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-orange{background:#FFF7E6;color:var(--warning);}
.tag-red{background:#FFF1F0;color:var(--danger);}
.tag-blue{background:var(--primary-light);color:var(--primary);}
.link{color:var(--primary);cursor:pointer;font-size:var(--font-sm);text-decoration:none;}.link:hover{color:#40a9ff;}
/* 面包屑 */
.breadcrumb{display:flex;align-items:center;gap:8px;margin-bottom:20px;font-size:var(--font-sm);color:var(--text-secondary);}
.breadcrumb a{color:var(--primary);text-decoration:none;}
.breadcrumb a:hover{color:#40a9ff;}
.breadcrumb .sep{color:var(--text-placeholder);}
/* 操作栏 */
.action-bar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.action-bar__left{display:flex;gap:10px;}
.action-bar__right{font-size:var(--font-sm);color:var(--text-secondary);}
/* 表格区域 */
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.data-table-wrap{overflow-x:auto;}
.data-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);min-width:600px;}
.data-table th{background:#FAFAFA;padding:12px 14px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
.data-table td{padding:12px 14px;border-bottom:1px solid #F0F0F0;color:var(--text-primary);}
.data-table tr:last-child td{border-bottom:none;}
.data-table tr:hover td{background:#F8FBFF;}
.data-table .td-actions{white-space:nowrap;}
.data-table .td-actions .link{margin-right:10px;}
.data-table .td-actions .link:last-child{margin-right:0;}
/* 数字链接 */
.num-link{color:var(--primary);cursor:pointer;font-weight:600;font-size:var(--font-sm);text-decoration:underline;text-underline-offset:2px;}
.num-link:hover{color:#40a9ff;}
/* 分页器 */
.pagination{display:flex;align-items:center;justify-content:space-between;padding:14px 24px;border-top:1px solid #F0F0F0;}
.pagination__info{font-size:var(--font-xs);color:var(--text-placeholder);}
.pagination__btns{display:flex;gap:4px;align-items:center;}
.page-btn{min-width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;padding:0 8px;transition:all .2s;color:var(--text-primary);}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn.disabled{cursor:not-allowed;color:var(--text-placeholder);background:#F5F5F5;}
.page-btn.disabled:hover{background:#F5F5F5;color:var(--text-placeholder);border-color:var(--border);}
/* Dialog 遮罩 + 弹窗 */
.dialog-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:1000;}
.dialog-mask.show{display:flex;align-items:center;justify-content:center;}
.dialog{background:#fff;border-radius:var(--radius-lg);box-shadow:0 12px 48px rgba(0,0,0,0.2);width:960px;max-width:92vw;max-height:82vh;display:flex;flex-direction:column;overflow:hidden;}
.dialog__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);flex-shrink:0;}
.dialog__title{font-size:var(--font-base);font-weight:600;}
.dialog__close{font-size:22px;cursor:pointer;color:var(--text-secondary);line-height:1;}.dialog__close:hover{color:var(--text-primary);}
.dialog__body{display:flex;flex-direction:column;flex:1;overflow:hidden;min-height:0;}
/* 左侧检验列表 */
.dialog-left{width:280px;border-right:1px solid var(--border);overflow-y:auto;flex-shrink:0;background:#FAFAFA;}
.dialog-left__item{padding:12px 16px;cursor:pointer;border-bottom:1px solid #F0F0F0;transition:background .15s;font-size:var(--font-sm);}
.dialog-left__item:hover{background:#E6F7FF;}
.dialog-left__item.active{background:var(--primary-light);border-left:3px solid var(--primary);}
.dialog-left__no{font-weight:600;color:var(--text-primary);margin-bottom:3px;}
.dialog-left__info{font-size:var(--font-xs);color:var(--text-secondary);display:flex;gap:8px;}
/* 右侧检验数据 */
.dialog-right{flex:1;overflow-y:auto;padding:20px 24px;}
.dialog-right__title{font-size:var(--font-sm);font-weight:600;margin-bottom:12px;color:var(--text-primary);}
/* 右侧 Tab */
.inspect-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:16px;}
.inspect-tab{padding:8px 16px;font-size:var(--font-sm);cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .2s;}
.inspect-tab:hover{color:var(--primary);}
.inspect-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:500;}
.inspect-panel{display:none;}
.inspect-panel.active{display:block;}
/* 指标网格 */
.metric-row{display:grid;grid-template-columns:repeat(2,1fr);gap:8px 20px;margin-bottom:8px;}
.metric-pair{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;background:#FAFAFA;border-radius:var(--radius-sm);}
.metric-pair__label{font-size:var(--font-xs);color:var(--text-secondary);}
.metric-pair__value{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
/* 基本信息区 */
.detail-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px 20px;margin-bottom:16px;}
.detail-item{display:flex;flex-direction:column;gap:3px;}
.detail-item__label{font-size:var(--font-xs);color:var(--text-placeholder);}
.detail-item__value{font-size:var(--font-sm);color:var(--text-primary);}
.section-title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid var(--border);}
/* Dialog 顶部均值区 */
.dialog__avg{flex-shrink:0;border-bottom:1px solid var(--border);padding:16px 24px 12px;background:#FAFBFC;}
.dialog__avg-title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;display:flex;align-items:center;gap:6px;}
.dialog__avg-title .tag{margin-left:4px;}
.avg-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:12px;}
.avg-tab{padding:6px 14px;font-size:var(--font-xs);cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .2s;}
.avg-tab:hover{color:var(--primary);}
.avg-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:500;}
.avg-panel{display:none;}
.avg-panel.active{display:block;}
.avg-grid{display:flex;flex-wrap:nowrap;gap:8px;}
.avg-cell{display:flex;flex-direction:column;align-items:center;padding:8px 6px;background:#fff;border-radius:var(--radius-sm);border:1px solid #F0F0F0;flex:1;min-width:0;}
.avg-cell__label{font-size:10px;color:var(--text-placeholder);margin-bottom:2px;white-space:nowrap;}
.avg-cell__value{font-size:var(--font-sm);font-weight:600;color:var(--primary);}
</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 class="btn-back" href="../../index.html">← 返回首页</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 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>
<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>
<!-- 主内容区 -->
<div class="main">
<!-- 面包屑 -->
<div class="breadcrumb">
<a href="prod-qc-nutrition.html">营养检验</a>
<span class="sep">/</span>
<span>同步记录</span>
</div>
<!-- 操作栏 -->
<div class="action-bar">
<div class="action-bar__right"><strong>4</strong> 条记录</div>
</div>
<!-- 数据表格 -->
<div class="table-card">
<div class="data-table-wrap">
<table class="data-table">
<thead>
<tr>
<th>餐品</th>
<th>同步时间</th>
<th>操作人</th>
<th>已选检验记录</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
<div class="pagination">
<div class="pagination__info">第 1-4 条 / 共 4 条</div>
<div class="pagination__btns">
<button class="page-btn disabled">上一页</button>
<button class="page-btn active">1</button>
<button class="page-btn disabled">下一页</button>
</div>
</div>
</div>
</div>
<!-- Dialog 弹窗 -->
<div class="dialog-mask" id="dialogMask" onclick="if(event.target===this)closeDialog()">
<div class="dialog">
<div class="dialog__header">
<span class="dialog__title" id="dialogTitle">检验记录详情</span>
<span class="dialog__close" onclick="closeDialog()">&times;</span>
</div>
<div class="dialog__body">
<div class="dialog__avg" id="dialogAvg"></div>
<div style="display:flex;flex:1;overflow:hidden;min-height:0;">
<div class="dialog-left" id="dialogLeft"></div>
<div class="dialog-right" id="dialogRight"></div>
</div>
</div>
</div>
</div>
<!-- 交互脚本 -->
<script>
/* ===== 检验原始记录(与主页面共享结构) ===== */
var RECORDS = [
{no:'QC-2026042201',time:'2026-04-22 14:30',dish:'红烧肉',category:'总部一号食堂',line:'A餐线',date:'2026-04-22',inspector:'张明',batch:'第1批次',
nutri:'达标',taste:'优秀',texture:'良好',color:'良好',
nutriData:{cal:235,protein:18.5,carb:8.2,fat:15.3,vc:1.2,mineral:45,fiber:0.8,sodium:680},
tasteData:{sour:1,sweet:2,bitter:1,spicy:2,salty:3,smell:4.2,flavor:4.5,grade:'优秀'},
textureData:{hardness:3,sticky:2,crispy:1,smooth:3,elastic:3,score:3.8,grade:'良好'},
colorData:{mainColor:'深红褐色',uniformity:4,gloss:4,freshness:4,appearance:4,score:4.0,grade:'良好'}},
{no:'QC-2026042204',time:'2026-04-22 15:00',dish:'红烧肉',category:'总部一号食堂',line:'B餐线',date:'2026-04-22',inspector:'王芳',batch:'第1批次',
nutri:'达标',taste:'优秀',texture:'优秀',color:'优秀',
nutriData:{cal:228,protein:19.1,carb:7.8,fat:14.8,vc:1.0,mineral:43,fiber:0.7,sodium:700},
tasteData:{sour:1,sweet:2,bitter:1,spicy:2,salty:3,smell:4.5,flavor:4.6,grade:'优秀'},
textureData:{hardness:3,sticky:1,crispy:1,smooth:4,elastic:4,score:4.2,grade:'优秀'},
colorData:{mainColor:'酱红色',uniformity:5,gloss:5,freshness:5,appearance:5,score:4.7,grade:'优秀'}},
{no:'QC-2026042202',time:'2026-04-22 14:35',dish:'清炒时蔬',category:'总部一号食堂',line:'A餐线',date:'2026-04-22',inspector:'李华',batch:'第1批次',
nutri:'偏差',taste:'良好',texture:'良好',color:'优秀',
nutriData:{cal:65,protein:3.2,carb:5.8,fat:3.1,vc:28,mineral:38,fiber:2.5,sodium:320},
tasteData:{sour:1,sweet:1,bitter:1,spicy:1,salty:2,smell:3.5,flavor:3.8,grade:'良好'},
textureData:{hardness:2,sticky:1,crispy:3,smooth:3,elastic:2,score:3.6,grade:'良好'},
colorData:{mainColor:'翠绿色',uniformity:5,gloss:5,freshness:5,appearance:5,score:4.8,grade:'优秀'}},
{no:'QC-2026042203',time:'2026-04-22 14:40',dish:'清炒时蔬',category:'总部二号食堂',line:'A餐线',date:'2026-04-22',inspector:'张明',batch:'第2批次',
nutri:'达标',taste:'良好',texture:'良好',color:'良好',
nutriData:{cal:62,protein:3.0,carb:6.1,fat:2.8,vc:25,mineral:36,fiber:2.3,sodium:340},
tasteData:{sour:1,sweet:1,bitter:1,spicy:1,salty:2,smell:3.6,flavor:3.7,grade:'良好'},
textureData:{hardness:2,sticky:1,crispy:3,smooth:3,elastic:2,score:3.5,grade:'良好'},
colorData:{mainColor:'翠绿色',uniformity:4,gloss:4,freshness:4,appearance:4,score:3.8,grade:'良好'}},
{no:'QC-2026042105',time:'2026-04-21 11:20',dish:'宫保鸡丁',category:'采油一厂职工食堂',line:'A餐线',date:'2026-04-21',inspector:'李华',batch:'第1批次',
nutri:'达标',taste:'优秀',texture:'良好',color:'良好',
nutriData:{cal:198,protein:22.3,carb:12.1,fat:8.6,vc:5.8,mineral:52,fiber:1.2,sodium:720},
tasteData:{sour:2,sweet:2,bitter:1,spicy:3,salty:3,smell:4.3,flavor:4.4,grade:'优秀'},
textureData:{hardness:2,sticky:1,crispy:2,smooth:4,elastic:3,score:3.9,grade:'良好'},
colorData:{mainColor:'金红色',uniformity:4,gloss:4,freshness:4,appearance:4,score:3.9,grade:'良好'}},
{no:'QC-2026042106',time:'2026-04-21 11:35',dish:'宫保鸡丁',category:'采油一厂职工食堂',line:'A餐线',date:'2026-04-21',inspector:'张明',batch:'第2批次',
nutri:'达标',taste:'良好',texture:'良好',color:'良好',
nutriData:{cal:192,protein:21.8,carb:11.5,fat:8.9,vc:5.2,mineral:50,fiber:1.1,sodium:710},
tasteData:{sour:2,sweet:2,bitter:1,spicy:3,salty:3,smell:4.0,flavor:4.1,grade:'良好'},
textureData:{hardness:2,sticky:1,crispy:2,smooth:3,elastic:3,score:3.7,grade:'良好'},
colorData:{mainColor:'金红色',uniformity:4,gloss:3,freshness:4,appearance:4,score:3.7,grade:'良好'}},
{no:'QC-2026042007',time:'2026-04-20 14:10',dish:'糖醋里脊',category:'采油二厂综合食堂',line:'A餐线',date:'2026-04-20',inspector:'王芳',batch:'第1批次',
nutri:'偏差',taste:'良好',texture:'良好',color:'优秀',
nutriData:{cal:265,protein:15.2,carb:22.8,fat:13.5,vc:1.8,mineral:30,fiber:0.4,sodium:780},
tasteData:{sour:3,sweet:4,bitter:1,spicy:1,salty:2,smell:3.9,flavor:3.8,grade:'良好'},
textureData:{hardness:3,sticky:1,crispy:4,smooth:2,elastic:3,score:3.6,grade:'良好'},
colorData:{mainColor:'金黄色',uniformity:5,gloss:5,freshness:5,appearance:5,score:4.6,grade:'优秀'}},
{no:'QC-2026042008',time:'2026-04-20 14:25',dish:'糖醋里脊',category:'采油二厂综合食堂',line:'A餐线',date:'2026-04-20',inspector:'李华',batch:'第2批次',
nutri:'达标',taste:'良好',texture:'优秀',color:'优秀',
nutriData:{cal:258,protein:15.8,carb:21.5,fat:12.8,vc:2.0,mineral:32,fiber:0.5,sodium:760},
tasteData:{sour:3,sweet:4,bitter:1,spicy:1,salty:2,smell:3.7,flavor:3.6,grade:'良好'},
textureData:{hardness:3,sticky:1,crispy:4,smooth:2,elastic:3,score:4.1,grade:'优秀'},
colorData:{mainColor:'金黄色',uniformity:5,gloss:4,freshness:5,appearance:5,score:4.5,grade:'优秀'}}
];
/* ===== 同步记录数据(引用 RECORDS 索引) ===== */
var SYNC_DATA = [
{dish:'红烧肉', syncTime:'2026-04-22 16:00',operator:'张明', recordIdxs:[0,1]},
{dish:'清炒时蔬',syncTime:'2026-04-22 16:05',operator:'李华', recordIdxs:[2,3]},
{dish:'宫保鸡丁',syncTime:'2026-04-21 14:00',operator:'王芳', recordIdxs:[4,5]},
{dish:'糖醋里脊',syncTime:'2026-04-20 16:30',operator:'张明', recordIdxs:[6,7]}
];
/* ===== Tag 工具函数 ===== */
function nutriTag(val){
if(val==='达标') return '<span class="tag tag-green">达标</span>';
if(val==='偏差') return '<span class="tag tag-orange">偏差</span>';
return '<span class="tag tag-red">未达标</span>';
}
function gradeTag(val){
if(val==='优秀') return '<span class="tag tag-green">优秀</span>';
if(val==='良好') return '<span class="tag tag-blue">良好</span>';
if(val==='一般') return '<span class="tag tag-orange">一般</span>';
return '<span class="tag tag-red">较差</span>';
}
/* ===== 渲染同步记录表格 ===== */
function renderTable(){
var html = SYNC_DATA.map(function(s,si){
return '<tr>'
+'<td>'+s.dish+'</td>'
+'<td>'+s.syncTime+'</td>'
+'<td>'+s.operator+'</td>'
+'<td><span class="num-link" onclick="openDialog('+si+')">'+s.recordIdxs.length+'</span></td>'
+'<td class="td-actions"><span class="link" onclick="openDialog('+si+')">详情</span></td>'
+'</tr>';
}).join('');
document.getElementById('tableBody').innerHTML = html;
}
/* ===== Dialog 弹窗 ===== */
var currentSyncIdx = 0;
var currentRecordIdx = 0;
function openDialog(syncIdx){
currentSyncIdx = syncIdx;
currentRecordIdx = 0;
renderDialogAvg();
renderDialogLeft();
renderDialogRight();
document.getElementById('dialogTitle').textContent = '检验记录详情 - ' + SYNC_DATA[syncIdx].dish;
document.getElementById('dialogMask').classList.add('show');
}
function closeDialog(){
document.getElementById('dialogMask').classList.remove('show');
}
/* ===== 均值计算与渲染 ===== */
function calcAvg(records, field){
var sum = 0;
records.forEach(function(r){ sum += r[field]; });
return (sum / records.length).toFixed(1);
}
/* 根据均值分数判定感官等级 */
function scoreToGrade(avg){
var v = parseFloat(avg);
if(v >= 4.0) return '优秀';
if(v >= 3.0) return '良好';
if(v >= 2.0) return '一般';
return '较差';
}
/* 根据多条营养结果取众数判定等级 */
function nutriMajorityGrade(recs){
var cnt = {达标:0,偏差:0,未达标:0};
recs.forEach(function(r){ cnt[r.nutri] = (cnt[r.nutri]||0) + 1; });
if(cnt['未达标'] > 0 && cnt['未达标'] >= cnt['达标']) return '未达标';
if(cnt['偏差'] > cnt['达标']) return '偏差';
return '达标';
}
function renderDialogAvg(){
var s = SYNC_DATA[currentSyncIdx];
var recs = s.recordIdxs.map(function(i){ return RECORDS[i]; });
var count = recs.length;
/* 营养均值 */
var nds = recs.map(function(r){ return r.nutriData; });
var avgNutri = {
cal: calcAvg(nds,'cal'), protein: calcAvg(nds,'protein'), carb: calcAvg(nds,'carb'), fat: calcAvg(nds,'fat'),
vc: calcAvg(nds,'vc'), mineral: calcAvg(nds,'mineral'), fiber: calcAvg(nds,'fiber'), sodium: calcAvg(nds,'sodium')
};
/* 味道均值 */
var tds = recs.map(function(r){ return r.tasteData; });
var avgTaste = {
sour: calcAvg(tds,'sour'), sweet: calcAvg(tds,'sweet'), bitter: calcAvg(tds,'bitter'), spicy: calcAvg(tds,'spicy'),
salty: calcAvg(tds,'salty'), smell: calcAvg(tds,'smell'), flavor: calcAvg(tds,'flavor')
};
/* 口感均值 */
var xds = recs.map(function(r){ return r.textureData; });
var avgTexture = {
hardness: calcAvg(xds,'hardness'), sticky: calcAvg(xds,'sticky'), crispy: calcAvg(xds,'crispy'),
smooth: calcAvg(xds,'smooth'), elastic: calcAvg(xds,'elastic'), score: calcAvg(xds,'score')
};
/* 色泽均值 */
var cds = recs.map(function(r){ return r.colorData; });
var avgColor = {
uniformity: calcAvg(cds,'uniformity'), gloss: calcAvg(cds,'gloss'), freshness: calcAvg(cds,'freshness'),
appearance: calcAvg(cds,'appearance'), score: calcAvg(cds,'score')
};
var html = '<div class="dialog__avg-title">检验均值汇总 <span class="tag tag-blue">共 '+count+' 条记录</span></div>';
/* Tab */
html += '<div class="avg-tabs">'
+'<div class="avg-tab active" onclick="switchAvgTab(0)">营养</div>'
+'<div class="avg-tab" onclick="switchAvgTab(1)">味道</div>'
+'<div class="avg-tab" onclick="switchAvgTab(2)">口感</div>'
+'<div class="avg-tab" onclick="switchAvgTab(3)">色泽</div>'
+'</div>';
/* 营养面板 */
html += '<div class="avg-panel active" id="avgP0"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">热量(kcal)</div><div class="avg-cell__value">'+avgNutri.cal+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">蛋白质(g)</div><div class="avg-cell__value">'+avgNutri.protein+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">碳水(g)</div><div class="avg-cell__value">'+avgNutri.carb+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">脂肪(g)</div><div class="avg-cell__value">'+avgNutri.fat+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">维C(mg)</div><div class="avg-cell__value">'+avgNutri.vc+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">矿物质(mg)</div><div class="avg-cell__value">'+avgNutri.mineral+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">膳食纤维(g)</div><div class="avg-cell__value">'+avgNutri.fiber+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">钠(mg)</div><div class="avg-cell__value">'+avgNutri.sodium+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">检验等级</div><div class="avg-cell__value">'+nutriTag(nutriMajorityGrade(recs))+'</div></div>'
+'</div></div>';
/* 味道面板 */
html += '<div class="avg-panel" id="avgP1"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">酸度</div><div class="avg-cell__value">'+avgTaste.sour+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">甜度</div><div class="avg-cell__value">'+avgTaste.sweet+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">苦度</div><div class="avg-cell__value">'+avgTaste.bitter+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">辣度</div><div class="avg-cell__value">'+avgTaste.spicy+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">咸度</div><div class="avg-cell__value">'+avgTaste.salty+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">气味评分</div><div class="avg-cell__value">'+avgTaste.smell+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合风味</div><div class="avg-cell__value">'+avgTaste.flavor+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合评级</div><div class="avg-cell__value">'+gradeTag(scoreToGrade(avgTaste.flavor))+'</div></div>'
+'</div></div>';
/* 口感面板 */
html += '<div class="avg-panel" id="avgP2"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">软硬度</div><div class="avg-cell__value">'+avgTexture.hardness+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">黏糯度</div><div class="avg-cell__value">'+avgTexture.sticky+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">酥脆度</div><div class="avg-cell__value">'+avgTexture.crispy+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">嫩滑度</div><div class="avg-cell__value">'+avgTexture.smooth+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">弹性</div><div class="avg-cell__value">'+avgTexture.elastic+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合口感</div><div class="avg-cell__value">'+avgTexture.score+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合评级</div><div class="avg-cell__value">'+gradeTag(scoreToGrade(avgTexture.score))+'</div></div>'
+'</div></div>';
/* 色泽面板 */
html += '<div class="avg-panel" id="avgP3"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">均匀度</div><div class="avg-cell__value">'+avgColor.uniformity+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">光泽度</div><div class="avg-cell__value">'+avgColor.gloss+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">新鲜度</div><div class="avg-cell__value">'+avgColor.freshness+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">卖相评分</div><div class="avg-cell__value">'+avgColor.appearance+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合色泽</div><div class="avg-cell__value">'+avgColor.score+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合评级</div><div class="avg-cell__value">'+gradeTag(scoreToGrade(avgColor.score))+'</div></div>'
+'</div></div>';
document.getElementById('dialogAvg').innerHTML = html;
}
function switchAvgTab(idx){
var tabs = document.querySelectorAll('#dialogAvg .avg-tab');
var panels = document.querySelectorAll('#dialogAvg .avg-panel');
tabs.forEach(function(t,i){ t.classList.toggle('active', i===idx); });
panels.forEach(function(p,i){ p.classList.toggle('active', i===idx); });
}
function renderDialogLeft(){
var s = SYNC_DATA[currentSyncIdx];
var html = '';
s.recordIdxs.forEach(function(ri, i){
var r = RECORDS[ri];
var cls = (i === currentRecordIdx) ? 'dialog-left__item active' : 'dialog-left__item';
html += '<div class="'+cls+'" onclick="selectRecord('+i+')">'
+'<div class="dialog-left__no">'+r.no+'</div>'
+'<div class="dialog-left__info">'
+'<span>'+r.time+'</span>'
+'<span>'+r.inspector+'</span>'
+'</div>'
+'<div class="dialog-left__info" style="margin-top:2px;">'
+'<span>'+r.category+'</span>'
+'<span>'+r.line+'</span>'
+'</div>'
+'</div>';
});
document.getElementById('dialogLeft').innerHTML = html;
}
function selectRecord(idx){
currentRecordIdx = idx;
renderDialogLeft();
renderDialogRight();
}
function renderDialogRight(){
var s = SYNC_DATA[currentSyncIdx];
var ri = s.recordIdxs[currentRecordIdx];
var r = RECORDS[ri];
var n = r.nutriData, t = r.tasteData, x = r.textureData, c = r.colorData;
var html = '';
/* 基本信息 */
html += '<div class="section-title">基本信息</div>'
+'<div class="detail-grid">'
+'<div class="detail-item"><div class="detail-item__label">检验单号</div><div class="detail-item__value">'+r.no+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">检验时间</div><div class="detail-item__value">'+r.time+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">检验人员</div><div class="detail-item__value">'+r.inspector+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">检验批次</div><div class="detail-item__value">'+r.batch+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">食堂</div><div class="detail-item__value">'+r.category+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">餐线</div><div class="detail-item__value">'+r.line+'</div></div>'
+'</div>';
/* 检验数据 Tab */
html += '<div class="section-title" style="margin-top:20px;">检验数据</div>'
+'<div class="inspect-tabs">'
+'<div class="inspect-tab active" onclick="switchDialogTab(0)">营养</div>'
+'<div class="inspect-tab" onclick="switchDialogTab(1)">味道</div>'
+'<div class="inspect-tab" onclick="switchDialogTab(2)">口感</div>'
+'<div class="inspect-tab" onclick="switchDialogTab(3)">色泽</div>'
+'</div>';
/* 营养面板 */
html += '<div class="inspect-panel active" id="dlgP0"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">热量 (kcal/100g)</span><span class="metric-pair__value">'+n.cal+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">蛋白质 (g)</span><span class="metric-pair__value">'+n.protein+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">碳水化合物 (g)</span><span class="metric-pair__value">'+n.carb+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">脂肪 (g)</span><span class="metric-pair__value">'+n.fat+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">维生素C (mg)</span><span class="metric-pair__value">'+n.vc+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">矿物质 (mg)</span><span class="metric-pair__value">'+n.mineral+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">膳食纤维 (g)</span><span class="metric-pair__value">'+n.fiber+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">钠 (mg)</span><span class="metric-pair__value">'+n.sodium+'</span></div>'
+'</div><div style="margin-top:8px;">检验结果:'+nutriTag(r.nutri)+'</div></div>';
/* 味道面板 */
html += '<div class="inspect-panel" id="dlgP1"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">酸度</span><span class="metric-pair__value">'+t.sour+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">甜度</span><span class="metric-pair__value">'+t.sweet+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">苦度</span><span class="metric-pair__value">'+t.bitter+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">辣度</span><span class="metric-pair__value">'+t.spicy+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">咸度</span><span class="metric-pair__value">'+t.salty+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">气味评分</span><span class="metric-pair__value">'+t.smell+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合风味</span><span class="metric-pair__value">'+t.flavor+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合评级</span><span class="metric-pair__value">'+gradeTag(t.grade)+'</span></div>'
+'</div></div>';
/* 口感面板 */
html += '<div class="inspect-panel" id="dlgP2"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">软硬度</span><span class="metric-pair__value">'+x.hardness+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">黏糯度</span><span class="metric-pair__value">'+x.sticky+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">酥脆度</span><span class="metric-pair__value">'+x.crispy+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">嫩滑度</span><span class="metric-pair__value">'+x.smooth+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">弹性</span><span class="metric-pair__value">'+x.elastic+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合口感</span><span class="metric-pair__value">'+x.score+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合评级</span><span class="metric-pair__value">'+gradeTag(x.grade)+'</span></div>'
+'</div></div>';
/* 色泽面板 */
html += '<div class="inspect-panel" id="dlgP3"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">主色调</span><span class="metric-pair__value">'+c.mainColor+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">色泽均匀度</span><span class="metric-pair__value">'+c.uniformity+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">光泽度</span><span class="metric-pair__value">'+c.gloss+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">新鲜度</span><span class="metric-pair__value">'+c.freshness+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">卖相评分</span><span class="metric-pair__value">'+c.appearance+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合色泽</span><span class="metric-pair__value">'+c.score+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合评级</span><span class="metric-pair__value">'+gradeTag(c.grade)+'</span></div>'
+'</div></div>';
document.getElementById('dialogRight').innerHTML = html;
}
function switchDialogTab(idx){
var tabs = document.querySelectorAll('#dialogRight .inspect-tab');
var panels = document.querySelectorAll('#dialogRight .inspect-panel');
tabs.forEach(function(t,i){ t.classList.toggle('active', i===idx); });
panels.forEach(function(p,i){ p.classList.toggle('active', i===idx); });
}
/* ===== 侧边栏折叠 ===== */
function toggleSubgroup(header){ header.parentElement.classList.toggle('open'); }
(function(){
var a = document.querySelector('.sidebar-item.active');
if(!a) return;
var s = a.closest('.sidebar-subgroup');
if(s) s.classList.add('open');
})();
/* ===== 初始化 ===== */
renderTable();
</script>
</body>
</html>
+562 -330
View File
@@ -43,86 +43,124 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
.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);}
.btn-danger-link{background:none;border:none;color:var(--danger);cursor:pointer;font-size:var(--font-sm);padding:0;}
.btn-danger-link:hover{color:#ff7875;}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);white-space:nowrap;}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-orange{background:#FFF7E6;color:var(--warning);}
.tag-red{background:#FFF1F0;color:var(--danger);}
.tag-blue{background:var(--primary-light);color:var(--primary);}
.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);}
.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;}
/* 筛选区域 */
.filter-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:16px 24px;margin-bottom:16px;}
.filter-row{display:flex;flex-wrap:wrap;gap:10px 20px;align-items:center;}
.filter-item{display:flex;align-items:center;gap:6px;white-space:nowrap;}
.filter-item label{font-size:var(--font-sm);color:var(--text-secondary);flex-shrink:0;}
.filter-item input,.filter-item select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);outline:none;background:#fff;min-width:130px;}
.filter-item input:focus,.filter-item select:focus{border-color:var(--primary);}
.filter-item select{padding-right:28px;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;cursor:pointer;}
.filter-item .date-range{display:flex;align-items:center;gap:6px;}
.filter-item .date-range input{min-width:120px;}
.filter-item .date-range span{color:var(--text-placeholder);font-size:var(--font-xs);}
.filter-actions{display:flex;align-items:center;gap:8px;margin-left:auto;}
.filter-actions .btn{height:32px;padding:0 14px;font-size:var(--font-sm);}
/* 操作栏 */
.action-bar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.action-bar__left{display:flex;gap:10px;}
.action-bar__right{font-size:var(--font-sm);color:var(--text-secondary);}
/* 表格区域 */
.table-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;}
.data-table-wrap{overflow-x:auto;}
.data-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);min-width:1200px;}
.data-table th{background:#FAFAFA;padding:12px 14px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
.data-table td{padding:12px 14px;border-bottom:1px solid #F0F0F0;color:var(--text-primary);}
.data-table tr:last-child td{border-bottom:none;}
.data-table tr:hover td{background:#F8FBFF;}
.data-table .td-actions{white-space:nowrap;}
.data-table .td-actions .link{margin-right:10px;}
.data-table .td-actions .link:last-child{margin-right:0;}
/* 分页器 */
.pagination{display:flex;align-items:center;justify-content:space-between;padding:14px 24px;border-top:1px solid #F0F0F0;}
.pagination__info{font-size:var(--font-xs);color:var(--text-placeholder);}
.pagination__btns{display:flex;gap:4px;align-items:center;}
.page-btn{min-width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;font-size:var(--font-sm);display:inline-flex;align-items:center;justify-content:center;padding:0 8px;transition:all .2s;color:var(--text-primary);}
.page-btn:hover,.page-btn.active{background:var(--primary);color:#fff;border-color:var(--primary);}
.page-btn.disabled{cursor:not-allowed;color:var(--text-placeholder);background:#F5F5F5;}
.page-btn.disabled:hover{background:#F5F5F5;color:var(--text-placeholder);border-color:var(--border);}
/* 右侧抽屉 */
.drawer-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:1000;}
.drawer-mask.show{display:block;}
.drawer{position:fixed;top:0;right:-720px;bottom:0;width:720px;background:#fff;z-index:1001;transition:right .3s ease;box-shadow:-4px 0 20px rgba(0,0,0,0.15);display:flex;flex-direction:column;}
.drawer.show{right:0;}
.drawer__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);flex-shrink:0;}
.drawer__title{font-size:var(--font-base);font-weight:600;}
.drawer__close{font-size:22px;cursor:pointer;color:var(--text-secondary);line-height:1;}.drawer__close:hover{color:var(--text-primary);}
.drawer__body{padding:24px;overflow-y:auto;flex:1;}
.detail-section{margin-bottom:24px;}
.detail-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid var(--border);}
.detail-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px 20px;}
.detail-grid--3{grid-template-columns:repeat(3,1fr);}
.detail-item{display:flex;flex-direction:column;gap:3px;}
.detail-item__label{font-size:var(--font-xs);color:var(--text-placeholder);}
.detail-item__value{font-size:var(--font-sm);color:var(--text-primary);}
/* 抽屉内 Tab */
.drawer-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:12px;}
.drawer-tab{padding:8px 16px;font-size:var(--font-sm);cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .2s;}
.drawer-tab:hover{color:var(--primary);}
.drawer-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:500;}
.drawer-panel{display:none;}
.drawer-panel.active{display:block;}
.metric-row{display:grid;grid-template-columns:repeat(2,1fr);gap:8px 20px;margin-bottom:8px;}
.metric-pair{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;background:#FAFAFA;border-radius:var(--radius-sm);}
.metric-pair__label{font-size:var(--font-xs);color:var(--text-secondary);}
.metric-pair__value{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
.attachment-list{display:flex;gap:12px;flex-wrap:wrap;}
.attachment-thumb{width:80px;height:80px;border-radius:var(--radius-sm);background:#F5F7FA;display:flex;align-items:center;justify-content:center;font-size:28px;border:1px solid var(--border);}
/* 右侧详情面板 */
.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);}
.panel-right__food-title .tag{margin-left:4px;}
.detail-tabs{display:flex;gap:0;}
.detail-tab{padding:10px 20px;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;}
/* 同步模式 */
.btn-success{background:var(--success);color:#fff;border:none;}.btn-success:hover{background:#73d13d;}
.cb-cell{width:36px;text-align:center;}
.cb-cell input[type="checkbox"]{width:16px;height:16px;cursor:pointer;accent-color:var(--primary);}
.col-cb{display:none;}
.sync-mode .col-cb{display:table-cell;}
.action-bar__left--sync{display:none;gap:10px;}
.sync-mode .action-bar__left--normal{display:none;}
.sync-mode .action-bar__left--sync{display:flex;}
/* 检验数据网格 */
.inspect-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px;}
.inspect-cell{background:#FAFBFC;border-radius:var(--radius-sm);padding:14px 16px;border:1px solid #F0F0F0;}
.inspect-cell__label{font-size:var(--font-xs);color:var(--text-placeholder);margin-bottom:4px;}
.inspect-cell__value{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);}
.inspect-cell__unit{font-size:var(--font-xs);color:var(--text-secondary);margin-left:2px;font-weight:400;}
/* 同步确认 Dialog */
.sync-dialog-mask{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:1000;}
.sync-dialog-mask.show{display:flex;align-items:center;justify-content:center;}
.sync-dialog{background:#fff;border-radius:var(--radius-lg);box-shadow:0 12px 48px rgba(0,0,0,0.2);width:640px;max-width:90vw;max-height:80vh;display:flex;flex-direction:column;overflow:hidden;}
.sync-dialog__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);flex-shrink:0;}
.sync-dialog__title{font-size:var(--font-base);font-weight:600;}
.sync-dialog__close{font-size:22px;cursor:pointer;color:var(--text-secondary);line-height:1;}.sync-dialog__close:hover{color:var(--text-primary);}
.sync-dialog__body{padding:20px 24px;overflow-y:auto;flex:1;}
.sync-dialog__footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:14px 24px;border-top:1px solid var(--border);flex-shrink:0;}
/* 检验记录表格 */
.inspect-section{margin-bottom:20px;}
.inspect-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;}
.inspect-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
.inspect-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.inspect-table td{padding:10px 12px;border-bottom:1px solid var(--border);color:var(--text-primary);}
.inspect-table tr:last-child td{border-bottom:none;}
.inspect-table tr:hover{background:#F8FBFF;}
/* 操作列固定右侧 */
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;}
/* 均值展示区 */
.avg-summary__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;display:flex;align-items:center;gap:6px;}
.avg-tabs{display:flex;gap:0;border-bottom:1px solid var(--border);margin-bottom:12px;}
.avg-tab{padding:6px 14px;font-size:var(--font-xs);cursor:pointer;color:var(--text-secondary);border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .2s;}
.avg-tab:hover{color:var(--primary);}
.avg-tab.active{color:var(--primary);border-bottom-color:var(--primary);font-weight:500;}
.avg-panel{display:none;}
.avg-panel.active{display:block;}
.avg-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;}
.avg-cell{display:flex;flex-direction:column;align-items:center;padding:8px 6px;background:#FAFAFA;border-radius:var(--radius-sm);border:1px solid #F0F0F0;}
.avg-cell__label{font-size:10px;color:var(--text-placeholder);margin-bottom:2px;white-space:nowrap;}
.avg-cell__value{font-size:var(--font-sm);font-weight:600;color:var(--primary);}
/* Toast */
.toast{position:fixed;top:80px;left:50%;transform:translateX(-50%) translateY(-20px);background:#52C41A;color:#fff;padding:10px 28px;border-radius:var(--radius-sm);font-size:var(--font-sm);font-weight:500;box-shadow:0 4px 16px rgba(82,196,26,0.3);z-index:2000;opacity:0;transition:all .3s ease;pointer-events:none;}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
</style>
</head>
<body>
@@ -151,6 +189,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 侧边栏 -->
<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 active"><span class="mi">🔬</span>营养检验</a>
<a href="din-ingredient.html" class="sidebar-item"><span class="mi">🥦</span>食物成分</a>
@@ -189,293 +228,337 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 主内容区 -->
<div 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">48</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">44</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">4</div><div class="stat-card__label">偏差/待改进</div></div></div>
<div class="stat-card"><div class="stat-card__icon blue">🔄</div><div class="stat-card__info"><div class="stat-card__value">6条</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 class="filter-card">
<div class="filter-row">
<div class="filter-item">
<label>检验餐品</label>
<input type="text" placeholder="搜索餐品名称...">
</div>
<div class="filter-item">
<label>检验单号</label>
<input type="text" placeholder="请输入检验单号">
</div>
<div class="filter-item">
<label>检验日期</label>
<div class="date-range">
<input type="date"><span></span><input type="date">
</div>
</div>
<div class="panel-left__actions">
<a href="prod-qc-nutrition-add.html" class="btn btn-primary"> 新增检验</a>
<div class="filter-item">
<label>营养检验结果</label>
<select><option value="">全部</option><option>达标</option><option>偏差</option><option>未达标</option></select>
</div>
<div class="food-list" id="dishList"></div>
<div class="panel-left__footer">
<span>共 48 条</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 class="filter-item">
<label>味道检验结果</label>
<select><option value="">全部</option><option>优秀</option><option>良好</option><option>一般</option><option>较差</option></select>
</div>
<div class="filter-item">
<label>口感检验结果</label>
<select><option value="">全部</option><option>优秀</option><option>良好</option><option>一般</option><option>较差</option></select>
</div>
<div class="filter-item">
<label>色泽检验结果</label>
<select><option value="">全部</option><option>优秀</option><option>良好</option><option>一般</option><option>较差</option></select>
</div>
<div class="filter-actions">
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</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="detailResult">达标</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>
<!-- 操作栏 -->
<div class="action-bar">
<div class="action-bar__left action-bar__left--normal">
<a href="prod-qc-nutrition-add.html" class="btn btn-primary">+ 新增检验</a>
<button class="btn btn-primary" onclick="enterSyncMode()">同步检验均值->餐品总库</button>
<a href="prod-qc-nutrition-sync.html" class="btn btn-default">同步记录</a>
<button class="btn btn-default">导出列表数据</button>
<button class="btn btn-default">查看导出任务</button>
</div>
<div class="action-bar__left action-bar__left--sync">
<button class="btn btn-success" onclick="openSyncDialog()">同步</button>
<button class="btn btn-default" onclick="exitSyncMode()">取消</button>
<span style="font-size:var(--font-sm);color:var(--text-secondary);margin-left:4px;">已选 <strong id="selectedCount">0</strong></span>
</div>
<div class="action-bar__right"><strong id="totalCount">12</strong> 条记录</div>
</div>
<!-- 数据表格 -->
<div class="table-card">
<div class="data-table-wrap">
<table class="data-table">
<thead>
<tr>
<th class="col-cb cb-cell"><input type="checkbox" id="checkAll" onchange="toggleAll(this.checked)"></th>
<th>检验单号</th>
<th>检验时间</th>
<th>检验餐品</th>
<th>食堂</th>
<th>检验人员</th>
<th>营养检验</th>
<th>味道检验</th>
<th>口感检验</th>
<th>色泽检验</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
<div class="pagination">
<div class="pagination__info">第 1-12 条 / 共 12 条</div>
<div class="pagination__btns">
<button class="page-btn disabled">上一页</button>
<button class="page-btn active">1</button>
<button class="page-btn disabled">下一页</button>
</div>
<div class="panel-right__body" id="detailBody"></div>
</div>
</div>
</div>
<!-- 详情抽屉 -->
<div class="drawer-mask" id="drawerMask" onclick="closeDrawer()"></div>
<div class="drawer" id="drawer">
<div class="drawer__header">
<span class="drawer__title" id="drawerTitle">检验详情</span>
<span class="drawer__close" onclick="closeDrawer()">&times;</span>
</div>
<div class="drawer__body" id="drawerBody"></div>
</div>
<!-- 同步确认 Dialog -->
<div class="sync-dialog-mask" id="syncDialogMask" onclick="if(event.target===this)closeSyncDialog()">
<div class="sync-dialog">
<div class="sync-dialog__header">
<span class="sync-dialog__title">确认同步检验均值</span>
<span class="sync-dialog__close" onclick="closeSyncDialog()">&times;</span>
</div>
<div class="sync-dialog__body" id="syncDialogBody"></div>
<div class="sync-dialog__footer">
<button class="btn btn-default" onclick="closeSyncDialog()">取消</button>
<button class="btn btn-success" onclick="confirmSync()">确定同步</button>
</div>
</div>
</div>
<!-- Toast 提示 -->
<div class="toast" id="toast">同步成功</div>
<!-- 交互脚本 -->
<script>
// 菜品检验数据
const DISHES = [
{id:'DISH001',name:'红烧肉',icon:'🥩',result:'达标',sync:'已同步',time:'2026-04-22 14:30',
nutri:{calorie:295,protein:14.2,carb:8.6,fat:22.1,vitC:0.8,mineral:86,note:'铁2.4mg'},
taste:{sour:1,sweet:3,bitter:1,spicy:1,salty:4,smell:4.2,overall:4.0,grade:'优秀'},
texture:{hardness:4,sticky:3,crispy:1,smooth:4,elastic:3,overall:3.8,grade:'良好'},
color:{hue:'深红褐色',uniform:4,gloss:3,fresh:4,look:4,overall:3.8,grade:'良好'}
},
{id:'DISH002',name:'清炒时蔬',icon:'🥬',result:'偏差',sync:'待同步',time:'2026-04-22 14:35',
nutri:{calorie:42,protein:2.1,carb:5.8,fat:1.2,vitC:18.6,mineral:42,note:'钾220mg'},
taste:{sour:1,sweet:1,bitter:1,spicy:1,salty:2,smell:3.8,overall:3.5,grade:'良好'},
texture:{hardness:3,sticky:1,crispy:3,smooth:3,elastic:2,overall:3.2,grade:'良好'},
color:{hue:'翠绿色',uniform:5,gloss:4,fresh:5,look:5,overall:4.8,grade:'优秀'}
},
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',result:'达标',sync:'已同步',time:'2026-04-22 14:40',
nutri:{calorie:38,protein:3.2,carb:3.4,fat:1.8,vitC:12.4,mineral:54,note:'—'},
taste:{sour:2,sweet:2,bitter:1,spicy:1,salty:3,smell:4.0,overall:3.8,grade:'良好'},
texture:{hardness:2,sticky:2,crispy:1,smooth:4,elastic:2,overall:3.5,grade:'良好'},
color:{hue:'橙红色',uniform:4,gloss:3,fresh:4,look:4,overall:3.8,grade:'良好'}
},
{id:'DISH004',name:'宫保鸡丁',icon:'🍗',result:'达标',sync:'待同步',time:'2026-04-21 15:00',
nutri:{calorie:186,protein:18.2,carb:8.1,fat:9.4,vitC:2.1,mineral:94,note:'锌1.8mg'},
taste:{sour:1,sweet:2,bitter:1,spicy:4,salty:3,smell:4.5,overall:4.2,grade:'优秀'},
texture:{hardness:3,sticky:2,crispy:3,smooth:4,elastic:3,overall:4.0,grade:'优秀'},
color:{hue:'红棕色',uniform:4,gloss:4,fresh:4,look:4,overall:4.0,grade:'优秀'}
}
// 检验记录数据(以单次检验记录为维度,含详细指标)
const RECORDS = [
{no:'QC-2026042201',time:'2026-04-22 14:30',dish:'红烧肉',category:'总部一号食堂',line:'A餐线(自选餐)',date:'2026-04-22',inspector:'张明',batch:'第1批次',
nutri:'达标',taste:'优秀',texture:'良好',color:'良好',
nutriData:{cal:235,protein:18.5,carb:8.2,fat:15.3,vc:1.2,mineral:45,fiber:0.8,sodium:680},
tasteData:{sour:1,sweet:2,bitter:1,spicy:2,salty:3,smell:4.2,flavor:4.5,grade:'优秀'},
textureData:{hardness:3,sticky:2,crispy:1,smooth:3,elastic:3,score:3.8,grade:'良好'},
colorData:{mainColor:'深红褐色',uniformity:4,gloss:4,freshness:4,appearance:4,score:4.0,grade:'良好'}},
{no:'QC-2026042202',time:'2026-04-22 14:35',dish:'清炒时蔬',category:'总部一号食堂',line:'A餐线(自选餐)',date:'2026-04-22',inspector:'李华',batch:'第1批次',
nutri:'偏差',taste:'良好',texture:'良好',color:'优秀',
nutriData:{cal:65,protein:3.2,carb:5.8,fat:3.1,vc:28,mineral:38,fiber:2.5,sodium:320},
tasteData:{sour:1,sweet:1,bitter:1,spicy:1,salty:2,smell:3.5,flavor:3.8,grade:'良好'},
textureData:{hardness:2,sticky:1,crispy:3,smooth:3,elastic:2,score:3.6,grade:'良好'},
colorData:{mainColor:'翠绿色',uniformity:5,gloss:5,freshness:5,appearance:5,score:4.8,grade:'优秀'}},
{no:'QC-2026042203',time:'2026-04-22 14:40',dish:'番茄鸡蛋汤',category:'总部二号食堂',line:'A餐线(主食窗口)',date:'2026-04-22',inspector:'张明',batch:'第2批次',
nutri:'达标',taste:'良好',texture:'良好',color:'良好',
nutriData:{cal:42,protein:3.8,carb:4.5,fat:1.2,vc:12,mineral:22,fiber:0.6,sodium:450},
tasteData:{sour:2,sweet:1,bitter:1,spicy:1,salty:2,smell:3.6,flavor:3.7,grade:'良好'},
textureData:{hardness:1,sticky:1,crispy:1,smooth:4,elastic:1,score:3.5,grade:'良好'},
colorData:{mainColor:'橙红色',uniformity:4,gloss:3,freshness:4,appearance:4,score:3.8,grade:'良好'}},
{no:'QC-2026042204',time:'2026-04-22 15:00',dish:'宫保鸡丁',category:'总部一号食堂',line:'B餐线(营养点餐)',date:'2026-04-22',inspector:'王芳',batch:'第1批次',
nutri:'达标',taste:'优秀',texture:'优秀',color:'优秀',
nutriData:{cal:198,protein:22.3,carb:12.1,fat:8.6,vc:5.8,mineral:52,fiber:1.2,sodium:720},
tasteData:{sour:2,sweet:2,bitter:1,spicy:3,salty:3,smell:4.5,flavor:4.6,grade:'优秀'},
textureData:{hardness:3,sticky:1,crispy:2,smooth:3,elastic:4,score:4.2,grade:'优秀'},
colorData:{mainColor:'金红色',uniformity:5,gloss:5,freshness:5,appearance:5,score:4.7,grade:'优秀'}},
{no:'QC-2026042105',time:'2026-04-21 11:20',dish:'鱼香肉丝',category:'采油一厂职工食堂',line:'A餐线(综合餐线)',date:'2026-04-21',inspector:'李华',batch:'第1批次',
nutri:'达标',taste:'优秀',texture:'良好',color:'良好',
nutriData:{cal:185,protein:16.8,carb:10.5,fat:9.2,vc:3.5,mineral:40,fiber:1.5,sodium:650},
tasteData:{sour:2,sweet:2,bitter:1,spicy:2,salty:3,smell:4.3,flavor:4.4,grade:'优秀'},
textureData:{hardness:2,sticky:1,crispy:1,smooth:4,elastic:3,score:3.9,grade:'良好'},
colorData:{mainColor:'橙红色',uniformity:4,gloss:4,freshness:4,appearance:4,score:3.9,grade:'良好'}},
{no:'QC-2026042106',time:'2026-04-21 11:35',dish:'麻婆豆腐',category:'采油一厂职工食堂',line:'A餐线(综合餐线)',date:'2026-04-21',inspector:'张明',batch:'第1批次',
nutri:'达标',taste:'良好',texture:'良好',color:'良好',
nutriData:{cal:120,protein:8.5,carb:6.2,fat:7.8,vc:2.1,mineral:35,fiber:0.9,sodium:580},
tasteData:{sour:1,sweet:1,bitter:1,spicy:4,salty:3,smell:3.8,flavor:3.9,grade:'良好'},
textureData:{hardness:1,sticky:2,crispy:1,smooth:5,elastic:2,score:3.7,grade:'良好'},
colorData:{mainColor:'红褐色',uniformity:4,gloss:3,freshness:4,appearance:3,score:3.6,grade:'良好'}},
{no:'QC-2026042007',time:'2026-04-20 14:10',dish:'糖醋里脊',category:'采油二厂综合食堂',line:'A餐线(自选)',date:'2026-04-20',inspector:'王芳',batch:'第2批次',
nutri:'偏差',taste:'良好',texture:'良好',color:'优秀',
nutriData:{cal:265,protein:15.2,carb:22.8,fat:13.5,vc:1.8,mineral:30,fiber:0.4,sodium:780},
tasteData:{sour:3,sweet:4,bitter:1,spicy:1,salty:2,smell:3.9,flavor:3.8,grade:'良好'},
textureData:{hardness:3,sticky:1,crispy:4,smooth:2,elastic:3,score:3.6,grade:'良好'},
colorData:{mainColor:'金黄色',uniformity:5,gloss:5,freshness:5,appearance:5,score:4.6,grade:'优秀'}},
{no:'QC-2026042008',time:'2026-04-20 14:25',dish:'蒜蓉西兰花',category:'采油二厂综合食堂',line:'A餐线(自选)',date:'2026-04-20',inspector:'李华',batch:'第2批次',
nutri:'达标',taste:'良好',texture:'优秀',color:'优秀',
nutriData:{cal:58,protein:4.1,carb:4.8,fat:2.5,vc:65,mineral:55,fiber:3.2,sodium:280},
tasteData:{sour:1,sweet:1,bitter:1,spicy:1,salty:2,smell:3.7,flavor:3.6,grade:'良好'},
textureData:{hardness:2,sticky:1,crispy:4,smooth:2,elastic:3,score:4.1,grade:'优秀'},
colorData:{mainColor:'深绿色',uniformity:5,gloss:5,freshness:5,appearance:5,score:4.5,grade:'优秀'}},
{no:'QC-2026041909',time:'2026-04-19 10:50',dish:'红烧排骨',category:'总部二号食堂',line:'B餐线(炒菜窗口)',date:'2026-04-19',inspector:'张明',batch:'第1批次',
nutri:'达标',taste:'优秀',texture:'优秀',color:'良好',
nutriData:{cal:280,protein:20.5,carb:10.8,fat:18.2,vc:0.8,mineral:48,fiber:0.3,sodium:750},
tasteData:{sour:1,sweet:3,bitter:1,spicy:1,salty:3,smell:4.6,flavor:4.7,grade:'优秀'},
textureData:{hardness:3,sticky:2,crispy:1,smooth:4,elastic:4,score:4.3,grade:'优秀'},
colorData:{mainColor:'酱红色',uniformity:4,gloss:4,freshness:4,appearance:4,score:3.9,grade:'良好'}},
{no:'QC-2026041910',time:'2026-04-19 11:05',dish:'酸辣土豆丝',category:'采气一厂营养餐厅',line:'A餐线(营养餐线)',date:'2026-04-19',inspector:'王芳',batch:'第1批次',
nutri:'未达标',taste:'一般',texture:'良好',color:'一般',
nutriData:{cal:95,protein:2.1,carb:15.5,fat:3.8,vc:8,mineral:18,fiber:1.8,sodium:920},
tasteData:{sour:4,sweet:1,bitter:1,spicy:3,salty:4,smell:2.8,flavor:2.6,grade:'一般'},
textureData:{hardness:2,sticky:1,crispy:3,smooth:2,elastic:2,score:3.5,grade:'良好'},
colorData:{mainColor:'淡黄色',uniformity:3,gloss:2,freshness:3,appearance:2,score:2.8,grade:'一般'}},
{no:'QC-2026041811',time:'2026-04-18 15:20',dish:'紫菜蛋花汤',category:'总部一号食堂',line:'A餐线(自选餐)',date:'2026-04-18',inspector:'李华',batch:'第3批次',
nutri:'达标',taste:'良好',texture:'良好',color:'良好',
nutriData:{cal:35,protein:3.2,carb:2.8,fat:1.5,vc:3.5,mineral:28,fiber:0.5,sodium:380},
tasteData:{sour:1,sweet:1,bitter:1,spicy:1,salty:2,smell:3.5,flavor:3.6,grade:'良好'},
textureData:{hardness:1,sticky:1,crispy:1,smooth:4,elastic:2,score:3.5,grade:'良好'},
colorData:{mainColor:'紫褐色',uniformity:3,gloss:3,freshness:4,appearance:3,score:3.5,grade:'良好'}},
{no:'QC-2026041812',time:'2026-04-18 15:35',dish:'干煸四季豆',category:'采气一厂营养餐厅',line:'A餐线(营养餐线)',date:'2026-04-18',inspector:'张明',batch:'第3批次',
nutri:'达标',taste:'良好',texture:'优秀',color:'良好',
nutriData:{cal:88,protein:3.5,carb:6.2,fat:5.8,vc:12,mineral:32,fiber:2.8,sodium:420},
tasteData:{sour:1,sweet:1,bitter:1,spicy:2,salty:2,smell:3.7,flavor:3.8,grade:'良好'},
textureData:{hardness:3,sticky:1,crispy:4,smooth:2,elastic:3,score:4.2,grade:'优秀'},
colorData:{mainColor:'深绿带焦',uniformity:4,gloss:4,freshness:4,appearance:4,score:3.8,grade:'良好'}}
];
let currentDish = 0;
let currentTab = 0;
// 渲染左侧菜品列表
function renderDishList(){
const el = document.getElementById('dishList');
el.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.time.split(' ')[0]}</span><span class="tag tag-${d.result==='达标'?'green':'orange'}" style="margin:0;padding:1px 6px;">${d.result}</span></div>
</div>
</div>
`).join('');
// 营养检验结果对应的 tag 样式
function nutriTag(val){
if(val==='达标') return '<span class="tag tag-green">达标</span>';
if(val==='偏差') return '<span class="tag tag-orange">偏差</span>';
return '<span class="tag tag-red">未达标</span>';
}
// 选中菜品
function selectDish(idx){
currentDish = idx;
renderDishList();
renderDetail();
// 感官检验评级对应的 tag 样式
function gradeTag(val){
if(val==='优秀') return '<span class="tag tag-green">优秀</span>';
if(val==='良好') return '<span class="tag tag-blue">良好</span>';
if(val==='一般') return '<span class="tag tag-orange">一般</span>';
return '<span class="tag tag-red">较差</span>';
}
// 切换详情Tab
function switchDetailTab(idx){
currentTab = idx;
document.querySelectorAll('.detail-tab').forEach((t,i)=>t.classList.toggle('active',i===idx));
renderDetail();
// 渲染表格
function renderTable(){
var html = RECORDS.map(function(r,idx){
return '<tr>'
+'<td class="col-cb cb-cell"><input type="checkbox" class="row-cb" data-idx="'+idx+'" onchange="updateSelectedCount()"></td>'
+'<td>'+r.no+'</td>'
+'<td>'+r.time+'</td>'
+'<td>'+r.dish+'</td>'
+'<td>'+r.category+'</td>'
+'<td>'+r.inspector+'</td>'
+'<td>'+nutriTag(r.nutri)+'</td>'
+'<td>'+gradeTag(r.taste)+'</td>'
+'<td>'+gradeTag(r.texture)+'</td>'
+'<td>'+gradeTag(r.color)+'</td>'
+'<td class="td-actions">'
+'<span class="link" onclick="openDrawer('+idx+')">检验详情</span>'
+'<a class="link" href="trace-query.html">食材溯源链路</a>'
+'<a class="link" href="prod-qc-nutrition-add.html?no='+r.no+'">编辑</a>'
+'<span class="btn-danger-link" onclick="confirmDel(\''+r.no+'\')">删除</span>'
+'</td>'
+'</tr>';
}).join('');
document.getElementById('tableBody').innerHTML = html;
}
// 渲染右侧详情
function renderDetail(){
const d = DISHES[currentDish];
document.getElementById('detailName').textContent = d.name;
document.getElementById('detailCode').textContent = d.id;
const resultEl = document.getElementById('detailResult');
resultEl.textContent = d.result;
resultEl.className = 'tag tag-'+(d.result==='达标'?'green':'orange');
const body = document.getElementById('detailBody');
let html = '';
if(currentTab===0) html = renderNutri(d);
else if(currentTab===1) html = renderTaste(d);
else if(currentTab===2) html = renderTexture(d);
else html = renderColor(d);
body.innerHTML = html;
// 删除确认
function confirmDel(no){
if(confirm('确认删除检验记录 '+no+' 吗?此操作不可恢复。')){
alert('已删除记录 '+no);
}
}
// 营养检验详情
function renderNutri(d){
const n = d.nutri;
return `
<div class="inspect-section">
<div class="inspect-section__title">检验指标均值 <span style="display:inline-flex;align-items:center;gap:8px;"><button class="btn btn-default" style="height:26px;padding:0 10px;font-size:var(--font-xs);">🔄 同步均值→餐品库</button><button class="btn btn-default" style="height:26px;padding:0 10px;font-size:var(--font-xs);">📊 同步均值→食物成分库</button><span class="tag tag-blue">已同步</span></span></div>
<div class="inspect-grid">
<div class="inspect-cell"><div class="inspect-cell__label">热量</div><div class="inspect-cell__value">${n.calorie}<span class="inspect-cell__unit">kcal/100g</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">蛋白质</div><div class="inspect-cell__value">${n.protein}<span class="inspect-cell__unit">g</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">碳水化合物</div><div class="inspect-cell__value">${n.carb}<span class="inspect-cell__unit">g</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">脂肪</div><div class="inspect-cell__value">${n.fat}<span class="inspect-cell__unit">g</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">维生素C</div><div class="inspect-cell__value">${n.vitC}<span class="inspect-cell__unit">mg</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">矿物质</div><div class="inspect-cell__value">${n.mineral}<span class="inspect-cell__unit">mg</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">微量元素备注</div><div class="inspect-cell__value" style="font-size:var(--font-sm);">${n.note}</div></div>
<div class="inspect-cell"><div class="inspect-cell__label">检验结果</div><div class="inspect-cell__value"><span class="tag tag-${d.result==='达标'?'green':'orange'}">${d.result}</span></div></div>
</div>
</div>
<div class="inspect-section">
<div class="inspect-section__title">历史检验记录</div>
<div style="display:flex;gap:12px;margin-bottom:12px;align-items:center;">
<label style="font-size:var(--font-xs);color:var(--text-secondary);">测时间:</label>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<span style="font-size:var(--font-xs);color:var(--text-secondary);"></span>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<label style="font-size:var(--font-xs);color:var(--text-secondary);margin-left:12px;">检测状态:</label>
<select style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 8px;font-size:var(--font-xs);background:#fff url(&quot;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&quot;) no-repeat right 8px center;appearance:none;">
<option>全部</option><option>达标</option><option>偏差</option>
</select>
<button class="btn btn-primary" style="height:30px;padding:0 12px;font-size:var(--font-xs);">查询</button>
</div>
<table class="inspect-table">
<thead><tr><th>检验单号</th><th>检验时间</th><th>热量</th><th>蛋白质</th><th>碳水</th><th>脂肪</th><th>维C</th><th>矿物质</th><th>结果</th><th>同步状态</th><th>操作</th></tr></thead>
<tbody>
<tr><td>NUT-N001</td><td>${d.time}</td><td>${n.calorie}</td><td>${n.protein}</td><td>${n.carb}</td><td>${n.fat}</td><td>${n.vitC}</td><td>${n.mineral}</td><td><span class="tag tag-${d.result==='达标'?'green':'orange'}">${d.result}</span></td><td><span class="tag tag-${d.sync==='已同步'?'blue':'orange'}">${d.sync}</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
<tr><td>NUT-N002</td><td>2026-04-20 10:15</td><td>${n.calorie-3}</td><td>${(n.protein-0.2).toFixed(1)}</td><td>${(n.carb+0.3).toFixed(1)}</td><td>${(n.fat-0.5).toFixed(1)}</td><td>${n.vitC}</td><td>${n.mineral-2}</td><td><span class="tag tag-green">达标</span></td><td><span class="tag tag-blue">已同步</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
<tr><td>NUT-N003</td><td>2026-04-18 09:40</td><td>${n.calorie+2}</td><td>${(n.protein+0.1).toFixed(1)}</td><td>${(n.carb-0.2).toFixed(1)}</td><td>${(n.fat+0.3).toFixed(1)}</td><td>${n.vitC}</td><td>${n.mineral+1}</td><td><span class="tag tag-green">达标</span></td><td><span class="tag tag-blue">已同步</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
</tbody>
</table>
</div>`;
}
// 抽屉相关
function openDrawer(idx){
var r=RECORDS[idx];
var n=r.nutriData,t=r.tasteData,x=r.textureData,c=r.colorData;
var html='';
// 基本信息
html+='<div class="detail-section"><div class="detail-section__title">基本信息</div><div class="detail-grid">'
+'<div class="detail-item"><div class="detail-item__label">检验单号</div><div class="detail-item__value">'+r.no+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">检验时间</div><div class="detail-item__value">'+r.time+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">检验人员</div><div class="detail-item__value">'+r.inspector+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">检验批次</div><div class="detail-item__value">'+r.batch+'</div></div>'
+'</div></div>';
// 检验菜品
html+='<div class="detail-section"><div class="detail-section__title">检验菜品</div><div class="detail-grid">'
+'<div class="detail-item"><div class="detail-item__label">食堂</div><div class="detail-item__value">'+r.category+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">餐线</div><div class="detail-item__value">'+r.line+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">日期</div><div class="detail-item__value">'+r.date+'</div></div>'
+'<div class="detail-item"><div class="detail-item__label">菜品名称</div><div class="detail-item__value">'+r.dish+'</div></div>'
+'</div></div>';
// 检验数据 - Tab
html+='<div class="detail-section"><div class="detail-section__title">验数据</div>'
+'<div class="drawer-tabs">'
+'<div class="drawer-tab active" onclick="switchDrawerTab(0)">🥗 营养</div>'
+'<div class="drawer-tab" onclick="switchDrawerTab(1)">👅 味道</div>'
+'<div class="drawer-tab" onclick="switchDrawerTab(2)">🍴 口感</div>'
+'<div class="drawer-tab" onclick="switchDrawerTab(3)">🎨 色泽</div>'
+'</div>';
// 营养面板
html+='<div class="drawer-panel active" id="dp0"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">热量 (kcal/100g)</span><span class="metric-pair__value">'+n.cal+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">蛋白质 (g)</span><span class="metric-pair__value">'+n.protein+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">碳水化合物 (g)</span><span class="metric-pair__value">'+n.carb+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">脂肪 (g)</span><span class="metric-pair__value">'+n.fat+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">维生素C (mg)</span><span class="metric-pair__value">'+n.vc+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">矿物质 (mg)</span><span class="metric-pair__value">'+n.mineral+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">膳食纤维 (g)</span><span class="metric-pair__value">'+n.fiber+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">钠 (mg)</span><span class="metric-pair__value">'+n.sodium+'</span></div>'
+'</div><div style="margin-top:8px;">检验结果:'+nutriTag(r.nutri)+'</div></div>';
// 味道面板
html+='<div class="drawer-panel" id="dp1"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">酸度</span><span class="metric-pair__value">'+t.sour+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">甜度</span><span class="metric-pair__value">'+t.sweet+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">苦度</span><span class="metric-pair__value">'+t.bitter+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">辣度</span><span class="metric-pair__value">'+t.spicy+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">咸度</span><span class="metric-pair__value">'+t.salty+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">气味评分</span><span class="metric-pair__value">'+t.smell+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合风味</span><span class="metric-pair__value">'+t.flavor+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合评级</span><span class="metric-pair__value">'+gradeTag(t.grade)+'</span></div>'
+'</div></div>';
// 口感面板
html+='<div class="drawer-panel" id="dp2"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">软硬度</span><span class="metric-pair__value">'+x.hardness+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">黏糯度</span><span class="metric-pair__value">'+x.sticky+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">酥脆度</span><span class="metric-pair__value">'+x.crispy+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">嫩滑度</span><span class="metric-pair__value">'+x.smooth+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">弹性</span><span class="metric-pair__value">'+x.elastic+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合口感</span><span class="metric-pair__value">'+x.score+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合评级</span><span class="metric-pair__value">'+gradeTag(x.grade)+'</span></div>'
+'</div></div>';
// 色泽面板
html+='<div class="drawer-panel" id="dp3"><div class="metric-row">'
+'<div class="metric-pair"><span class="metric-pair__label">主色调</span><span class="metric-pair__value">'+c.mainColor+'</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">色泽均匀度</span><span class="metric-pair__value">'+c.uniformity+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">光泽度</span><span class="metric-pair__value">'+c.gloss+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">新鲜度</span><span class="metric-pair__value">'+c.freshness+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">卖相评分</span><span class="metric-pair__value">'+c.appearance+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合色泽</span><span class="metric-pair__value">'+c.score+'/5</span></div>'
+'<div class="metric-pair"><span class="metric-pair__label">综合评级</span><span class="metric-pair__value">'+gradeTag(c.grade)+'</span></div>'
+'</div></div>';
html+='</div>';
// 备注
html+='<div class="detail-section"><div class="detail-section__title">附件与备注</div>'
+'<div class="detail-grid"><div class="detail-item"><div class="detail-item__label">检验图片</div><div class="attachment-list"><div class="attachment-thumb">📷</div><div class="attachment-thumb">📷</div></div></div>'
+'<div class="detail-item"><div class="detail-item__label">检验视频</div><div class="attachment-list"><div class="attachment-thumb">📹</div></div></div></div>'
+'<div style="margin-top:12px;"><div class="detail-item"><div class="detail-item__label">备注说明</div><div class="detail-item__value" style="margin-top:4px;color:var(--text-secondary);">检验过程正常,无异常情况</div></div></div></div>';
// 味道检验详情
function renderTaste(d){
const t = d.taste;
return `
<div class="inspect-section">
<div class="inspect-section__title">味道评分均值 <span style="display:inline-flex;align-items:center;gap:8px;"><button class="btn btn-default" style="height:26px;padding:0 10px;font-size:var(--font-xs);">🔄 同步均值→餐品库</button><span class="tag tag-blue">已同步</span></span></div>
<div class="inspect-grid">
<div class="inspect-cell"><div class="inspect-cell__label">酸度</div><div class="inspect-cell__value">${t.sour}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">甜度</div><div class="inspect-cell__value">${t.sweet}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">苦度</div><div class="inspect-cell__value">${t.bitter}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">辣度</div><div class="inspect-cell__value">${t.spicy}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">咸度</div><div class="inspect-cell__value">${t.salty}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">气味评分</div><div class="inspect-cell__value">${t.smell}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">综合风味评分</div><div class="inspect-cell__value">${t.overall}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">综合评级</div><div class="inspect-cell__value"><span class="tag tag-${t.grade==='优秀'?'green':'blue'}">${t.grade}</span></div></div>
</div>
</div>
<div class="inspect-section">
<div class="inspect-section__title">历史检验记录</div>
<div style="display:flex;gap:12px;margin-bottom:12px;align-items:center;">
<label style="font-size:var(--font-xs);color:var(--text-secondary);">检测时间:</label>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<span style="font-size:var(--font-xs);color:var(--text-secondary);"></span>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<label style="font-size:var(--font-xs);color:var(--text-secondary);margin-left:12px;">评级:</label>
<select style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 8px;font-size:var(--font-xs);background:#fff url(&quot;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&quot;) no-repeat right 8px center;appearance:none;">
<option>全部</option><option>优秀</option><option>良好</option>
</select>
<button class="btn btn-primary" style="height:30px;padding:0 12px;font-size:var(--font-xs);">查询</button>
</div>
<table class="inspect-table">
<thead><tr><th>检验单号</th><th>检验时间</th><th></th><th></th><th></th><th></th><th></th><th>气味</th><th>综合</th><th>评级</th><th>操作</th></tr></thead>
<tbody>
<tr><td>NUT-T001</td><td>${d.time}</td><td>${t.sour}</td><td>${t.sweet}</td><td>${t.bitter}</td><td>${t.spicy}</td><td>${t.salty}</td><td>${t.smell}</td><td>${t.overall}</td><td><span class="tag tag-${t.grade==='优秀'?'green':'blue'}">${t.grade}</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
<tr><td>NUT-T002</td><td>2026-04-20 10:15</td><td>${t.sour}</td><td>${t.sweet}</td><td>${t.bitter}</td><td>${t.spicy}</td><td>${t.salty}</td><td>${(t.smell-0.1).toFixed(1)}</td><td>${(t.overall-0.2).toFixed(1)}</td><td><span class="tag tag-blue">良好</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
</tbody>
</table>
</div>`;
document.getElementById('drawerTitle').textContent='检验详情 - '+r.no;
document.getElementById('drawerBody').innerHTML=html;
document.getElementById('drawerMask').classList.add('show');
document.getElementById('drawer').classList.add('show');
}
// 口感检验详情
function renderTexture(d){
const t = d.texture;
return `
<div class="inspect-section">
<div class="inspect-section__title">口感评分均值 <span style="display:inline-flex;align-items:center;gap:8px;"><button class="btn btn-default" style="height:26px;padding:0 10px;font-size:var(--font-xs);">🔄 同步均值→餐品库</button><span class="tag tag-blue">已同步</span></span></div>
<div class="inspect-grid">
<div class="inspect-cell"><div class="inspect-cell__label">软硬度</div><div class="inspect-cell__value">${t.hardness}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">黏糯度</div><div class="inspect-cell__value">${t.sticky}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">酥脆度</div><div class="inspect-cell__value">${t.crispy}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">嫩滑度</div><div class="inspect-cell__value">${t.smooth}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">弹性</div><div class="inspect-cell__value">${t.elastic}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">综合口感评分</div><div class="inspect-cell__value">${t.overall}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">综合评级</div><div class="inspect-cell__value"><span class="tag tag-${t.grade==='优秀'?'green':'blue'}">${t.grade}</span></div></div>
</div>
</div>
<div class="inspect-section">
<div class="inspect-section__title">历史检验记录</div>
<div style="display:flex;gap:12px;margin-bottom:12px;align-items:center;">
<label style="font-size:var(--font-xs);color:var(--text-secondary);">检测时间:</label>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<span style="font-size:var(--font-xs);color:var(--text-secondary);"></span>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<label style="font-size:var(--font-xs);color:var(--text-secondary);margin-left:12px;">评级:</label>
<select style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 8px;font-size:var(--font-xs);background:#fff url(&quot;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&quot;) no-repeat right 8px center;appearance:none;">
<option>全部</option><option>优秀</option><option>良好</option>
</select>
<button class="btn btn-primary" style="height:30px;padding:0 12px;font-size:var(--font-xs);">查询</button>
</div>
<table class="inspect-table">
<thead><tr><th>检验单号</th><th>检验时间</th><th>软硬</th><th>黏糯</th><th>酥脆</th><th>嫩滑</th><th>弹性</th><th>综合</th><th>评级</th><th>操作</th></tr></thead>
<tbody>
<tr><td>NUT-K001</td><td>${d.time}</td><td>${t.hardness}</td><td>${t.sticky}</td><td>${t.crispy}</td><td>${t.smooth}</td><td>${t.elastic}</td><td>${t.overall}</td><td><span class="tag tag-${t.grade==='优秀'?'green':'blue'}">${t.grade}</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
<tr><td>NUT-K002</td><td>2026-04-20 10:15</td><td>${t.hardness}</td><td>${t.sticky}</td><td>${t.crispy}</td><td>${t.smooth}</td><td>${t.elastic}</td><td>${(t.overall-0.2).toFixed(1)}</td><td><span class="tag tag-blue">良好</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
</tbody>
</table>
</div>`;
function closeDrawer(){
document.getElementById('drawerMask').classList.remove('show');
document.getElementById('drawer').classList.remove('show');
}
// 色泽检验详情
function renderColor(d){
const c = d.color;
return `
<div class="inspect-section">
<div class="inspect-section__title">色泽评分均值 <span style="display:inline-flex;align-items:center;gap:8px;"><button class="btn btn-default" style="height:26px;padding:0 10px;font-size:var(--font-xs);">🔄 同步均值→餐品库</button><span class="tag tag-blue">已同步</span></span></div>
<div class="inspect-grid">
<div class="inspect-cell"><div class="inspect-cell__label">主色调</div><div class="inspect-cell__value" style="font-size:var(--font-sm);">${c.hue}</div></div>
<div class="inspect-cell"><div class="inspect-cell__label">色泽均匀度</div><div class="inspect-cell__value">${c.uniform}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">光泽度</div><div class="inspect-cell__value">${c.gloss}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">新鲜度</div><div class="inspect-cell__value">${c.fresh}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">卖相评分</div><div class="inspect-cell__value">${c.look}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">综合色泽评分</div><div class="inspect-cell__value">${c.overall}<span class="inspect-cell__unit">/5</span></div></div>
<div class="inspect-cell"><div class="inspect-cell__label">综合评级</div><div class="inspect-cell__value"><span class="tag tag-${c.grade==='优秀'?'green':'blue'}">${c.grade}</span></div></div>
</div>
</div>
<div class="inspect-section">
<div class="inspect-section__title">历史检验记录</div>
<div style="display:flex;gap:12px;margin-bottom:12px;align-items:center;">
<label style="font-size:var(--font-xs);color:var(--text-secondary);">检测时间:</label>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<span style="font-size:var(--font-xs);color:var(--text-secondary);"></span>
<input type="date" style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);">
<label style="font-size:var(--font-xs);color:var(--text-secondary);margin-left:12px;">评级:</label>
<select style="height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 8px;font-size:var(--font-xs);background:#fff url(&quot;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&quot;) no-repeat right 8px center;appearance:none;">
<option>全部</option><option>优秀</option><option>良好</option>
</select>
<button class="btn btn-primary" style="height:30px;padding:0 12px;font-size:var(--font-xs);">查询</button>
</div>
<table class="inspect-table">
<thead><tr><th>检验单号</th><th>检验时间</th><th>主色调</th><th>均匀度</th><th>光泽</th><th>新鲜度</th><th>卖相</th><th>综合</th><th>评级</th><th>操作</th></tr></thead>
<tbody>
<tr><td>NUT-C001</td><td>${d.time}</td><td>${c.hue}</td><td>${c.uniform}</td><td>${c.gloss}</td><td>${c.fresh}</td><td>${c.look}</td><td>${c.overall}</td><td><span class="tag tag-${c.grade==='优秀'?'green':'blue'}">${c.grade}</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
<tr><td>NUT-C002</td><td>2026-04-20 10:15</td><td>${c.hue}</td><td>${c.uniform}</td><td>${c.gloss}</td><td>${c.fresh}</td><td>${c.look}</td><td>${(c.overall-0.2).toFixed(1)}</td><td><span class="tag tag-blue">良好</span></td><td class="td-actions" style="white-space:nowrap;"><span class="link" style="margin-right:6px;">食材溯源链路</span><span class="link" style="margin-right:6px;">查看视频</span><span class="link" style="margin-right:6px;">查看</span><span class="link">编辑</span></td></tr>
</tbody>
</table>
</div>`;
function switchDrawerTab(idx){
document.querySelectorAll('.drawer-tab').forEach(function(t,i){t.classList.toggle('active',i===idx);});
document.querySelectorAll('.drawer-panel').forEach(function(p,i){p.classList.toggle('active',i===idx);});
}
// 侧边栏折叠
@@ -483,8 +566,157 @@ function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
(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();
renderTable();
/* ===== 同步模式 ===== */
function enterSyncMode(){
document.querySelector('.main').classList.add('sync-mode');
updateSelectedCount();
}
function exitSyncMode(){
document.querySelector('.main').classList.remove('sync-mode');
// 清空所有勾选
document.querySelectorAll('.row-cb').forEach(function(cb){ cb.checked = false; });
var allCb = document.getElementById('checkAll');
if(allCb) allCb.checked = false;
updateSelectedCount();
}
function toggleAll(checked){
document.querySelectorAll('.row-cb').forEach(function(cb){ cb.checked = checked; });
updateSelectedCount();
}
function updateSelectedCount(){
var count = document.querySelectorAll('.row-cb:checked').length;
var el = document.getElementById('selectedCount');
if(el) el.textContent = count;
}
function getSelectedRecords(){
var arr = [];
document.querySelectorAll('.row-cb:checked').forEach(function(cb){
arr.push(RECORDS[parseInt(cb.getAttribute('data-idx'))]);
});
return arr;
}
/* ===== 同步 Dialog ===== */
function calcAvg(records, field){
var sum = 0;
records.forEach(function(r){ sum += r[field]; });
return (sum / records.length).toFixed(1);
}
/* 根据均值分数判定感官等级 */
function scoreToGrade(avg){
var v = parseFloat(avg);
if(v >= 4.0) return '优秀';
if(v >= 3.0) return '良好';
if(v >= 2.0) return '一般';
return '较差';
}
/* 根据多条营养结果取众数判定等级 */
function nutriMajorityGrade(recs){
var cnt = {达标:0,偏差:0,未达标:0};
recs.forEach(function(r){ cnt[r.nutri] = (cnt[r.nutri]||0) + 1; });
if(cnt['未达标'] > 0 && cnt['未达标'] >= cnt['达标']) return '未达标';
if(cnt['偏差'] > cnt['达标']) return '偏差';
return '达标';
}
function openSyncDialog(){
var recs = getSelectedRecords();
if(recs.length === 0){ alert('请至少选择一条检验记录'); return; }
var count = recs.length;
var html = '<div class="avg-summary__title">检验均值汇总 <span class="tag tag-blue">已选 '+count+' 条记录</span></div>';
/* 营养均值 */
var nds = recs.map(function(r){ return r.nutriData; });
var an = {cal:calcAvg(nds,'cal'),protein:calcAvg(nds,'protein'),carb:calcAvg(nds,'carb'),fat:calcAvg(nds,'fat'),
vc:calcAvg(nds,'vc'),mineral:calcAvg(nds,'mineral'),fiber:calcAvg(nds,'fiber'),sodium:calcAvg(nds,'sodium')};
/* 味道均值 */
var tds = recs.map(function(r){ return r.tasteData; });
var at = {sour:calcAvg(tds,'sour'),sweet:calcAvg(tds,'sweet'),bitter:calcAvg(tds,'bitter'),spicy:calcAvg(tds,'spicy'),
salty:calcAvg(tds,'salty'),smell:calcAvg(tds,'smell'),flavor:calcAvg(tds,'flavor')};
/* 口感均值 */
var xds = recs.map(function(r){ return r.textureData; });
var ax = {hardness:calcAvg(xds,'hardness'),sticky:calcAvg(xds,'sticky'),crispy:calcAvg(xds,'crispy'),
smooth:calcAvg(xds,'smooth'),elastic:calcAvg(xds,'elastic'),score:calcAvg(xds,'score')};
/* 色泽均值 */
var cds = recs.map(function(r){ return r.colorData; });
var ac = {uniformity:calcAvg(cds,'uniformity'),gloss:calcAvg(cds,'gloss'),freshness:calcAvg(cds,'freshness'),
appearance:calcAvg(cds,'appearance'),score:calcAvg(cds,'score')};
/* Tab */
html += '<div class="avg-tabs">'
+'<div class="avg-tab active" onclick="switchSyncTab(0)">营养</div>'
+'<div class="avg-tab" onclick="switchSyncTab(1)">味道</div>'
+'<div class="avg-tab" onclick="switchSyncTab(2)">口感</div>'
+'<div class="avg-tab" onclick="switchSyncTab(3)">色泽</div>'
+'</div>';
/* 营养 */
html += '<div class="avg-panel active" id="syncP0"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">热量(kcal)</div><div class="avg-cell__value">'+an.cal+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">蛋白质(g)</div><div class="avg-cell__value">'+an.protein+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">碳水(g)</div><div class="avg-cell__value">'+an.carb+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">脂肪(g)</div><div class="avg-cell__value">'+an.fat+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">维C(mg)</div><div class="avg-cell__value">'+an.vc+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">矿物质(mg)</div><div class="avg-cell__value">'+an.mineral+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">膳食纤维(g)</div><div class="avg-cell__value">'+an.fiber+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">钠(mg)</div><div class="avg-cell__value">'+an.sodium+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">检验等级</div><div class="avg-cell__value">'+nutriTag(nutriMajorityGrade(recs))+'</div></div>'
+'</div></div>';
/* 味道 */
html += '<div class="avg-panel" id="syncP1"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">酸度</div><div class="avg-cell__value">'+at.sour+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">甜度</div><div class="avg-cell__value">'+at.sweet+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">苦度</div><div class="avg-cell__value">'+at.bitter+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">辣度</div><div class="avg-cell__value">'+at.spicy+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">咸度</div><div class="avg-cell__value">'+at.salty+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">气味评分</div><div class="avg-cell__value">'+at.smell+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合风味</div><div class="avg-cell__value">'+at.flavor+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合评级</div><div class="avg-cell__value">'+gradeTag(scoreToGrade(at.flavor))+'</div></div>'
+'</div></div>';
/* 口感 */
html += '<div class="avg-panel" id="syncP2"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">软硬度</div><div class="avg-cell__value">'+ax.hardness+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">黏糯度</div><div class="avg-cell__value">'+ax.sticky+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">酥脆度</div><div class="avg-cell__value">'+ax.crispy+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">嫩滑度</div><div class="avg-cell__value">'+ax.smooth+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">弹性</div><div class="avg-cell__value">'+ax.elastic+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合口感</div><div class="avg-cell__value">'+ax.score+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合评级</div><div class="avg-cell__value">'+gradeTag(scoreToGrade(ax.score))+'</div></div>'
+'</div></div>';
/* 色泽 */
html += '<div class="avg-panel" id="syncP3"><div class="avg-grid">'
+'<div class="avg-cell"><div class="avg-cell__label">均匀度</div><div class="avg-cell__value">'+ac.uniformity+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">光泽度</div><div class="avg-cell__value">'+ac.gloss+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">新鲜度</div><div class="avg-cell__value">'+ac.freshness+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">卖相评分</div><div class="avg-cell__value">'+ac.appearance+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合色泽</div><div class="avg-cell__value">'+ac.score+'</div></div>'
+'<div class="avg-cell"><div class="avg-cell__label">综合评级</div><div class="avg-cell__value">'+gradeTag(scoreToGrade(ac.score))+'</div></div>'
+'</div></div>';
document.getElementById('syncDialogBody').innerHTML = html;
document.getElementById('syncDialogMask').classList.add('show');
}
function closeSyncDialog(){
document.getElementById('syncDialogMask').classList.remove('show');
}
function confirmSync(){
closeSyncDialog();
exitSyncMode();
showToast('同步成功');
}
function switchSyncTab(idx){
var tabs = document.querySelectorAll('#syncDialogBody .avg-tab');
var panels = document.querySelectorAll('#syncDialogBody .avg-panel');
tabs.forEach(function(t,i){ t.classList.toggle('active', i===idx); });
panels.forEach(function(p,i){ p.classList.toggle('active', i===idx); });
}
function showToast(msg){
var el = document.getElementById('toast');
el.textContent = msg;
el.classList.add('show');
setTimeout(function(){ el.classList.remove('show'); }, 2000);
}
</script>
</body>
</html>