feat: 营养用餐模块交互增强——订单排队、食谱管理、生产计划详情页

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 20:34:56 +08:00
co-authored by Claude Opus 4.6
parent af97f9fedb
commit 1cf5ac8cf9
8 changed files with 2201 additions and 197 deletions
-1
View File
@@ -237,7 +237,6 @@ 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>
</div>
<div class="food-list" id="dishList"></div>
-1
View File
@@ -235,7 +235,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
<div class="panel-left__actions">
<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)">
+316 -48
View File
@@ -100,9 +100,10 @@ tbody tr:hover{background:#F8FBFF;}
.summary-row td{background:#FAFAFA;font-weight:600;}
.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;}
thead th:last-child{position:sticky;right:0;background:#FAFAFA;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;white-space:nowrap;}
tbody td:last-child{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;white-space:nowrap;}
tbody tr:hover td:last-child{background:#F8FBFF;}
.summary-row td:last-child{background:#FAFAFA;}
/* 弹窗样式 */
.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center;animation:modalFadeIn .2s ease;}
@@ -123,6 +124,26 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
.modal-table tbody td{padding:10px 12px;border-bottom:1px solid var(--border);color:var(--text-primary);}
.modal-table tbody tr:last-child td{border-bottom:none;}
.modal-table tbody tr:hover{background:#F8FBFF;}
/* 右侧抽屉 */
.drawer-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.45);z-index:1000;animation:modalFadeIn .2s ease;}
.drawer-overlay.closing{animation:modalFadeOut .2s ease forwards;}
.drawer{position:fixed;top:0;right:0;bottom:0;width:520px;max-width:90vw;background:#fff;box-shadow:-6px 0 24px rgba(0,0,0,.12);display:flex;flex-direction:column;z-index:1001;animation:drawerSlideIn .25s ease;}
.drawer-overlay.closing .drawer{animation:drawerSlideOut .2s ease forwards;}
@keyframes drawerSlideIn{from{transform:translateX(100%)}to{transform:translateX(0)}}
@keyframes drawerSlideOut{from{transform:translateX(0)}to{transform:translateX(100%)}}
.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-lg);font-weight:600;color:var(--text-primary);}
.drawer__close{width:32px;height:32px;border:none;background:none;font-size:20px;cursor:pointer;border-radius:var(--radius-sm);color:var(--text-secondary);display:flex;align-items:center;justify-content:center;}
.drawer__close:hover{background:#f5f5f5;color:var(--text-primary);}
.drawer__body{padding:0 24px 24px;overflow-y:auto;flex:1;}
.drawer-section{margin-top:20px;}
.drawer-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);padding-bottom:8px;border-bottom:1px solid var(--border);margin-bottom:12px;display:flex;align-items:center;gap:6px;}
.drawer-section__title::before{content:'';display:inline-block;width:3px;height:14px;background:var(--primary);border-radius:2px;}
.drawer-fields{display:grid;grid-template-columns:1fr 1fr;gap:12px 24px;}
.drawer-field{display:flex;flex-direction:column;gap:2px;}
.drawer-field__label{font-size:var(--font-xs);color:var(--text-placeholder);}
.drawer-field__value{font-size:var(--font-sm);color:var(--text-primary);font-weight:500;min-height:22px;}
</style>
</head>
<body>
@@ -227,17 +248,18 @@ 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(1)">📊 累计汇总(按餐线)</div>
<div class="page-tab" onclick="switchTab(2)">⚖️ 累计汇总(按秤台)</div>
</div>
<!-- Tab1: 实时监控 -->
<div class="tab-panel active">
<div class="filter-bar">
<div class="filter-item"><label>秤台编号</label><input class="filter-input" type="text" placeholder="请输入秤台编号"></div>
<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>A区早餐线</option><option>B区早餐线</option><option>A区中餐线</option><option>B区中餐线</option><option>C区快餐线</option><option>D区晚餐线</option></select></div>
<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" id="filterCanteen" onchange="updateLineOptions()"><option value="">全部</option><option value="1">一号食堂</option><option value="2">二号食堂</option><option value="3">三号食堂</option></select></div>
<div class="filter-item"><label>餐线</label><select class="filter-select" id="filterLine"><option value="">全部</option><option data-canteen="1">A区早餐线</option><option data-canteen="1">A区中餐线</option><option data-canteen="1">B区中餐线</option><option data-canteen="2">B区早餐线</option><option data-canteen="2">C区快餐线</option><option data-canteen="3">D区晚餐线</option></select></div>
<div class="filter-item"><label>日期</label><input class="filter-input" type="date" value="2026-05-19" style="min-width:140px;"></div>
<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><input class="filter-input" type="text" placeholder="请输入称台编号" style="min-width:140px;"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
@@ -253,15 +275,15 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<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><th>应收金额</th><th>实时重量(kg)</th><th>累计取餐重量</th><th>累计上餐生重(kg)</th><th>累计上餐重量</th><th>最高温度</th><th>最低温度</th><th>保鲜时间</th><th>取餐人次</th><th>取餐视频</th><th>上餐视频</th><th>操作</th></tr>
<tr><th>日期</th><th>食堂</th><th>餐线名称</th><th></th><th>秤台编号</th><th>秤台状态</th><th>称台类型</th><th>上餐类型</th><th>开机时间</th><th>上餐时间</th><th>品名称</th><th>品单价</th><th>应收金额</th><th>实时重量(kg)</th><th>累计取餐重量</th><th>累计上餐生重(kg)</th><th>累计上餐重量</th><th>最高温度</th><th>最低温度</th><th>保鲜时间</th><th>取餐人次</th><th>取餐视频</th><th>上餐视频</th><th>操作</th></tr>
</thead>
<tbody>
<tr><td>00022号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>A区早餐线</td><td>早餐</td><td><span class="tag tag-green">开餐</span></td><td>06:50</td><td>07:28</td><td>小米粥</td><td>¥5</td><td>¥1,600</td><td>12.6</td><td><a class="link">9.8kg</a></td><td>14.2</td><td><a class="link">12.6kg</a></td><td>68℃</td><td>55℃</td><td>09:30</td><td>320人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00023号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>B区早餐线</td><td>早餐</td><td><span class="tag tag-green">开餐</span></td><td>06:55</td><td>07:35</td><td>燕麦粥</td><td>¥4</td><td>¥1,144</td><td>8.4</td><td><a class="link">6.2kg</a></td><td>9.6</td><td><a class="link">8.4kg</a></td><td>65℃</td><td>55℃</td><td>09:30</td><td>286人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00025号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>A区中餐线</td><td>午餐</td><td><span class="tag tag-green">开餐</span></td><td>10:30</td><td>11:30</td><td>宫保鸡丁</td><td>¥14</td><td>¥6,804</td><td>38.2</td><td><a class="link">31.6kg</a></td><td>44.0</td><td><a class="link">38.2kg</a></td><td>75℃</td><td>71℃</td><td>13:30</td><td>486人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00026号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>B区中餐线</td><td>午餐</td><td><span class="tag tag-green">开餐</span></td><td>10:35</td><td>11:38</td><td>红烧肉</td><td>¥16</td><td>¥6,592</td><td>42.6</td><td><a class="link">38.4kg</a></td><td>48.5</td><td><a class="link">42.6kg</a></td><td>72℃</td><td>68℃</td><td>13:30</td><td>412人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00027号秤</td><td><span class="tag tag-orange">无网络</span></td><td><span class="tag tag-gray">档口秤</span></td><td>C区快餐线</td><td>午餐</td><td><span class="tag tag-blue">加餐</span></td><td>10:40</td><td>12:12</td><td>番茄炒蛋</td><td>¥8</td><td>¥1,024</td><td>16.8</td><td><a class="link">14.2kg</a></td><td>18.6</td><td><a class="link">16.8kg</a></td><td>74℃</td><td>70℃</td><td>13:30</td><td>128人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00028号秤</td><td><span class="tag tag-gray">未开机</span></td><td><span class="tag tag-gray">档口秤</span></td><td>D区晚餐线</td><td>晚餐</td><td><span class="tag tag-gray">待上餐</span></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>19:30</td><td></td><td></td><td></td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>A区早餐线</td><td>早餐</td><td>00022号秤</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>06:50</td><td>07:28</td><td>小米粥</td><td>¥5</td><td>¥1,600</td><td>12.6</td><td><a class="link">9.8kg</a></td><td>14.2</td><td><a class="link">12.6kg</a></td><td>68℃</td><td>55℃</td><td>09:30</td><td>320人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>二号食堂</td><td>B区早餐线</td><td>早餐</td><td>00023号秤</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>06:55</td><td>07:35</td><td>燕麦粥</td><td>¥4</td><td>¥1,144</td><td>8.4</td><td><a class="link">6.2kg</a></td><td>9.6</td><td><a class="link">8.4kg</a></td><td>65℃</td><td>55℃</td><td>09:30</td><td>286人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>A区中餐线</td><td>中餐</td><td>00025号秤</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>10:30</td><td>11:30</td><td>宫保鸡丁</td><td>¥14</td><td>¥6,804</td><td>38.2</td><td><a class="link">31.6kg</a></td><td>44.0</td><td><a class="link">38.2kg</a></td><td>75℃</td><td>71℃</td><td>13:30</td><td>486人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>B区中餐线</td><td>中餐</td><td>00026号秤</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>10:35</td><td>11:38</td><td>红烧肉</td><td>¥16</td><td>¥6,592</td><td>42.6</td><td><a class="link">38.4kg</a></td><td>48.5</td><td><a class="link">42.6kg</a></td><td>72℃</td><td>68℃</td><td>13:30</td><td>412人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>二号食堂</td><td>C区快餐线</td><td>中餐</td><td>00027号秤</td><td><span class="tag tag-orange">无网络</span></td><td><span class="tag tag-gray">单称</span></td><td><span class="tag tag-blue">加餐</span></td><td>10:40</td><td>12:12</td><td>番茄炒蛋</td><td>¥8</td><td>¥1,024</td><td>16.8</td><td><a class="link">14.2kg</a></td><td>18.6</td><td><a class="link">16.8kg</a></td><td>74℃</td><td>70℃</td><td>13:30</td><td>128人</td><td><a class="link">▶ 取餐</a></td><td><a class="link">▶ 上餐</a></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>三号食堂</td><td>D区晚餐线</td><td>晚餐</td><td>00028号秤</td><td><span class="tag tag-gray">未开机</span></td><td><span class="tag tag-gray">双称</span></td><td><span class="tag tag-gray">待上餐</span></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>19:30</td><td></td><td></td><td></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
</tbody>
</table>
</div>
@@ -279,9 +301,10 @@ 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" type="date"></div>
<div class="filter-item"><label>餐线编号</label><select class="filter-select"><option>全部</option><option>A区中餐线</option><option>B区中餐线</option><option>A区早餐线</option></select></div>
<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><input class="filter-input" type="date" value="2026-05-19" style="min-width:140px;"></div>
<div class="filter-item"><label>食堂</label><select class="filter-select" id="filterCanteen2" onchange="updateLineOptions2()"><option value="">全部</option><option value="1">一号食堂</option><option value="2">二号食堂</option><option value="3">三号食堂</option></select></div>
<div class="filter-item"><label>线</label><select class="filter-select" id="filterLine2"><option value="">全部</option><option data-canteen="1">A区早餐线</option><option data-canteen="1">A区中餐线</option><option data-canteen="1">B区中餐线</option><option data-canteen="2">B区早餐线</option><option data-canteen="2">C区快餐线</option><option data-canteen="3">D区晚餐线</option></select></div>
<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>
@@ -296,17 +319,17 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<div class="table-card__content">
<table>
<thead>
<tr><th>统计日期</th><th>餐线编号</th><th>餐次</th><th>上餐菜品数</th><th>累计取餐重量(kg)</th><th>累计上餐重量(kg)</th><th>累计应收金额</th><th>取餐人次</th><th>平均取餐重量(g/人)</th><th>最高温度</th><th>最低温度</th><th>异常次数</th><th>操作</th></tr>
<tr><th>统计日期</th><th>食堂</th><th>餐线名称</th><th>餐次</th><th>品数</th><th>累计取餐重量(kg)</th><th>累计上餐重量(kg)</th><th>累计应收金额</th><th>取餐人次</th><th>平均取餐重量(g/人)</th><th>最高温度</th><th>最低温度</th><th>异常次数</th><th>操作</th></tr>
</thead>
<tbody>
<tr class="summary-row"><td>汇总</td><td></td><td></td><td>84道</td><td>1,286</td><td>1,540</td><td>¥58,420</td><td>5,612人</td><td>229g</td><td></td><td></td><td>3次</td><td><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-04-28</td><td>A区中餐线</td><td></td><td>12道</td><td>256</td><td>312</td><td>¥14,280</td><td>1,140人</td><td>225g</td><td>78℃</td><td>68℃</td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-04-28</td><td>B区中餐线</td><td></td><td>10道</td><td>218</td><td>268</td><td>¥12,640</td><td>980人</td><td>222g</td><td>76℃</td><td>66℃</td><td>1次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-04-28</td><td>A区早餐线</td><td>早餐</td><td>8道</td><td>148</td><td>182</td><td>¥7,400</td><td>980人</td><td>151g</td><td>70℃</td><td>55℃</td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-04-27</td><td>A区中餐线</td><td></td><td>12道</td><td>248</td><td>304</td><td>¥13,860</td><td>1,100人</td><td>226g</td><td>77℃</td><td>67℃</td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-04-27</td><td>B区中餐线</td><td></td><td>10道</td><td>210</td><td>258</td><td>¥12,180</td><td>960人</td><td>219g</td><td>75℃</td><td>65℃</td><td>1次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-04-27</td><td>A区早餐线</td><td>早餐</td><td>8道</td><td>142</td><td>176</td><td>¥7,100</td><td>920人</td><td>154g</td><td>69℃</td><td>54℃</td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-04-26</td><td>A区中餐线</td><td></td><td>11道</td><td>212</td><td>260</td><td>¥11,860</td><td>960人</td><td>221g</td><td>76℃</td><td>66℃</td><td>1次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr class="summary-row"><td>汇总</td><td></td><td></td><td></td><td>84道</td><td>1,286</td><td>1,540</td><td>¥58,420</td><td>5,612人</td><td>229g</td><td></td><td></td><td>3次</td><td></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>A区中餐线</td><td></td><td>12道</td><td>256</td><td>312</td><td>¥14,280</td><td>1,140人</td><td>225g</td><td>78℃</td><td>68℃</td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>B区中餐线</td><td></td><td>10道</td><td>218</td><td>268</td><td>¥12,640</td><td>980人</td><td>222g</td><td>76℃</td><td>66℃</td><td>1次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>A区早餐线</td><td>早餐</td><td>8道</td><td>148</td><td>182</td><td>¥7,400</td><td>980人</td><td>151g</td><td>70℃</td><td>55℃</td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-18</td><td>一号食堂</td><td>A区中餐线</td><td></td><td>12道</td><td>248</td><td>304</td><td>¥13,860</td><td>1,100人</td><td>226g</td><td>77℃</td><td>67℃</td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-18</td><td>一号食堂</td><td>B区中餐线</td><td></td><td>10道</td><td>210</td><td>258</td><td>¥12,180</td><td>960人</td><td>219g</td><td>75℃</td><td>65℃</td><td>1次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-18</td><td>二号食堂</td><td>B区早餐线</td><td>早餐</td><td>8道</td><td>142</td><td>176</td><td>¥7,100</td><td>920人</td><td>154g</td><td>69℃</td><td>54℃</td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-17</td><td>一号食堂</td><td>A区中餐线</td><td></td><td>11道</td><td>212</td><td>260</td><td>¥11,860</td><td>960人</td><td>221g</td><td>76℃</td><td>66℃</td><td>1次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
</tbody>
</table>
</div>
@@ -320,11 +343,11 @@ 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" type="date"></div>
<div class="filter-item"><label>秤台编号</label><input class="filter-input" type="text" placeholder="请输入秤台编号"></div>
<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>A区早餐线</option><option>B区早餐线</option><option>A区中餐线</option><option>B区中餐线</option><option>C区快餐线</option><option>D区晚餐线</option></select></div>
<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><input class="filter-input" type="date" value="2026-05-19" style="min-width:140px;"></div>
<div class="filter-item"><label>食堂</label><select class="filter-select" id="filterCanteen3" onchange="updateLineOptions3()"><option value="">全部</option><option value="1">一号食堂</option><option value="2">二号食堂</option><option value="3">三号食堂</option></select></div>
<div class="filter-item"><label>餐线</label><select class="filter-select" id="filterLine3"><option value="">全部</option><option data-canteen="1">A区早餐线</option><option data-canteen="1">A区中餐线</option><option data-canteen="1">B区中餐线</option><option data-canteen="2">B区早餐线</option><option data-canteen="2">C区快餐线</option><option data-canteen="3">D区晚餐线</option></select></div>
<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><input class="filter-input" type="text" placeholder="请输入称台编号" style="min-width:140px;"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
@@ -339,16 +362,16 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<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>累计取餐重量(kg)</th><th>累计上餐生重(kg)</th><th>累计上餐重量(kg)</th><th>最高温度</th><th>最低温度</th><th>取餐人次</th><th>平均取餐重量(g/人)</th><th>异常次数</th><th>操作</th></tr>
<tr><th>日期</th><th>食堂</th><th>餐线名称</th><th></th><th>秤台编号</th><th>秤台状态</th><th>称台类型</th><th>上餐类型</th><th>品数</th><th>累计应收金额</th><th>累计取餐重量(kg)</th><th>累计上餐生重(kg)</th><th>累计上餐重量(kg)</th><th>最高温度</th><th>最低温度</th><th>取餐人次</th><th>平均取餐重量(g/人)</th><th>异常次数</th><th>操作</th></tr>
</thead>
<tbody>
<tr class="summary-row"><td>汇总</td><td></td><td></td><td></td><td></td><td></td><td>84道</td><td>¥58,420</td><td>1,286</td><td>1,720</td><td>1,540</td><td></td><td></td><td>5,612人</td><td>229g</td><td>3次</td><td><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00022号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>A区早餐线</td><td>早餐</td><td><span class="tag tag-green">开餐</span></td><td>8道</td><td>¥7,400</td><td>148</td><td>186</td><td>182</td><td>70℃</td><td>55℃</td><td>980人</td><td>151g</td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00023号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>B区早餐线</td><td>早餐</td><td><span class="tag tag-green">开餐</span></td><td>6道</td><td>¥4,860</td><td>92</td><td>118</td><td>108</td><td>68℃</td><td>54℃</td><td>720人</td><td>128g</td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00025号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>A区中餐线</td><td>午餐</td><td><span class="tag tag-green">开餐</span></td><td>12道</td><td>¥14,280</td><td>256</td><td>342</td><td>312</td><td>78℃</td><td>68℃</td><td>1,140人</td><td>225g</td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00026号秤</td><td><span class="tag tag-green">正常</span></td><td><span class="tag tag-blue">餐线秤</span></td><td>B区中餐线</td><td>午餐</td><td><span class="tag tag-green">开餐</span></td><td>10道</td><td>¥12,640</td><td>218</td><td>294</td><td>268</td><td>76℃</td><td>66℃</td><td>980人</td><td>222g</td><td>1次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00027号秤</td><td><span class="tag tag-orange">无网络</span></td><td><span class="tag tag-gray">档口秤</span></td><td>C区快餐线</td><td>午餐</td><td><span class="tag tag-blue">加餐</span></td><td>6道</td><td>¥4,820</td><td>92</td><td>128</td><td>120</td><td>74℃</td><td>70℃</td><td>516人</td><td>178g</td><td>2次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr><td>00028号秤</td><td><span class="tag tag-gray">未开机</span></td><td><span class="tag tag-gray">档口秤</span></td><td>D区晚餐线</td><td>晚餐</td><td><span class="tag tag-gray">待上餐</span></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>0次</td><td class="td-actions"><a class="link">查看</a> <a class="link">编辑</a></td></tr>
<tr class="summary-row"><td>汇总</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>84道</td><td>¥58,420</td><td>1,286</td><td>1,720</td><td>1,540</td><td></td><td></td><td>5,612人</td><td>229g</td><td>3次</td><td></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>A区早餐线</td><td>早餐</td><td>00022号秤</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>8道</td><td>¥7,400</td><td>148</td><td>186</td><td>182</td><td>70℃</td><td>55℃</td><td>980人</td><td>151g</td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>二号食堂</td><td>B区早餐线</td><td>早餐</td><td>00023号秤</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>6道</td><td>¥4,860</td><td>92</td><td>118</td><td>108</td><td>68℃</td><td>54℃</td><td>720人</td><td>128g</td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>A区中餐线</td><td>中餐</td><td>00025号秤</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>12道</td><td>¥14,280</td><td>256</td><td>342</td><td>312</td><td>78℃</td><td>68℃</td><td>1,140人</td><td>225g</td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>一号食堂</td><td>B区中餐线</td><td>中餐</td><td>00026号秤</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>10道</td><td>¥12,640</td><td>218</td><td>294</td><td>268</td><td>76℃</td><td>66℃</td><td>980人</td><td>222g</td><td>1次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>二号食堂</td><td>C区快餐线</td><td>中餐</td><td>00027号秤</td><td><span class="tag tag-orange">无网络</span></td><td><span class="tag tag-gray">单称</span></td><td><span class="tag tag-blue">加餐</span></td><td>6道</td><td>¥4,820</td><td>92</td><td>128</td><td>120</td><td>74℃</td><td>70℃</td><td>516人</td><td>178g</td><td>2次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
<tr><td>2026-05-19</td><td>三号食堂</td><td>D区晚餐线</td><td>晚餐</td><td>00028号秤</td><td><span class="tag tag-gray">未开机</span></td><td><span class="tag tag-gray">双称</span></td><td><span class="tag tag-gray">待上餐</span></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td>0次</td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a></td></tr>
</tbody>
</table>
</div>
@@ -374,6 +397,41 @@ function switchTab(idx){
document.querySelectorAll('.tab-panel').forEach(function(p,i){p.classList.toggle('active',i===idx);});
}
/* ========== 食堂→餐线联动 ========== */
function updateLineOptions(){
var canteenVal=document.getElementById('filterCanteen').value;
var lineSelect=document.getElementById('filterLine');
var opts=lineSelect.querySelectorAll('option');
opts.forEach(function(opt){
if(!opt.value&&!opt.getAttribute('data-canteen')){opt.style.display='';return;}
if(!canteenVal){opt.style.display='';return;}
opt.style.display=opt.getAttribute('data-canteen')===canteenVal?'':'none';
});
lineSelect.value='';
}
function updateLineOptions2(){
var canteenVal=document.getElementById('filterCanteen2').value;
var lineSelect=document.getElementById('filterLine2');
var opts=lineSelect.querySelectorAll('option');
opts.forEach(function(opt){
if(!opt.value&&!opt.getAttribute('data-canteen')){opt.style.display='';return;}
if(!canteenVal){opt.style.display='';return;}
opt.style.display=opt.getAttribute('data-canteen')===canteenVal?'':'none';
});
lineSelect.value='';
}
function updateLineOptions3(){
var canteenVal=document.getElementById('filterCanteen3').value;
var lineSelect=document.getElementById('filterLine3');
var opts=lineSelect.querySelectorAll('option');
opts.forEach(function(opt){
if(!opt.value&&!opt.getAttribute('data-canteen')){opt.style.display='';return;}
if(!canteenVal){opt.style.display='';return;}
opt.style.display=opt.getAttribute('data-canteen')===canteenVal?'':'none';
});
lineSelect.value='';
}
/* ========== 弹窗基础 ========== */
const Modal = {
open(title, bodyHtml) {
@@ -447,7 +505,7 @@ function showPickupDetail(scaleId) {
pageBtns += `<button class="page-btn${i===page?' active':''}" onclick="pickupGoPage(${i})">${i}</button>`;
}
return `${summaryHtml}<table class="modal-table">
<thead><tr><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>${tbody}</tbody>
</table>
<div style="display:flex;align-items:center;justify-content:flex-end;padding:12px 0;gap:8px;">
@@ -491,26 +549,236 @@ function showServingDetail(scaleId) {
const rows = servingData[scaleId] || [];
const tbody = rows.map(r => `<tr><td>${r.time}</td><td>${r.batch}</td><td>${r.type}</td><td>${r.dish}</td><td>${r.rawWeight}</td><td>${r.servWeight}</td><td>${r.chef}</td></tr>`).join('');
const html = `<table class="modal-table">
<thead><tr><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>${tbody}</tbody>
</table>`;
Modal.open('上餐明细 — ' + scaleId, html);
}
/* ========== 事件绑定 ========== */
/* ========== 右侧抽屉 ========== */
const Drawer = {
open(title, bodyHtml) {
this.close();
const overlay = document.createElement('div');
overlay.className = 'drawer-overlay';
overlay.innerHTML = '<div class="drawer">' +
'<div class="drawer__header"><div class="drawer__title">' + title + '</div><button class="drawer__close" onclick="Drawer.close()">&times;</button></div>' +
'<div class="drawer__body">' + bodyHtml + '</div>' +
'</div>';
overlay.addEventListener('click', function(e){ if(e.target===overlay) Drawer.close(); });
document.body.appendChild(overlay);
},
close() {
const o = document.querySelector('.drawer-overlay');
if(!o) return;
o.classList.add('closing');
setTimeout(function(){ o.remove(); }, 200);
}
};
function showScaleDetail(row) {
var cells = row.querySelectorAll('td');
var get = function(i){ return cells[i] ? cells[i].innerHTML.trim() : '—'; };
var getText = function(i){ return cells[i] ? cells[i].textContent.trim() : '—'; };
var html = '' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">设备信息</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">日期</span><span class="drawer-field__value">' + getText(0) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">食堂</span><span class="drawer-field__value">' + getText(1) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">餐线名称</span><span class="drawer-field__value">' + getText(2) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">秤台编号</span><span class="drawer-field__value">' + getText(4) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">秤台状态</span><span class="drawer-field__value">' + get(5) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">称台类型</span><span class="drawer-field__value">' + get(6) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">餐次与时间</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">餐次</span><span class="drawer-field__value">' + getText(3) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">上餐类型</span><span class="drawer-field__value">' + get(7) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">开机时间</span><span class="drawer-field__value">' + getText(8) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">上餐时间</span><span class="drawer-field__value">' + getText(9) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">保鲜时间</span><span class="drawer-field__value">' + getText(19) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">餐品与计费</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">餐品名称</span><span class="drawer-field__value">' + getText(10) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">餐品单价</span><span class="drawer-field__value">' + getText(11) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">应收金额</span><span class="drawer-field__value">' + getText(12) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">取餐人次</span><span class="drawer-field__value">' + getText(20) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">重量数据</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">实时重量(kg)</span><span class="drawer-field__value">' + getText(13) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计取餐重量</span><span class="drawer-field__value">' + getText(14) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计上餐生重(kg)</span><span class="drawer-field__value">' + getText(15) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计上餐重量</span><span class="drawer-field__value">' + getText(16) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">温度监控</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">最高温度</span><span class="drawer-field__value">' + getText(17) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">最低温度</span><span class="drawer-field__value">' + getText(18) + '</span></div>' +
'</div>' +
'</div>';
Drawer.open('秤台详情 — ' + getText(4), html);
}
/* ========== 累计汇总抽屉 ========== */
function showSummaryDetail(row) {
var cells = row.querySelectorAll('td');
var getText = function(i){ return cells[i] ? cells[i].textContent.trim() : '—'; };
var html = '' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">基本信息</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">统计日期</span><span class="drawer-field__value">' + getText(0) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">食堂</span><span class="drawer-field__value">' + getText(1) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">餐线名称</span><span class="drawer-field__value">' + getText(2) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">餐次</span><span class="drawer-field__value">' + getText(3) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">餐品与计费</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">餐品数量</span><span class="drawer-field__value">' + getText(4) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计应收金额</span><span class="drawer-field__value">' + getText(7) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">取餐人次</span><span class="drawer-field__value">' + getText(8) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">平均取餐重量(g/人)</span><span class="drawer-field__value">' + getText(9) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">重量数据</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">累计取餐重量(kg)</span><span class="drawer-field__value">' + getText(5) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计上餐重量(kg)</span><span class="drawer-field__value">' + getText(6) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">温度与异常</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">最高温度</span><span class="drawer-field__value">' + getText(10) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">最低温度</span><span class="drawer-field__value">' + getText(11) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">异常次数</span><span class="drawer-field__value">' + getText(12) + '</span></div>' +
'</div>' +
'</div>';
Drawer.open('累计汇总详情 — ' + getText(2) + ' ' + getText(0), html);
}
/* ========== 绑定查看/取餐/上餐链接 ========== */
// Tab1: 实时监控
(function bindDetailLinks() {
const panel = document.querySelectorAll('.tab-panel')[0];
if (!panel) return;
panel.querySelectorAll('tbody tr').forEach(row => {
const cells = row.querySelectorAll('td');
if (cells.length < 15) return;
const scaleId = cells[0].textContent.trim();
// 累计取餐重量 — 第13列(index 12)
const pickupLink = cells[12] && cells[12].querySelector('.link');
if (cells.length < 17) return;
const scaleId = cells[4].textContent.trim();
// 累计取餐重量 — 第15列(index 14)
const pickupLink = cells[14] && cells[14].querySelector('.link');
if (pickupLink) pickupLink.addEventListener('click', () => showPickupDetail(scaleId));
// 累计上餐重量 — 第15列(index 14)
const servingLink = cells[14] && cells[14].querySelector('.link');
// 累计上餐重量 — 第17列(index 16)
const servingLink = cells[16] && cells[16].querySelector('.link');
if (servingLink) servingLink.addEventListener('click', () => showServingDetail(scaleId));
// 操作列"查看"按钮 — 最后一列第一个 link
const actionsCell = row.querySelector('td.td-actions');
if (actionsCell) {
const links = actionsCell.querySelectorAll('.link');
if (links[0]) links[0].addEventListener('click', function(){ showScaleDetail(row); });
}
});
})();
// Tab2: 累计汇总
(function bindSummaryLinks() {
const panel = document.querySelectorAll('.tab-panel')[1];
if (!panel) return;
panel.querySelectorAll('tbody tr').forEach(row => {
if (row.classList.contains('summary-row')) return;
const actionsCell = row.querySelector('td.td-actions');
if (actionsCell) {
const links = actionsCell.querySelectorAll('.link');
if (links[0]) links[0].addEventListener('click', function(){ showSummaryDetail(row); });
}
});
})();
// Tab3: 累计汇总(按秤台)
function showScaleSummaryDetail(row) {
var cells = row.querySelectorAll('td');
var get = function(i){ return cells[i] ? cells[i].innerHTML.trim() : '—'; };
var getText = function(i){ return cells[i] ? cells[i].textContent.trim() : '—'; };
var html = '' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">基本信息</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">日期</span><span class="drawer-field__value">' + getText(0) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">食堂</span><span class="drawer-field__value">' + getText(1) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">餐线名称</span><span class="drawer-field__value">' + getText(2) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">餐次</span><span class="drawer-field__value">' + getText(3) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">设备信息</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">秤台编号</span><span class="drawer-field__value">' + getText(4) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">秤台状态</span><span class="drawer-field__value">' + get(5) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">称台类型</span><span class="drawer-field__value">' + get(6) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">上餐类型</span><span class="drawer-field__value">' + get(7) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">餐品与计费</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">餐品数量</span><span class="drawer-field__value">' + getText(8) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计应收金额</span><span class="drawer-field__value">' + getText(9) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">取餐人次</span><span class="drawer-field__value">' + getText(15) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">平均取餐重量(g/人)</span><span class="drawer-field__value">' + getText(16) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">重量数据</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">累计取餐重量(kg)</span><span class="drawer-field__value">' + getText(10) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计上餐生重(kg)</span><span class="drawer-field__value">' + getText(11) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">累计上餐重量(kg)</span><span class="drawer-field__value">' + getText(12) + '</span></div>' +
'</div>' +
'</div>' +
'<div class="drawer-section">' +
'<div class="drawer-section__title">温度与异常</div>' +
'<div class="drawer-fields">' +
'<div class="drawer-field"><span class="drawer-field__label">最高温度</span><span class="drawer-field__value">' + getText(13) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">最低温度</span><span class="drawer-field__value">' + getText(14) + '</span></div>' +
'<div class="drawer-field"><span class="drawer-field__label">异常次数</span><span class="drawer-field__value">' + getText(17) + '</span></div>' +
'</div>' +
'</div>';
Drawer.open('秤台汇总详情 — ' + getText(4), html);
}
(function bindScaleSummaryLinks() {
const panel = document.querySelectorAll('.tab-panel')[2];
if (!panel) return;
panel.querySelectorAll('tbody tr').forEach(row => {
if (row.classList.contains('summary-row')) return;
const actionsCell = row.querySelector('td.td-actions');
if (actionsCell) {
const links = actionsCell.querySelectorAll('.link');
if (links[0]) links[0].addEventListener('click', function(){ showScaleSummaryDetail(row); });
}
});
})();
</script>
+276 -53
View File
@@ -116,6 +116,65 @@ 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;}
/* 餐品详情弹窗 */
.meal-section{margin-bottom:20px;}
.meal-section__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);padding-left:10px;border-left:3px solid var(--primary);margin-bottom:12px;}
.meal-info-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px 24px;margin-bottom:24px;}
.meal-info-item{display:flex;align-items:center;gap:8px;font-size:var(--font-sm);padding:8px 0;border-bottom:1px dashed #f0f0f0;}
.meal-info-item__label{color:var(--text-secondary);min-width:72px;flex-shrink:0;}
.meal-info-item__value{color:var(--text-primary);font-weight:500;}
.meal-table{width:100%;border-collapse:collapse;margin-top:8px;}
.meal-table thead th{background:#FAFAFA;padding:10px 16px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.meal-table tbody td{padding:12px 16px;font-size:var(--font-sm);color:var(--text-primary);border-bottom:1px solid var(--border);text-align:center;}
.meal-table tbody tr:last-child td{border-bottom:none;font-weight:600;background:#FAFAFA;}
.meal-table .dish-name{color:var(--primary);cursor:pointer;font-weight:500;}
.meal-table .dish-name:hover{color:#40a9ff;}
.tag-scale{display:inline-block;padding:1px 6px;border-radius:3px;font-size:11px;background:#E6F7FF;color:var(--primary);border:1px solid #91D5FF;margin-left:6px;vertical-align:middle;}
/* 历史用餐明细表格 */
.meal-history-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
.meal-history-table thead th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
.meal-history-table tbody td{padding:12px;color:var(--text-primary);border-bottom:1px solid #f0f0f0;}
.meal-history-table tbody tr:last-child td{border-bottom:none;}
.meal-history-table tbody tr:hover{background:#F8FBFF;}
.score-tag{display:inline-block;padding:2px 8px;border-radius:4px;font-size:var(--font-sm);font-weight:600;}
.score-green{color:#52C41A;background:#F6FFED;}
.score-orange{color:#FA8C16;background:#FFF7E6;}
/* 菜品偏好 */
.dish-pref-section{display:flex;flex-direction:column;gap:12px;}
.dish-pref-item__label{font-size:var(--font-xs);color:var(--text-secondary);margin-bottom:6px;}
.dish-pref-item__tags{display:flex;gap:6px;flex-wrap:wrap;}
.dish-tag{display:inline-block;padding:2px 10px;border-radius:4px;font-size:var(--font-xs);color:#52C41A;background:#F6FFED;border:1px solid #B7EB8F;}
.dish-pref-item__value{font-size:var(--font-sm);color:var(--text-primary);}
.dish-pref-item__big{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
/* 妈妈味道 */
.mom-section{background:#FFF8F0;border-radius:var(--radius-md);padding:12px 16px;}
.mom-header{display:flex;align-items:center;gap:10px;margin-bottom:10px;}
.mom-header__icon{font-size:22px;}
.mom-header__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);}
.mom-header__sub{font-size:var(--font-xs);color:#FA8C16;margin-top:2px;}
.mom-header__tags{display:flex;gap:6px;flex-wrap:wrap;}
.mom-tag{display:inline-block;padding:2px 10px;border-radius:4px;font-size:var(--font-xs);color:#FA8C16;background:#FFF7E6;border:1px solid #FFD591;}
/* 个性模型 */
.model-section{background:#E6F7FF;border-radius:var(--radius-md);padding:12px 16px;margin-bottom:16px;}
.model-header{display:flex;align-items:center;gap:10px;margin-bottom:10px;}
.model-header__icon{font-size:22px;}
.model-header__title{font-size:var(--font-sm);font-weight:600;color:var(--primary);}
.model-header__sub{font-size:var(--font-xs);color:var(--text-secondary);margin-top:2px;}
.model-header__tags{display:flex;gap:6px;flex-wrap:wrap;}
.model-tag{display:inline-block;padding:2px 10px;border-radius:4px;font-size:var(--font-xs);color:var(--primary);background:#fff;border:1px solid #91D5FF;}
.model-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px 20px;margin-bottom:16px;}
.model-item__label{font-size:var(--font-xs);color:var(--text-secondary);margin-bottom:4px;}
.model-item__big{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.model-item__value{font-size:var(--font-sm);}
.risk-tag{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:4px;font-size:var(--font-xs);font-weight:500;}
.risk-safe{background:#F6FFED;color:#52C41A;border:1px solid #B7EB8F;}
.risk-warn{background:#FFF7E6;color:#FA8C16;border:1px solid #FFD591;}
.model-footer{font-size:var(--font-xs);color:var(--text-secondary);padding:10px 12px;background:#F5F7FA;border-radius:var(--radius-sm);}
</style>
</head>
<body>
@@ -187,13 +246,15 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item"><label>员工姓名/工号</label><input class="filter-input" type="text" 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><option>晚餐</option></select></div>
<div class="filter-item"><label>员工姓名</label><input class="filter-input" type="text" placeholder="请输入姓名" style="min-width:120px;"></div>
<div class="filter-item"><label>员工编号</label><input class="filter-input" type="text" placeholder="请输入编号" style="min-width:120px;"></div>
<div class="filter-item"><label>单位部门</label><select class="filter-select"><option>全部</option><option>研发部</option><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><option>三号食堂</option></select></div>
<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><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><option>员工</option><option>工作人员</option><option>外部人员</option></select></div>
<div class="filter-item"><label>日期范围</label><input class="filter-input" type="date"></div>
<div class="filter-item"><label>日期范围</label><input class="filter-input" type="date" style="min-width:130px;"> <span style="margin:0 4px;color:#999;"></span> <input class="filter-input" type="date" style="min-width:130px;"></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
</div>
@@ -202,7 +263,6 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<div class="action-bar">
<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-default">📊 营养汇总报告</button>
</div>
<!-- 数据表格 -->
@@ -213,18 +273,18 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<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><th>营养评分</th><th>操作</th></tr>
<tr><th>就餐记录号</th><th>姓名</th><th>员工编号</th><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>M-20260428-001</td><td>张伟(EMP001)</td><td>研发部</td><td>午餐</td><td><span class="tag tag-blue">点餐取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:02</td><td>11:08</td><td><a class="link">3件</a></td><td><span class="tag tag-green">88分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-002</td><td>李娜(EMP002)</td><td>市场部</td><td>午餐</td><td><span class="tag tag-blue">点餐取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:08</td><td>11:14</td><td><a class="link">3件</a></td><td><span class="tag tag-green">92分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-003</td><td>王磊(EMP003)</td><td>运营部</td><td>午餐</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-gray">非会员</span></td><td>11:15</td><td>11:20</td><td><a class="link">2件</a></td><td><span class="tag tag-orange">68分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-004</td><td>赵敏(EMP004)</td><td>财务部</td><td>午餐</td><td><span class="tag tag-green">档口+餐线</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:22</td><td>11:30</td><td><a class="link">3件</a></td><td><span class="tag tag-green">94分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-005</td><td>陈刚(EMP005)</td><td>人事部</td><td>早餐</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-gray">非会员</span></td><td>07:48</td><td>07:55</td><td><a class="link">3件</a></td><td><span class="tag tag-green">85分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-006</td><td>刘芳(EMP006)</td><td>市场部</td><td>午餐</td><td><span class="tag tag-blue">外卖/外带</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:45</td><td>11:48</td><td><a class="link">2件</a></td><td><span class="tag tag-green">90分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>SP-001-001</td><td>张运维(EMP101)</td><td>运维部</td><td>午餐</td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-blue">员工</span></td><td>14:05</td><td>14:06</td><td><a class="link">1件</a></td><td></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>SP-001-003</td><td>王保安(EMP103)</td><td>安保部</td><td>午餐</td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-blue">工作人员</span></td><td>14:10</td><td>14:11</td><td><a class="link">1件</a></td><td></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-007</td><td>林教授</td><td>外部访客</td><td>午餐</td><td><span class="tag tag-blue">点餐取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-purple">外部人员</span></td><td>12:10</td><td>12:18</td><td><a class="link">3件</a></td><td><span class="tag tag-green">86分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-001</td><td>张伟</td><td>EMP001</td><td>长庆油田</td><td>研发部</td><td>午餐</td><td><span class="tag tag-blue">点餐取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:02</td><td>11:08</td><td><a class="link">3件</a></td><td><span class="tag tag-green">88分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-002</td><td>李娜</td><td>EMP002</td><td>长庆油田</td><td>市场部</td><td>午餐</td><td><span class="tag tag-blue">点餐取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:08</td><td>11:14</td><td><a class="link">3件</a></td><td><span class="tag tag-green">92分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-003</td><td>王磊</td><td>EMP003</td><td>长庆油田</td><td>运营部</td><td>午餐</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-gray">非会员</span></td><td>11:15</td><td>11:20</td><td><a class="link">2件</a></td><td><span class="tag tag-orange">68分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-004</td><td>赵敏</td><td>EMP004</td><td>长庆油田</td><td>财务部</td><td>午餐</td><td><span class="tag tag-green">档口+餐线</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:22</td><td>11:30</td><td><a class="link">3件</a></td><td><span class="tag tag-green">94分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-005</td><td>陈刚</td><td>EMP005</td><td>长庆油田</td><td>人事部</td><td>早餐</td><td><span class="tag tag-gray">餐线取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-gray">非会员</span></td><td>07:48</td><td>07:55</td><td><a class="link">3件</a></td><td><span class="tag tag-green">85分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-006</td><td>刘芳</td><td>EMP006</td><td>长庆油田</td><td>市场部</td><td>午餐</td><td><span class="tag tag-blue">外卖/外带</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-orange">会员</span></td><td>11:45</td><td>11:48</td><td><a class="link">2件</a></td><td><span class="tag tag-green">90分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>SP-001-001</td><td>张运维</td><td>EMP101</td><td>长庆油田</td><td>运维部</td><td>午餐</td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-blue">员工</span></td><td>14:05</td><td>14:06</td><td><a class="link">1件</a></td><td></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>SP-001-003</td><td>王保安</td><td></td><td>长庆油田</td><td>安保部</td><td>午餐</td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-orange">余量取餐</span></td><td><span class="tag tag-blue">工作人员</span></td><td>14:10</td><td>14:11</td><td><a class="link">1件</a></td><td></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
<tr><td>M-20260428-007</td><td>林教授</td><td></td><td>外部单位</td><td>外部访客</td><td>午餐</td><td><span class="tag tag-blue">点餐取餐</span></td><td><span class="tag tag-green">正常取餐</span></td><td><span class="tag tag-purple">外部人员</span></td><td>12:10</td><td>12:18</td><td><a class="link">3件</a></td><td><span class="tag tag-green">86分</span></td><td class="td-actions"><a class="link">查看用户信息</a></td></tr>
</tbody>
</table>
</div>
@@ -274,20 +334,40 @@ const userProfiles = {
taste:'偏辣', region:'川菜、湘菜',
taboo:'无', allergy:'花生',
exercise:'6000步/日', weightTrend:'稳定', bodyFat:'18%', heartRate:'68', sleep:'7.5h', stress:'低', source:'智能手环',
meals30:'58次', hotDish:'宫保鸡丁、番茄牛腩', avgCal:'680kcal', nutriRate:'88%',
favDish:'宫保鸡丁、清炒西兰花', recDish:'三文鱼沙拉', avoidDish:'油炸花生', topRate:'番茄牛腩(4.8)', repurchase:'宫保鸡丁',
hometown:'四川成都', momTag:'麻辣鲜香', momList:'回锅肉、麻婆豆腐', memory:'妈妈的回锅肉', recTry:'水煮鱼',
modelVer:'v2.3', modelTime:'2026-04-20', recCal:'2100kcal', nutriFocus:'蛋白质+膳食纤维', risk:'无', tags:'运动达人、川菜爱好者', confidence:'92%' },
meals30:'86次', hotDish:'宫保鸡丁、番茄牛腩', avgCal:'2,186 kcal/天', nutriRate:'88%',
proteinRate:'92%', highFatRate:'8%', preferMeal:'午餐', lastMeal:'2026-04-28 午餐',
mealHistory:[
{date:'2026-04-28',meal:'午餐',dishes:'清蒸鱼、炒西兰花、米饭、紫菜蛋花汤',cal:'832 kcal',score:95},
{date:'2026-04-28',meal:'早餐',dishes:'小米粥、卤蛋、凉拌黄瓜',cal:'380 kcal',score:92},
{date:'2026-04-27',meal:'午餐',dishes:'杂粮饭、清蒸鱼、蒜蓉菠菜',cal:'798 kcal',score:94},
{date:'2026-04-26',meal:'午餐',dishes:'米饭、宫保鸡丁、炒西兰花、汤',cal:'906 kcal',score:86},
{date:'2026-04-25',meal:'晚餐',dishes:'杂粮饭、清蒸鱼、炒时蔬',cal:'716 kcal',score:96},
{date:'2026-04-25',meal:'午餐',dishes:'米饭、宫保鸡丁、番茄炒蛋',cal:'928 kcal',score:88},
{date:'2026-04-24',meal:'午餐',dishes:'米饭、清蒸鱼、蒜蓉菠菜、番茄蛋花汤',cal:'768 kcal',score:95}
],
favDish:'宫保鸡丁、清炒西兰花、杂粮饭', recDish:'三文鱼沙拉、蒜蓉菠菜', avoidDish:'油炸花生', topRate:'宫保鸡丁(94分)', repurchase:'宫保鸡丁(近30日18次)',
hometown:'四川成都', momTag:'麻辣、红油、鲜香', momList:'回锅肉、麻婆豆腐、水煮鱼', memory:'从小常吃妈妈做的回锅肉,奠定了麻辣口味偏好。', recTry:'川味水煮鱼套餐(已上架一号食堂)',
modelVer:'v2.3', modelTime:'2026-04-20', recCal:'2,250 kcal', nutriFocus:'蛋白质+膳食纤维', risk:'无风险', tags:'清淡偏好、中度活动、无慢病', confidence:'94%', nextUpdate:'2026-05-28' },
'李娜': { dept:'市场部', age:28, gender:'女', height:'163cm', weight:'52kg', bmi:'19.6', activity:'轻度',
disease:'无', chronic:'无', medicine:'无', family:'糖尿病(母)',
examDate:'2026-02-20', bp:'110/70', glucose:'4.8', lipid:'正常', uric:'290', liver:'正常', kidney:'正常',
taste:'清淡', region:'粤菜、日料',
taboo:'无', allergy:'海鲜(虾蟹)',
exercise:'4000步/日', weightTrend:'稳定', bodyFat:'22%', heartRate:'72', sleep:'7h', stress:'中', source:'手机计步',
meals30:'52次', hotDish:'蒸鱼、蔬菜沙拉', avgCal:'520kcal', nutriRate:'92%',
favDish:'清蒸鲈鱼、藜麦沙拉', recDish:'牛油果吐司', avoidDish:'麻辣火锅', topRate:'藜麦沙拉(4.9)', repurchase:'清蒸鲈鱼',
hometown:'广东广州', momTag:'鲜甜清润', momList:'老火靓汤、白切鸡', memory:'周末的煲汤', recTry:'椰子鸡',
modelVer:'v2.3', modelTime:'2026-04-18', recCal:'1650kcal', nutriFocus:'钙+铁+维生素D', risk:'注意糖摄入', tags:'轻食主义、养生达人', confidence:'95%' }
meals30:'52次', hotDish:'清蒸鲈鱼、藜麦沙拉', avgCal:'1,650 kcal/天', nutriRate:'92%',
proteinRate:'95%', highFatRate:'5%', preferMeal:'午餐', lastMeal:'2026-04-28 午餐',
mealHistory:[
{date:'2026-04-28',meal:'午餐',dishes:'藜麦沙拉、清蒸鲈鱼、蒜蓉西兰花',cal:'680 kcal',score:96},
{date:'2026-04-28',meal:'早餐',dishes:'牛油果吐司、酸奶、水果',cal:'420 kcal',score:94},
{date:'2026-04-27',meal:'午餐',dishes:'三文鱼沙拉、杂粮饭、蔬菜汤',cal:'720 kcal',score:97},
{date:'2026-04-26',meal:'午餐',dishes:'清蒸鲈鱼、藜麦饭、凉拌木耳',cal:'650 kcal',score:95},
{date:'2026-04-25',meal:'晚餐',dishes:'蔬菜沙拉、鸡胸肉、全麦面包',cal:'580 kcal',score:93},
{date:'2026-04-25',meal:'午餐',dishes:'番茄鱼片、杂粮饭、炒时蔬',cal:'750 kcal',score:91},
{date:'2026-04-24',meal:'午餐',dishes:'清蒸鲈鱼、藜麦沙拉、紫菜汤',cal:'690 kcal',score:96}
],
favDish:'清蒸鲈鱼、藜麦沙拉、杂粮饭', recDish:'蒜蓉菠菜、紫菜蛋花汤', avoidDish:'暂无', topRate:'清蒸鱼(96分)', repurchase:'清蒸鱼(近30日21次)',
hometown:'广东广州', momTag:'清炖、甜咸适中、河鲜', momList:'松鼠桂鱼、响油鳝糊、腌笃鲜', memory:'从小常吃妈妈做的清炖鱼,奠定了清淡口味偏好。', recTry:'苏式清蒸鱼套餐(已上架二号食堂)',
modelVer:'v2.3', modelTime:'2026-04-18', recCal:'1,650 kcal', nutriFocus:'钙+铁+维生素D', risk:'注意糖摄入', tags:'轻食主义、养生达人、低糖饮食', confidence:'95%', nextUpdate:'2026-05-28' }
};
/* 默认档案(其他用户) */
@@ -297,10 +377,20 @@ const defaultProfile = { dept:'—', age:30, gender:'—', height:'170cm', weigh
taste:'适中', region:'不限',
taboo:'无', allergy:'无',
exercise:'5000步/日', weightTrend:'稳定', bodyFat:'20%', heartRate:'70', sleep:'7h', stress:'低', source:'手动录入',
meals30:'45次', hotDish:'家常菜', avgCal:'600kcal', nutriRate:'78%',
favDish:'红烧肉、西红柿炒蛋', recDish:'杂粮饭套餐', avoidDish:'', topRate:'红烧肉(4.5)', repurchase:'西红柿炒蛋',
hometown:'—', momTag:'家常味', momList:'红烧肉', memory:'—', recTry:'—',
modelVer:'v2.3', modelTime:'2026-04-15', recCal:'1900kcal', nutriFocus:'均衡', risk:'无', tags:'普通用户', confidence:'80%' };
meals30:'45次', hotDish:'家常菜', avgCal:'1,900 kcal/天', nutriRate:'78%',
proteinRate:'80%', highFatRate:'12%', preferMeal:'午餐', lastMeal:'2026-04-28 午餐',
mealHistory:[
{date:'2026-04-28',meal:'午餐',dishes:'红烧肉、米饭、炒青菜、蛋花汤',cal:'856 kcal',score:82},
{date:'2026-04-27',meal:'午餐',dishes:'西红柿炒蛋、米饭、紫菜汤',cal:'720 kcal',score:85},
{date:'2026-04-26',meal:'午餐',dishes:'糖醋里脊、米饭、炒时蔬',cal:'880 kcal',score:80},
{date:'2026-04-25',meal:'午餐',dishes:'红烧肉、杂粮饭、凉拌黄瓜',cal:'810 kcal',score:84},
{date:'2026-04-25',meal:'早餐',dishes:'豆浆、油条、茶叶蛋',cal:'450 kcal',score:76},
{date:'2026-04-24',meal:'午餐',dishes:'宫保鸡丁、米饭、番茄蛋汤',cal:'790 kcal',score:86},
{date:'2026-04-23',meal:'午餐',dishes:'西红柿炒蛋、米饭、炒青菜',cal:'680 kcal',score:88}
],
favDish:'红烧肉、西红柿炒蛋、炒青菜', recDish:'杂粮饭套餐、蒸南瓜', avoidDish:'暂无', topRate:'红烧肉(90分)', repurchase:'西红柿炒蛋(近30日15次)',
hometown:'—', momTag:'家常、咸鲜、炖煮', momList:'红烧肉、炖排骨、西红柿炒蛋', memory:'小时候最爱妈妈做的红烧肉,是家的味道。', recTry:'家常红烧肉套餐(已上架一号食堂)',
modelVer:'v2.3', modelTime:'2026-04-15', recCal:'1,900 kcal', nutriFocus:'均衡', risk:'无风险', tags:'家常口味、中度活动、无慢病', confidence:'80%', nextUpdate:'2026-05-28' };
const tabDefs = [
{ key:'basic', label:'静态基础' },
@@ -360,34 +450,52 @@ function buildProfileHTML(name, p) {
<div class="profile-field"><span class="profile-field__label">数据来源</span><span class="profile-field__value">${p.source}</span></div>
</div>`,
meal: `<div class="profile-grid">
<div class="profile-field"><span class="profile-field__label">近30日就餐</span><span class="profile-field__value">${p.meals30}</span></div>
<div class="profile-field"><span class="profile-field__label">常点菜品</span><span class="profile-field__value">${p.hotDish}</span></div>
<div class="profile-field"><span class="profile-field__label">平均热量</span><span class="profile-field__value">${p.avgCal}</span></div>
<div class="profile-field"><span class="profile-field__label">营养达标率</span><span class="profile-field__value">${p.nutriRate}</span></div>
<div class="profile-field"><span class="profile-field__label">近30日就餐次数</span><span class="profile-field__value">${p.meals30}</span></div>
<div class="profile-field"><span class="profile-field__label">常点菜品</span><span class="profile-field__value">${p.hotDish}</span></div>
<div class="profile-field"><span class="profile-field__label">平均热量摄入</span><span class="profile-field__value">${p.avgCal}</span></div>
<div class="profile-field"><span class="profile-field__label">蛋白质达标率</span><span class="profile-field__value">${p.proteinRate}</span></div>
<div class="profile-field"><span class="profile-field__label">高脂餐占比</span><span class="profile-field__value">${p.highFatRate}</span></div>
<div class="profile-field"><span class="profile-field__label">偏好餐次</span><span class="profile-field__value">${p.preferMeal}</span></div>
</div>
<div class="profile-field" style="border-bottom:none;padding:12px 0 4px;"><span class="profile-field__label">最近一次就餐</span><span class="profile-field__value">${p.lastMeal}</span></div>
<div style="margin-top:16px;font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;">📋 最近用餐明细(${p.mealHistory.length}条)</div>
<table class="meal-history-table">
<thead><tr><th>日期</th><th>餐次</th><th>菜品</th><th>热量</th><th>营养评分</th></tr></thead>
<tbody>${p.mealHistory.map(h => '<tr><td>'+h.date+'</td><td><span class="tag tag-blue">'+h.meal+'</span></td><td>'+h.dishes+'</td><td>'+h.cal+'</td><td><span class="score-tag'+(h.score>=90?' score-green':h.score>=85?' score-green':' score-orange')+'">'+h.score+'分</span></td></tr>').join('')}</tbody>
</table>`,
dish: `<div class="dish-pref-section">
<div class="dish-pref-item"><div class="dish-pref-item__label">常点菜品</div><div class="dish-pref-item__tags">${p.favDish.split('、').map(d=>'<span class="dish-tag">'+d+'</span>').join('')}</div></div>
<div class="dish-pref-item"><div class="dish-pref-item__label">系统推荐</div><div class="dish-pref-item__tags">${p.recDish.split('、').map(d=>'<span class="dish-tag">'+d+'</span>').join('')}</div></div>
<div class="dish-pref-item"><div class="dish-pref-item__label">忌口菜品</div><div class="dish-pref-item__value">${p.avoidDish||'暂无'}</div></div>
<div class="dish-pref-item"><div class="dish-pref-item__label">评分最高</div><div class="dish-pref-item__big">${p.topRate}</div></div>
<div class="dish-pref-item"><div class="dish-pref-item__label">复购冠军</div><div class="dish-pref-item__big">${p.repurchase}</div></div>
</div>`,
dish: `<div class="profile-grid">
<div class="profile-field"><span class="profile-field__label">常点菜品</span><span class="profile-field__value">${p.favDish}</span></div>
<div class="profile-field"><span class="profile-field__label">系统推荐</span><span class="profile-field__value">${p.recDish}</span></div>
<div class="profile-field"><span class="profile-field__label">忌口菜品</span><span class="profile-field__value">${p.avoidDish}</span></div>
<div class="profile-field"><span class="profile-field__label">评分最高</span><span class="profile-field__value">${p.topRate}</span></div>
<div class="profile-field"><span class="profile-field__label">复购冠军</span><span class="profile-field__value">${p.repurchase}</span></div>
mom: `<div class="mom-section">
<div class="mom-header">
<div class="mom-header__icon">🐱</div>
<div class="mom-header__info"><div class="mom-header__title">家乡:${p.hometown}</div><div class="mom-header__sub">从童年记忆中沉淀的味觉偏好与情感连接</div></div>
</div>
<div class="mom-header__tags">${p.momTag.split('、').map(t=>'<span class="mom-tag">'+t+'</span>').join('')}</div>
</div>
<div class="dish-pref-section" style="margin-top:20px;">
<div class="dish-pref-item"><div class="dish-pref-item__label">思乡菜清单</div><div class="dish-pref-item__tags">${p.momList.split('、').map(d=>'<span class="dish-tag" style="color:#FA8C16;background:#FFF7E6;border-color:#FFD591;">'+d+'</span>').join('')}</div></div>
<div class="dish-pref-item"><div class="dish-pref-item__label">关联记忆</div><div class="dish-pref-item__value" style="margin-top:4px;">${p.memory}</div></div>
<div class="dish-pref-item"><div class="dish-pref-item__label">推荐尝试</div><div class="dish-pref-item__value" style="color:#52C41A;font-weight:600;margin-top:4px;">${p.recTry}</div></div>
</div>`,
mom: `<div class="profile-grid">
<div class="profile-field"><span class="profile-field__label">家乡</span><span class="profile-field__value">${p.hometown}</span></div>
<div class="profile-field"><span class="profile-field__label">妈妈味道标签</span><span class="profile-field__value">${p.momTag}</span></div>
<div class="profile-field"><span class="profile-field__label">思乡菜清单</span><span class="profile-field__value">${p.momList}</span></div>
<div class="profile-field"><span class="profile-field__label">关联记忆</span><span class="profile-field__value">${p.memory}</span></div>
<div class="profile-field"><span class="profile-field__label">推荐尝试</span><span class="profile-field__value">${p.recTry}</span></div>
</div>`,
model: `<div class="profile-grid">
<div class="profile-field"><span class="profile-field__label">模型版本</span><span class="profile-field__value">${p.modelVer}</span></div>
<div class="profile-field"><span class="profile-field__label">生成时间</span><span class="profile-field__value">${p.modelTime}</span></div>
<div class="profile-field"><span class="profile-field__label">推荐热量</span><span class="profile-field__value">${p.recCal}</span></div>
<div class="profile-field"><span class="profile-field__label">营养侧重</span><span class="profile-field__value">${p.nutriFocus}</span></div>
<div class="profile-field"><span class="profile-field__label">风险预警</span><span class="profile-field__value">${p.risk}</span></div>
<div class="profile-field"><span class="profile-field__label">个性标签</span><span class="profile-field__value">${p.tags}</span></div>
<div class="profile-field"><span class="profile-field__label">模型置信度</span><span class="profile-field__value">${p.confidence}</span></div>
</div>`
model: `<div class="model-section">
<div class="model-header">
<div class="model-header__icon">🤖</div>
<div class="model-header__info"><div class="model-header__title">${name} 的个性化营养模型</div><div class="model-header__sub">${p.modelVer} · 生成于 ${p.modelTime} · 置信度 ${p.confidence}</div></div>
</div>
<div class="model-header__tags">${p.tags.split('、').map(t=>'<span class="model-tag">'+t+'</span>').join('')}</div>
</div>
<div class="model-grid">
<div class="model-item"><div class="model-item__label">推荐每日热量</div><div class="model-item__big">${p.recCal}</div></div>
<div class="model-item"><div class="model-item__label">营养侧重</div><div class="model-item__big">${p.nutriFocus}</div></div>
<div class="model-item"><div class="model-item__label">风险预警</div><div class="model-item__value"><span class="risk-tag${p.risk==='无风险'?' risk-safe':' risk-warn'}">${p.risk==='无风险'?'✅ ':''} ${p.risk}</span></div></div>
<div class="model-item"><div class="model-item__label">下次模型更新</div><div class="model-item__big">${p.nextUpdate}</div></div>
</div>
<div class="model-footer">️ 个性模型由前9维档案数据综合计算生成(含菜品偏好、妈妈味道等情感维度),每月自动更新一次</div>`
};
const tabs = tabDefs.map((t, i) => `<div class="profile-tab${i===0?' active':''}" onclick="switchTab(this,'${t.key}')">${t.label}</div>`).join('');
const panelsHtml = tabDefs.map((t, i) => `<div class="profile-panel${i===0?' active':''}" data-panel="${t.key}">${panels[t.key]}</div>`).join('');
@@ -416,6 +524,121 @@ document.querySelectorAll('tbody tr').forEach(row => {
link.addEventListener('click', () => showUserProfile(nameCell.textContent));
}
});
/* ========== 取餐详情弹窗 ========== */
const mealData = {
'M-20260428-001': [
{ name:'清炒生菜', type:'秤', weight:133.0, cal:24.6, carb:1.6, fat:1.4, protein:2.2 },
{ name:'西红柿鸡蛋', type:'秤', weight:114.0, cal:94.5, carb:4.7, fat:5.1, protein:8.4 },
{ name:'辣子鸡丁', type:'秤', weight:79.0, cal:92.5, carb:3.3, fat:4.6, protein:9.8 }
],
'M-20260428-002': [
{ name:'藜麦沙拉', type:'秤', weight:150.0, cal:68.0, carb:12.5, fat:1.8, protein:3.2 },
{ name:'清蒸鲈鱼', type:'秤', weight:120.0, cal:112.0, carb:0.5, fat:4.2, protein:18.6 },
{ name:'蒜蓉西兰花', type:'秤', weight:95.0, cal:32.0, carb:4.1, fat:1.2, protein:2.8 }
],
'M-20260428-003': [
{ name:'红烧肉', type:'秤', weight:85.0, cal:210.5, carb:5.2, fat:15.3, protein:12.1 },
{ name:'米饭', type:'秤', weight:200.0, cal:232.0, carb:50.8, fat:0.6, protein:4.6 }
],
'M-20260428-004': [
{ name:'宫保鸡丁', type:'秤', weight:110.0, cal:145.0, carb:8.2, fat:7.5, protein:12.3 },
{ name:'番茄牛腩', type:'秤', weight:130.0, cal:125.0, carb:6.8, fat:5.2, protein:14.5 },
{ name:'杂粮饭', type:'秤', weight:180.0, cal:198.0, carb:42.0, fat:1.2, protein:5.8 }
],
'M-20260428-005': [
{ name:'小米粥', type:'秤', weight:250.0, cal:115.0, carb:24.5, fat:0.8, protein:2.4 },
{ name:'煎蛋', type:'秤', weight:60.0, cal:90.0, carb:0.6, fat:7.0, protein:6.3 },
{ name:'全麦馒头', type:'秤', weight:80.0, cal:176.0, carb:35.2, fat:1.5, protein:6.8 }
],
'M-20260428-006': [
{ name:'鸡胸肉沙拉', type:'秤', weight:180.0, cal:145.0, carb:5.2, fat:3.8, protein:22.5 },
{ name:'玉米浓汤', type:'秤', weight:200.0, cal:88.0, carb:16.0, fat:2.1, protein:2.8 }
],
'SP-001-001': [
{ name:'余量套餐A', type:'秤', weight:220.0, cal:185.0, carb:28.0, fat:4.5, protein:9.2 }
],
'SP-001-003': [
{ name:'余量套餐B', type:'秤', weight:195.0, cal:168.0, carb:25.5, fat:3.8, protein:8.5 }
],
'M-20260428-007': [
{ name:'糖醋里脊', type:'秤', weight:95.0, cal:178.0, carb:14.2, fat:8.5, protein:11.8 },
{ name:'蛋花汤', type:'秤', weight:200.0, cal:35.0, carb:1.2, fat:1.5, protein:3.8 },
{ name:'米饭', type:'秤', weight:180.0, cal:208.8, carb:45.7, fat:0.5, protein:4.1 }
]
};
function showMealDetail(row) {
const cells = row.querySelectorAll('td');
const recordId = cells[0].textContent;
const name = cells[1].textContent;
const empNo = cells[2].textContent;
const unit = cells[3].textContent;
const dept = cells[4].textContent;
const mealTime = cells[5].textContent;
const pickupMethod = cells[6].textContent.trim();
const pickupType = cells[7].textContent.trim();
const startTime = cells[9].textContent;
const endTime = cells[10].textContent;
const items = mealData[recordId] || [];
const totalWeight = items.reduce((s, i) => s + i.weight, 0);
const totalCal = items.reduce((s, i) => s + i.cal, 0);
const totalCarb = items.reduce((s, i) => s + i.carb, 0);
const totalFat = items.reduce((s, i) => s + i.fat, 0);
const totalProtein = items.reduce((s, i) => s + i.protein, 0);
const infoHtml = `<div class="meal-section">
<div class="meal-section__title">基本信息</div>
<div class="meal-info-grid">
<div class="meal-info-item"><span class="meal-info-item__label">就餐记录号</span><span class="meal-info-item__value">${recordId}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">姓名</span><span class="meal-info-item__value">${name}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">员工编号</span><span class="meal-info-item__value">${empNo}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">单位</span><span class="meal-info-item__value">${unit}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">部门</span><span class="meal-info-item__value">${dept}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">餐次</span><span class="meal-info-item__value">${mealTime}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">取餐方式</span><span class="meal-info-item__value">${pickupMethod}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">取餐类型</span><span class="meal-info-item__value">${pickupType}</span></div>
<div class="meal-info-item"><span class="meal-info-item__label">取餐时间</span><span class="meal-info-item__value">${startTime} ~ ${endTime}</span></div>
</div>
</div>`;
const tableRows = items.map(i => `<tr>
<td>${i.name}</td>
<td>${i.weight.toFixed(1)}</td>
<td>${i.cal.toFixed(1)}</td>
<td>${i.carb.toFixed(1)}</td>
<td>${i.fat.toFixed(1)}</td>
<td>${i.protein.toFixed(1)}</td>
</tr>`).join('');
const summaryRow = `<tr>
<td>合计</td>
<td>${totalWeight.toFixed(1)}克</td>
<td>${totalCal.toFixed(1)}千卡</td>
<td>${totalCarb.toFixed(1)}克</td>
<td>${totalFat.toFixed(1)}克</td>
<td>${totalProtein.toFixed(1)}克</td>
</tr>`;
const tableHtml = `<div class="meal-section">
<div class="meal-section__title">餐品列表</div>
<table class="meal-table">
<thead><tr><th>餐品名称</th><th>重量(克)</th><th>热量(千卡)</th><th>碳水化合物(克)</th><th>脂肪(克)</th><th>蛋白质(克)</th></tr></thead>
<tbody>${tableRows}${summaryRow}</tbody>
</table>
</div>`;
Modal.open('取餐详情 — ' + name, infoHtml + tableHtml);
}
/* 绑定"取餐总数量"列点击事件 */
document.querySelectorAll('tbody tr').forEach(row => {
const quantityLink = row.querySelector('td:nth-child(12) .link');
if (quantityLink) {
quantityLink.addEventListener('click', () => showMealDetail(row));
}
});
</script>
</body>
</html>
+755 -34
View File
@@ -87,6 +87,133 @@ 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;}
/* ===== 周食谱日历 ===== */
.recipe-toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;}
.recipe-toolbar__left{display:flex;align-items:center;gap:16px;}
.recipe-toolbar__left label{font-size:var(--font-sm);color:var(--text-secondary);white-space:nowrap;}
.recipe-toolbar__right{display:flex;align-items:center;gap:8px;}
.btn-outline-danger{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:1px solid var(--danger);color:var(--danger);background:#fff;display:inline-flex;align-items:center;gap:6px;transition:all .2s;}
.btn-outline-danger:hover{background:#FFF1F0;}
.btn-outline-primary{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:1px solid var(--primary);color:var(--primary);background:#fff;display:inline-flex;align-items:center;gap:6px;transition:all .2s;}
.btn-outline-primary:hover{background:var(--primary-light);}
.week-grid{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;border:1px solid var(--border);}
.week-grid table{width:100%;border-collapse:collapse;table-layout:fixed;}
.week-grid thead th{background:#FAFAFA;padding:12px 8px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border:1px solid var(--border);}
.week-grid thead th .day-date{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.week-grid thead th .day-name{font-size:var(--font-xs);color:var(--text-secondary);margin-top:2px;}
.week-grid thead th.today{background:#FFF7E6;}
.week-grid thead th.today .day-date{color:#E8740C;}
.week-grid tbody td{border:1px solid var(--border);padding:0;vertical-align:top;height:140px;}
.week-grid tbody td.meal-label{width:64px;background:#FAFAFA;text-align:center;vertical-align:middle;font-size:var(--font-sm);font-weight:600;color:#E8740C;padding:12px 8px;white-space:nowrap;}
.week-grid tbody td.meal-cell{cursor:pointer;transition:background .15s;position:relative;}
.week-grid tbody td.meal-cell:hover{background:#FAFBFF;}
.week-grid tbody td.meal-cell .cell-hint{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#ccc;font-size:24px;display:none;pointer-events:none;}
.week-grid tbody td.meal-cell:hover .cell-hint{display:block;}
.week-grid tbody td.today-col{background:#FFFCF5;}
.recipe-footer{display:flex;justify-content:flex-end;margin-top:16px;}
.btn-save{height:40px;padding:0 24px;border-radius:var(--radius-sm);font-size:var(--font-base);cursor:pointer;border:none;background:#E8740C;color:#fff;display:inline-flex;align-items:center;gap:8px;font-weight:500;transition:all .2s;box-shadow:0 2px 6px rgba(232,116,12,0.3);}
.btn-save:hover{background:#D06000;}
/* ===== 周日历选择器 ===== */
.week-picker{position:relative;display:inline-block;}
.week-picker__trigger{height:36px;padding:0 32px 0 12px;border:1px solid var(--border);border-radius:var(--radius-sm);font-size:var(--font-sm);color:var(--text-primary);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;white-space:nowrap;min-width:300px;text-align:left;display:flex;align-items:center;gap:6px;}
.week-picker__trigger:hover{border-color:var(--primary);}
.week-picker__trigger.open{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.15);}
.week-picker__trigger svg{flex-shrink:0;opacity:0.5;}
.week-picker__dropdown{position:absolute;top:calc(100% + 4px);left:0;z-index:50;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-md);border:1px solid var(--border);padding:12px;width:300px;display:none;}
.week-picker__dropdown.show{display:block;}
.wp-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
.wp-header__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.wp-header__btn{width:28px;height:28px;border:none;background:none;cursor:pointer;border-radius:var(--radius-sm);font-size:16px;color:var(--text-secondary);display:flex;align-items:center;justify-content:center;transition:all .15s;}
.wp-header__btn:hover{background:#F0F0F0;color:var(--text-primary);}
.wp-weekdays{display:grid;grid-template-columns:repeat(7,1fr);text-align:center;margin-bottom:4px;}
.wp-weekdays span{font-size:var(--font-xs);color:var(--text-placeholder);padding:4px 0;}
.wp-days{display:grid;grid-template-columns:repeat(7,1fr);text-align:center;}
.wp-day{padding:6px 0;font-size:var(--font-sm);color:var(--text-primary);cursor:pointer;border-radius:0;transition:background .1s;position:relative;}
.wp-day.other-month{color:#ccc;}
.wp-day.today{font-weight:700;color:var(--primary);}
.wp-day:first-child{border-radius:var(--radius-sm) 0 0 var(--radius-sm);}
.wp-day:last-child,.wp-day:nth-child(7n){border-radius:0 var(--radius-sm) var(--radius-sm) 0;}
.wp-day:nth-child(7n+1){border-radius:var(--radius-sm) 0 0 var(--radius-sm);}
.wp-week-row:hover .wp-day:not(.other-month){background:#E6F7FF;}
.wp-week-row:hover .wp-day.other-month{background:#F5FBFF;}
.wp-week-row{display:contents;}
.wp-week-row.selected .wp-day{background:var(--primary);color:#fff;}
.wp-week-row.selected .wp-day.other-month{background:#69B8FF;color:rgba(255,255,255,0.7);}
.wp-week-row.selected .wp-day.today{color:#fff;}
.wp-today-btn{display:block;width:100%;margin-top:8px;padding:6px 0;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;font-size:var(--font-sm);color:var(--primary);cursor:pointer;transition:all .15s;}
.wp-today-btn:hover{background:var(--primary-light);border-color:var(--primary);}
/* ===== 餐品选择弹窗 ===== */
.dish-modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;display:none;align-items:center;justify-content:center;}
.dish-modal-overlay.show{display:flex;}
.dish-modal{background:#fff;border-radius:var(--radius-lg);width:480px;max-height:70vh;display:flex;flex-direction:column;box-shadow:0 8px 24px rgba(0,0,0,0.18);}
.dish-modal__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
.dish-modal__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.dish-modal__close{width:28px;height:28px;border:none;background:none;cursor:pointer;font-size:18px;color:var(--text-secondary);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;}
.dish-modal__close:hover{background:#F0F0F0;color:var(--text-primary);}
.dish-modal__search{padding:12px 20px;border-bottom:1px solid var(--border);}
.dish-modal__search input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px 0 32px;font-size:var(--font-sm);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='none'%3E%3Ccircle cx='6' cy='6' r='4.5' stroke='%23999' stroke-width='1.2'/%3E%3Cpath d='M9.5 9.5L12.5 12.5' stroke='%23999' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat 10px center;}
.dish-modal__search input:focus{border-color:var(--primary);}
.dish-modal__list{flex:1;overflow-y:auto;padding:8px 0;}
.dish-item{display:flex;align-items:center;gap:10px;padding:10px 20px;cursor:pointer;transition:background .1s;}
.dish-item:hover{background:#F8FBFF;}
.dish-item.selected{background:var(--primary-light);}
.dish-item__check{width:18px;height:18px;border:1.5px solid var(--border);border-radius:3px;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:all .15s;}
.dish-item.selected .dish-item__check{background:var(--primary);border-color:var(--primary);}
.dish-item.selected .dish-item__check::after{content:'';display:block;width:5px;height:9px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg) translate(-1px,-1px);}
.dish-item__info{display:flex;flex-direction:column;gap:2px;}
.dish-item__name{font-size:var(--font-sm);color:var(--text-primary);}
.dish-item__cat{font-size:var(--font-xs);color:var(--text-placeholder);}
.dish-modal__footer{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-top:1px solid var(--border);}
.dish-modal__count{font-size:var(--font-sm);color:var(--text-secondary);}
.dish-modal__actions{display:flex;gap:8px;}
.dish-modal__actions .btn{min-width:72px;justify-content:center;}
/* ===== 单元格内餐品标签 ===== */
.cell-dishes{display:flex;flex-wrap:wrap;gap:4px;padding:6px 8px;}
.cell-dish-tag{display:inline-flex;align-items:center;gap:3px;padding:2px 8px;background:#F0F5FF;border:1px solid #D6E4FF;border-radius:10px;font-size:var(--font-xs);color:var(--primary);max-width:100%;white-space:nowrap;}
.cell-dish-tag__del{width:14px;height:14px;border-radius:50%;border:none;background:none;cursor:pointer;font-size:12px;color:var(--text-placeholder);display:inline-flex;align-items:center;justify-content:center;line-height:1;transition:all .1s;}
.cell-dish-tag__del:hover{color:var(--danger);background:#FFF1F0;}
/* ===== 复用规划食谱弹窗 ===== */
.reuse-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;display:none;align-items:center;justify-content:center;}
.reuse-overlay.show{display:flex;}
.reuse-modal{background:#fff;border-radius:var(--radius-lg);width:1100px;max-width:92vw;max-height:82vh;display:flex;flex-direction:column;box-shadow:0 8px 24px rgba(0,0,0,0.18);}
.reuse-modal__header{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid var(--border);}
.reuse-modal__title{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);}
.reuse-modal__close{width:32px;height:32px;border:none;background:none;cursor:pointer;font-size:20px;color:var(--text-secondary);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;}
.reuse-modal__close:hover{background:#F5F5F5;color:var(--text-primary);}
.reuse-modal__body{display:flex;flex:1;overflow:hidden;}
.reuse-modal__left{width:360px;flex-shrink:0;border-right:1px solid var(--border);display:flex;flex-direction:column;}
.reuse-modal__search{padding:12px 16px;border-bottom:1px solid var(--border);}
.reuse-modal__search input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px 0 32px;font-size:var(--font-sm);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='none'%3E%3Ccircle cx='6' cy='6' r='4.5' stroke='%23999' stroke-width='1.2'/%3E%3Cpath d='M9.5 9.5L12.5 12.5' stroke='%23999' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat 10px center;}
.reuse-modal__search input:focus{border-color:var(--primary);}
.reuse-modal__list{flex:1;overflow-y:auto;padding:8px 0;}
.reuse-recipe-item{display:flex;align-items:flex-start;gap:10px;padding:12px 16px;cursor:pointer;transition:background .1s;border-bottom:1px solid #F5F5F5;}
.reuse-recipe-item:hover{background:#F8FBFF;}
.reuse-recipe-item.selected{background:var(--primary-light);}
.reuse-recipe-item__radio{width:18px;height:18px;border:1.5px solid var(--border);border-radius:50%;flex-shrink:0;margin-top:2px;display:flex;align-items:center;justify-content:center;transition:all .15s;}
.reuse-recipe-item.selected .reuse-recipe-item__radio{border-color:var(--primary);background:var(--primary);}
.reuse-recipe-item.selected .reuse-recipe-item__radio::after{content:'';display:block;width:6px;height:6px;border-radius:50%;background:#fff;}
.reuse-recipe-item__info{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0;}
.reuse-recipe-item__name{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.reuse-recipe-item__meta{display:flex;align-items:center;gap:6px;flex-wrap:wrap;}
.reuse-modal__right{flex:1;display:flex;flex-direction:column;overflow:hidden;}
.reuse-modal__preview-title{padding:12px 20px;font-size:var(--font-sm);color:var(--text-secondary);border-bottom:1px solid var(--border);flex-shrink:0;}
.reuse-modal__preview{flex:1;overflow:auto;padding:16px 20px;}
.reuse-modal__preview .week-grid{border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:none;}
.reuse-modal__preview .week-grid table{table-layout:fixed;}
.reuse-modal__preview .week-grid thead th{padding:10px 6px;font-size:var(--font-xs);}
.reuse-modal__preview .week-grid tbody td{height:100px;}
.reuse-modal__preview .week-grid .meal-label{width:50px;font-size:var(--font-xs);}
.reuse-modal__preview .cell-dishes{padding:4px 6px;gap:3px;}
.reuse-modal__preview .cell-dish-tag{font-size:10px;padding:1px 6px;}
.reuse-modal__empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-placeholder);font-size:var(--font-sm);}
.reuse-modal__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);}
</style>
</head>
<body>
@@ -154,48 +281,642 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 主内容 -->
<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><select class="filter-select"><option>全部</option><option>已发布</option><option>待发布</option><option>已停用</option></select></div>
<div class="filter-item"><label>生效周期</label><input class="filter-input" type="date"></div>
<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><option>超标</option><option>待评估</option></select></div>
<button class="btn btn-primary">查询</button>
<button class="btn btn-default">重置</button>
<!-- 工具栏:食堂选择 + 周选择 + 操作按钮 -->
<div class="recipe-toolbar">
<div class="recipe-toolbar__left">
<div class="filter-item">
<label>食堂:</label>
<select class="filter-select" id="canteenSelect">
<option value="1" selected>一号食堂</option>
<option value="2">二号食堂</option>
<option value="3">三号食堂</option>
</select>
</div>
<div class="filter-item">
<label>当前周:</label>
<div class="week-picker" id="weekPicker">
<button class="week-picker__trigger" id="wpTrigger">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><rect x="1" y="2" width="12" height="11" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M1 5.5h12M4 1v2M10 1v2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
<span id="wpLabel">2026-05-18 ~ 2026-05-24</span>
</button>
<div class="week-picker__dropdown" id="wpDropdown">
<div class="wp-header">
<button class="wp-header__btn" id="wpPrev">&#8249;</button>
<span class="wp-header__title" id="wpMonthTitle">2026年5月</span>
<button class="wp-header__btn" id="wpNext">&#8250;</button>
</div>
<div class="wp-weekdays"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
<div class="wp-days" id="wpDays"></div>
<button class="wp-today-btn" id="wpToday">回到本周</button>
</div>
</div>
</div>
</div>
<div class="recipe-toolbar__right">
<button class="btn-outline-danger" id="btnResetWeek">↻ 重置本周</button>
<button class="btn-outline-primary" id="btnReuseRecipe">📋 复用规划食谱</button>
<button class="btn-outline-primary">⧉ 复制到下周</button>
</div>
</div>
<!-- 表格卡片 -->
<div class="action-bar"><!-- 新建食谱:后续用独立二级页面实现 --><button class="btn btn-primary"> 新建食谱</button> <button class="btn btn-default">📋 生成净菜订单</button> <button class="btn btn-default">🚀 发布食谱</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>
<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><th>发布人</th><th>操作</th></tr></thead>
<tbody>
<tr><td>RECIPE-015</td><td>第15周营养食谱</td><td>2026-04-14~20</td><td>一号食堂/二号食堂</td><td>食堂+小程序</td><td>42道</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-13 18:30</td><td>营养师李明</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>RECIPE-014</td><td>第14周营养食谱</td><td>2026-04-07~13</td><td>一号食堂</td><td>食堂+小程序</td><td>38道</td><td><span class="tag tag-green">已发布</span></td><td><span class="tag tag-blue">已生成</span></td><td><span class="tag tag-orange">偏高</span></td><td>2026-04-06 18:10</td><td>营养师李明</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>RECIPE-016</td><td>第16周营养食谱</td><td>2026-04-21~27</td><td>一号食堂/三号食堂</td><td>食堂+小程序</td><td>40道</td><td><span class="tag tag-orange">待发布</span></td><td><span class="tag tag-orange">待生成</span></td><td><span class="tag tag-gray">待评估</span></td><td></td><td>营养师李明</td><td class="td-actions"><span class="link">编辑</span> <span class="link">详情</span> <span class="link" style="color:var(--danger)">删除</span></td></tr>
</tbody>
</table>
</div>
<div class="pagination">
<span>显示 3 条,共 12 条</span>
<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">4</button>
<button class="page-btn"></button>
</div>
<!-- 周食谱网格 -->
<div class="week-grid">
<table>
<thead>
<tr>
<th style="width:64px;"></th>
<th><div class="day-date">2026-05-18</div><div class="day-name">星期一</div></th>
<th class="today"><div class="day-date">2026-05-19</div><div class="day-name">星期二</div></th>
<th><div class="day-date">2026-05-20</div><div class="day-name">星期三</div></th>
<th><div class="day-date">2026-05-21</div><div class="day-name">星期四</div></th>
<th><div class="day-date">2026-05-22</div><div class="day-name">星期五</div></th>
<th><div class="day-date">2026-05-23</div><div class="day-name">星期六</div></th>
<th><div class="day-date">2026-05-24</div><div class="day-name">星期日</div></th>
</tr>
</thead>
<tbody>
<!-- 早餐 -->
<tr>
<td class="meal-label">早餐</td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell today-col"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
</tr>
<!-- 午餐 -->
<tr>
<td class="meal-label">午餐</td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell today-col"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
</tr>
<!-- 晚餐 -->
<tr>
<td class="meal-label">晚餐</td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell today-col"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
</tr>
<!-- 加餐 -->
<tr>
<td class="meal-label">加餐</td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell today-col"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
<td class="meal-cell"><span class="cell-hint">+</span></td>
</tr>
</tbody>
</table>
</div>
<!-- 底部保存按钮 -->
<div class="recipe-footer">
<button class="btn-save">💾 保存</button>
</div>
</main>
<!-- 复用规划食谱弹窗 -->
<div class="reuse-overlay" id="reuseOverlay">
<div class="reuse-modal">
<div class="reuse-modal__header">
<div class="reuse-modal__title">复用规划食谱</div>
<button class="reuse-modal__close" id="reuseClose">&times;</button>
</div>
<div class="reuse-modal__body">
<div class="reuse-modal__left">
<div class="reuse-modal__search">
<input type="text" id="reuseSearch" placeholder="搜索菜谱名称...">
</div>
<div class="reuse-modal__list" id="reuseList"></div>
</div>
<div class="reuse-modal__right">
<div class="reuse-modal__preview-title">一周菜谱预览</div>
<div class="reuse-modal__preview" id="reusePreview">
<div class="reuse-modal__empty">请从左侧选择一个食谱查看详情</div>
</div>
</div>
</div>
<div class="reuse-modal__footer">
<button class="btn btn-default" id="reuseCancel">取消</button>
<button class="btn btn-primary" id="reuseConfirm">确定复用</button>
</div>
</div>
</div>
<!-- 餐品选择弹窗 -->
<div class="dish-modal-overlay" id="dishOverlay">
<div class="dish-modal">
<div class="dish-modal__header">
<span class="dish-modal__title">选择餐品</span>
<button class="dish-modal__close" id="dishClose">&times;</button>
</div>
<div class="dish-modal__search">
<input type="text" id="dishSearch" placeholder="搜索餐品名称...">
</div>
<div class="dish-modal__list" id="dishList"></div>
<div class="dish-modal__footer">
<span class="dish-modal__count" id="dishCount">已选 0 项</span>
<div class="dish-modal__actions">
<button class="btn btn-default" id="dishCancel">取消</button>
<button class="btn btn-primary" id="dishConfirm">确认</button>
</div>
</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(){
var trigger=document.getElementById('wpTrigger');
var dropdown=document.getElementById('wpDropdown');
var label=document.getElementById('wpLabel');
var monthTitle=document.getElementById('wpMonthTitle');
var daysContainer=document.getElementById('wpDays');
var prevBtn=document.getElementById('wpPrev');
var nextBtn=document.getElementById('wpNext');
var todayBtn=document.getElementById('wpToday');
var today=new Date(2026,4,19); // 2026-05-19
var viewYear=2026, viewMonth=4; // 0-indexed
var selectedMonday=new Date(2026,4,18); // 当前选中周的周一
function pad(n){return n<10?'0'+n:n;}
function fmt(d){return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate());}
function sameDay(a,b){return a.getFullYear()===b.getFullYear()&&a.getMonth()===b.getMonth()&&a.getDate()===b.getDate();}
// 获取某日期所在周的周一
function getMonday(d){
var dt=new Date(d);
var day=dt.getDay()||7; // 周日=7
dt.setDate(dt.getDate()-(day-1));
dt.setHours(0,0,0,0);
return dt;
}
function updateLabel(){
var sun=new Date(selectedMonday);
sun.setDate(sun.getDate()+6);
label.textContent=fmt(selectedMonday)+' ~ '+fmt(sun);
}
function updateGrid(){
// 更新周食谱网格表头
var ths=document.querySelectorAll('.week-grid thead th');
var dayNames=['星期一','星期二','星期三','星期四','星期五','星期六','星期日'];
for(var i=1;i<ths.length;i++){
var d=new Date(selectedMonday);
d.setDate(d.getDate()+(i-1));
var isToday=sameDay(d,today);
ths[i].className=isToday?'today':'';
ths[i].innerHTML='<div class="day-date">'+fmt(d)+'</div><div class="day-name">'+dayNames[i-1]+'</div>';
}
// 更新 today-col
var rows=document.querySelectorAll('.week-grid tbody tr');
rows.forEach(function(tr){
var cells=tr.querySelectorAll('td.meal-cell');
cells.forEach(function(td,idx){
var d=new Date(selectedMonday);
d.setDate(d.getDate()+idx);
td.classList.toggle('today-col',sameDay(d,today));
});
});
}
function renderCalendar(){
monthTitle.textContent=viewYear+'年'+(viewMonth+1)+'月';
daysContainer.innerHTML='';
// 本月1号
var first=new Date(viewYear,viewMonth,1);
// 本月1号是周几 (周一=1...周日=7)
var startDay=first.getDay()||7;
// 日历起始日 = 1号往前推到周一
var startDate=new Date(first);
startDate.setDate(startDate.getDate()-(startDay-1));
// 渲染6行(42天)
for(var row=0;row<6;row++){
var weekDiv=document.createElement('div');
weekDiv.className='wp-week-row';
var rowMonday=new Date(startDate);
rowMonday.setDate(rowMonday.getDate()+row*7);
for(var col=0;col<7;col++){
var d=new Date(rowMonday);
d.setDate(d.getDate()+col);
var cell=document.createElement('div');
cell.className='wp-day';
cell.textContent=d.getDate();
if(d.getMonth()!==viewMonth) cell.classList.add('other-month');
if(sameDay(d,today)) cell.classList.add('today');
(function(date,monday){
cell.addEventListener('click',function(){
selectedMonday=new Date(monday);
updateLabel();
updateGrid();
renderCalendar();
// 关闭面板
dropdown.classList.remove('show');
trigger.classList.remove('open');
});
})(d,rowMonday);
weekDiv.appendChild(cell);
}
// 判断是否是选中周
if(sameDay(rowMonday,selectedMonday)){
weekDiv.classList.add('selected');
}
daysContainer.appendChild(weekDiv);
// 如果这行已经超出本月且到下月第2周,停止
var lastOfRow=new Date(rowMonday);
lastOfRow.setDate(lastOfRow.getDate()+6);
if(row>=4 && lastOfRow.getMonth()!==viewMonth && lastOfRow.getDate()>7) break;
}
}
// 开关面板
trigger.addEventListener('click',function(e){
e.stopPropagation();
var isOpen=dropdown.classList.contains('show');
if(isOpen){
dropdown.classList.remove('show');
trigger.classList.remove('open');
}else{
// 打开时定位到选中周所在月
viewYear=selectedMonday.getFullYear();
viewMonth=selectedMonday.getMonth();
renderCalendar();
dropdown.classList.add('show');
trigger.classList.add('open');
}
});
// 点外部关闭
document.addEventListener('click',function(e){
if(!document.getElementById('weekPicker').contains(e.target)){
dropdown.classList.remove('show');
trigger.classList.remove('open');
}
});
prevBtn.addEventListener('click',function(e){
e.stopPropagation();
viewMonth--;
if(viewMonth<0){viewMonth=11;viewYear--;}
renderCalendar();
});
nextBtn.addEventListener('click',function(e){
e.stopPropagation();
viewMonth++;
if(viewMonth>11){viewMonth=0;viewYear++;}
renderCalendar();
});
todayBtn.addEventListener('click',function(e){
e.stopPropagation();
selectedMonday=getMonday(today);
viewYear=selectedMonday.getFullYear();
viewMonth=selectedMonday.getMonth();
updateLabel();
updateGrid();
renderCalendar();
dropdown.classList.remove('show');
trigger.classList.remove('open');
});
// 初始化
updateLabel();
})();
/* ===== 餐品选择弹窗逻辑 ===== */
(function(){
var DISHES=[
{id:1,name:'白米饭',cat:'主食/米饭'},{id:2,name:'杂粮饭',cat:'主食/米饭'},
{id:3,name:'馒头',cat:'主食/面点'},{id:4,name:'花卷',cat:'主食/面点'},
{id:5,name:'小米粥',cat:'主食/粥品'},{id:6,name:'皮蛋瘦肉粥',cat:'主食/粥品'},
{id:7,name:'红烧肉',cat:'荤菜/猪肉'},{id:8,name:'糖醋排骨',cat:'荤菜/猪肉'},
{id:9,name:'清蒸鲈鱼',cat:'荤菜/水产'},{id:10,name:'宫保鸡丁',cat:'荤菜/禽类'},
{id:11,name:'西红柿炒蛋',cat:'素菜/蛋类'},{id:12,name:'清炒时蔬',cat:'素菜/叶菜'},
{id:13,name:'麻婆豆腐',cat:'素菜/豆制品'},{id:14,name:'凉拌黄瓜',cat:'凉菜/蔬菜'},
{id:15,name:'酸辣土豆丝',cat:'素菜/根茎'},{id:16,name:'紫菜蛋花汤',cat:'汤品/蛋汤'},
{id:17,name:'冬瓜排骨汤',cat:'汤品/肉汤'},{id:18,name:'水果拼盘',cat:'甜品/水果'},
{id:19,name:'绿豆汤',cat:'甜品/糖水'},{id:20,name:'煎饺',cat:'主食/面点'}
];
var overlay=document.getElementById('dishOverlay');
var listEl=document.getElementById('dishList');
var searchInput=document.getElementById('dishSearch');
var countEl=document.getElementById('dishCount');
var confirmBtn=document.getElementById('dishConfirm');
var cancelBtn=document.getElementById('dishCancel');
var closeBtn=document.getElementById('dishClose');
var currentCell=null; // 当前操作的单元格
var tempSelected=[]; // 弹窗内临时选中的id列表
// 每个单元格存储已选餐品: cell.__dishes = [{id,name}]
function getCellDishes(cell){return cell.__dishes||(cell.__dishes=[]);}
function openModal(cell){
currentCell=cell;
tempSelected=getCellDishes(cell).map(function(d){return d.id;});
searchInput.value='';
renderList('');
updateCount();
overlay.classList.add('show');
}
function closeModal(){
overlay.classList.remove('show');
currentCell=null;
}
function renderList(keyword){
var kw=keyword.trim().toLowerCase();
var html='';
DISHES.forEach(function(d){
if(kw&&d.name.toLowerCase().indexOf(kw)===-1) return;
var sel=tempSelected.indexOf(d.id)!==-1;
html+='<div class="dish-item'+(sel?' selected':'')+'" data-id="'+d.id+'">'
+'<span class="dish-item__check"></span>'
+'<span class="dish-item__info"><span class="dish-item__name">'+d.name+'</span><span class="dish-item__cat">'+d.cat+'</span></span>'
+'</div>';
});
if(!html) html='<div style="padding:24px;text-align:center;color:#999;font-size:13px;">无匹配餐品</div>';
listEl.innerHTML=html;
}
function updateCount(){
countEl.textContent='已选 '+tempSelected.length+' 项';
}
// 点击列表项切换选中
listEl.addEventListener('click',function(e){
var item=e.target.closest('.dish-item');
if(!item) return;
var id=parseInt(item.getAttribute('data-id'));
var idx=tempSelected.indexOf(id);
if(idx===-1){tempSelected.push(id);}else{tempSelected.splice(idx,1);}
item.classList.toggle('selected');
updateCount();
});
// 搜索
searchInput.addEventListener('input',function(){renderList(this.value);});
// 确认
confirmBtn.addEventListener('click',function(){
if(!currentCell) return;
var dishes=[];
tempSelected.forEach(function(id){
var d=DISHES.find(function(x){return x.id===id;});
if(d) dishes.push({id:d.id,name:d.name});
});
currentCell.__dishes=dishes;
renderCellDishes(currentCell);
closeModal();
});
// 渲染单元格内餐品标签
function renderCellDishes(cell){
// 移除旧内容(保留 cell-hint
var old=cell.querySelector('.cell-dishes');
if(old) old.remove();
var hint=cell.querySelector('.cell-hint');
var dishes=getCellDishes(cell);
if(!dishes.length){if(hint)hint.style.display='';return;}
if(hint) hint.style.display='none';
var wrap=document.createElement('div');
wrap.className='cell-dishes';
dishes.forEach(function(d){
var tag=document.createElement('span');
tag.className='cell-dish-tag';
tag.innerHTML=d.name+'<button class="cell-dish-tag__del" data-id="'+d.id+'">&times;</button>';
wrap.appendChild(tag);
});
cell.appendChild(wrap);
}
// 单元格内删除餐品
document.querySelector('.week-grid').addEventListener('click',function(e){
var delBtn=e.target.closest('.cell-dish-tag__del');
if(delBtn){
e.stopPropagation();
var cell=delBtn.closest('.meal-cell');
var id=parseInt(delBtn.getAttribute('data-id'));
var dishes=getCellDishes(cell);
cell.__dishes=dishes.filter(function(d){return d.id!==id;});
renderCellDishes(cell);
return;
}
// 点击单元格打开弹窗
var cell=e.target.closest('.meal-cell');
if(cell) openModal(cell);
});
// 关闭弹窗
cancelBtn.addEventListener('click',closeModal);
closeBtn.addEventListener('click',closeModal);
overlay.addEventListener('click',function(e){if(e.target===overlay)closeModal();});
// ===== 预填示例数据 =====
var rows=document.querySelectorAll('.week-grid tbody tr');
var presetData=[
// 早餐 (row 0): 周一~周五
[[{id:1,name:'白米饭'},{id:5,name:'小米粥'},{id:3,name:'馒头'}],
[{id:6,name:'皮蛋瘦肉粥'},{id:4,name:'花卷'}],
[{id:5,name:'小米粥'},{id:20,name:'煎饺'}],
[{id:2,name:'杂粮饭'},{id:3,name:'馒头'}],
[{id:6,name:'皮蛋瘦肉粥'},{id:20,name:'煎饺'},{id:4,name:'花卷'}],
[],[]],
// 午餐 (row 1): 周一~周五
[[{id:7,name:'红烧肉'},{id:12,name:'清炒时蔬'},{id:16,name:'紫菜蛋花汤'}],
[{id:10,name:'宫保鸡丁'},{id:15,name:'酸辣土豆丝'},{id:1,name:'白米饭'}],
[{id:8,name:'糖醋排骨'},{id:13,name:'麻婆豆腐'},{id:17,name:'冬瓜排骨汤'}],
[{id:9,name:'清蒸鲈鱼'},{id:11,name:'西红柿炒蛋'},{id:1,name:'白米饭'}],
[{id:7,name:'红烧肉'},{id:14,name:'凉拌黄瓜'},{id:16,name:'紫菜蛋花汤'}],
[],[]],
// 晚餐 (row 2): 周一~周四
[[{id:11,name:'西红柿炒蛋'},{id:15,name:'酸辣土豆丝'}],
[{id:13,name:'麻婆豆腐'},{id:12,name:'清炒时蔬'}],
[{id:10,name:'宫保鸡丁'},{id:14,name:'凉拌黄瓜'}],
[{id:9,name:'清蒸鲈鱼'},{id:12,name:'清炒时蔬'}],
[],[],[]],
// 加餐 (row 3): 周一、周三
[[{id:18,name:'水果拼盘'}],[],[{id:19,name:'绿豆汤'},{id:18,name:'水果拼盘'}],[],[],[],[]]
];
rows.forEach(function(tr,rowIdx){
if(!presetData[rowIdx]) return;
var cells=tr.querySelectorAll('td.meal-cell');
cells.forEach(function(cell,colIdx){
var data=presetData[rowIdx][colIdx];
if(data&&data.length){
cell.__dishes=data.slice();
renderCellDishes(cell);
}
});
});
// 重置本周:恢复初始数据
function resetWeek(){
rows.forEach(function(tr,rowIdx){
if(!presetData[rowIdx]) return;
var cells=tr.querySelectorAll('td.meal-cell');
cells.forEach(function(cell,colIdx){
var data=presetData[rowIdx][colIdx];
cell.__dishes=data&&data.length?data.slice():[];
renderCellDishes(cell);
});
});
}
document.getElementById('btnResetWeek').addEventListener('click',resetWeek);
})();
/* ===== 复用规划食谱弹窗逻辑 ===== */
(function(){
var RECIPES=[
{id:1,name:'第21周标准营养食谱',population:['普通成人'],rating:'优',
meals:[
[['白米饭','小米粥','馒头'],['皮蛋瘦肉粥','花卷'],['小米粥','煎饺'],['杂粮饭','馒头'],['皮蛋瘦肉粥','煎饺','花卷'],[],[]],
[['红烧肉','清炒时蔬','紫菜蛋花汤'],['宫保鸡丁','酸辣土豆丝','白米饭'],['糖醋排骨','麻婆豆腐','冬瓜排骨汤'],['清蒸鲈鱼','西红柿炒蛋','白米饭'],['红烧肉','凉拌黄瓜','紫菜蛋花汤'],[],[]],
[['西红柿炒蛋','酸辣土豆丝'],['麻婆豆腐','清炒时蔬'],['宫保鸡丁','凉拌黄瓜'],['清蒸鲈鱼','清炒时蔬'],[],[],[]],
[['水果拼盘'],[],['绿豆汤','水果拼盘'],[],[],[],[]]
]},
{id:2,name:'第21周老年营养食谱',population:['老年人'],rating:'良',
meals:[
[['小米粥','馒头'],['燕麦粥','花卷'],['红薯粥','馒头'],['小米粥','花卷'],['燕麦粥','馒头'],[],[]],
[['清蒸鲈鱼','清炒时蔬','紫菜蛋花汤'],['番茄炖牛腩','凉拌黄瓜','白米饭'],['清蒸鸡胸','麻婆豆腐','冬瓜排骨汤'],['红烧肉','清炒时蔬','白米饭'],['清蒸鲈鱼','酸辣土豆丝','紫菜蛋花汤'],[],[]],
[['西红柿炒蛋','清炒时蔬'],['麻婆豆腐','凉拌黄瓜'],['清蒸鲈鱼','清炒时蔬'],['西红柿炒蛋','酸辣土豆丝'],[],[],[]],
[['水果拼盘'],[],['绿豆汤'],[],[],[],[]]
]},
{id:3,name:'第21周糖尿病专用食谱',population:['糖尿病人群'],rating:'优',
meals:[
[['杂粮饭','煮鸡蛋'],['燕麦粥','全麦馒头'],['杂粮饭','煮鸡蛋'],['燕麦粥','全麦馒头'],['杂粮饭','煮鸡蛋'],[],[]],
[['清蒸鸡胸','清炒西兰花','紫菜蛋花汤'],['清蒸鲈鱼','凉拌黄瓜','杂粮饭'],['番茄炖牛腩','清炒时蔬','冬瓜排骨汤'],['清蒸鸡胸','麻婆豆腐','杂粮饭'],['清蒸鲈鱼','清炒西兰花','紫菜蛋花汤'],[],[]],
[['清炒时蔬','凉拌黄瓜'],['麻婆豆腐','清炒西兰花'],['清蒸鲈鱼','凉拌黄瓜'],['清炒时蔬','酸辣土豆丝'],[],[],[]],
[[],[],[],[],[],[],[]]
]},
{id:4,name:'第20周标准营养食谱',population:['普通成人','高血压人群'],rating:'良',
meals:[
[['白米饭','小米粥'],['皮蛋瘦肉粥','馒头'],['小米粥','花卷'],['杂粮饭','馒头'],['皮蛋瘦肉粥','煎饺'],[],[]],
[['红烧肉','清炒时蔬','紫菜蛋花汤'],['宫保鸡丁','凉拌黄瓜','白米饭'],['糖醋排骨','清炒时蔬','冬瓜排骨汤'],['清蒸鲈鱼','西红柿炒蛋','白米饭'],['红烧肉','酸辣土豆丝','紫菜蛋花汤'],[],[]],
[['西红柿炒蛋','清炒时蔬'],['麻婆豆腐','凉拌黄瓜'],['宫保鸡丁','酸辣土豆丝'],['清蒸鲈鱼','清炒时蔬'],[],[],[]],
[['水果拼盘'],[],[],['绿豆汤'],[],[],[]]
]},
{id:5,name:'第20周儿童营养食谱',population:['儿童'],rating:'合格',
meals:[
[['小米粥','馒头','煮鸡蛋'],['牛奶','花卷'],['小米粥','煎饺'],['牛奶','馒头'],['小米粥','花卷','煮鸡蛋'],[],[]],
[['番茄炖牛腩','清炒时蔬','紫菜蛋花汤'],['宫保鸡丁','西红柿炒蛋','白米饭'],['糖醋排骨','清炒时蔬','冬瓜排骨汤'],['清蒸鲈鱼','西红柿炒蛋','白米饭'],['红烧肉','清炒时蔬','紫菜蛋花汤'],[],[]],
[['西红柿炒蛋','清炒时蔬'],['麻婆豆腐','凉拌黄瓜'],['宫保鸡丁','清炒时蔬'],['清蒸鲈鱼','凉拌黄瓜'],[],[],[]],
[['水果拼盘','牛奶'],['水果拼盘'],['绿豆汤','水果拼盘'],['水果拼盘'],[],[],[]]
]}
];
var overlay=document.getElementById('reuseOverlay');
var listEl=document.getElementById('reuseList');
var previewEl=document.getElementById('reusePreview');
var searchInput=document.getElementById('reuseSearch');
var selectedId=null;
function renderList(keyword){
var kw=(keyword||'').trim().toLowerCase();
var html='';
RECIPES.forEach(function(r){
if(kw&&r.name.toLowerCase().indexOf(kw)===-1) return;
var sel=r.id===selectedId;
html+='<div class="reuse-recipe-item'+(sel?' selected':'')+'" data-id="'+r.id+'">'
+'<span class="reuse-recipe-item__radio"></span>'
+'<span class="reuse-recipe-item__info">'
+'<span class="reuse-recipe-item__name">'+r.name+'</span>'
+'<span class="reuse-recipe-item__meta">';
r.population.forEach(function(p){html+='<span class="tag tag-blue">'+p+'</span>';});
var ratingClass=r.rating==='优'?'tag-green':r.rating==='良'?'tag-blue':'tag-orange';
html+='<span class="tag '+ratingClass+'">'+r.rating+'</span>';
html+='</span></span></div>';
});
if(!html) html='<div style="padding:24px;text-align:center;color:#999;font-size:13px;">无匹配食谱</div>';
listEl.innerHTML=html;
}
function renderPreview(recipe){
if(!recipe){
previewEl.innerHTML='<div class="reuse-modal__empty">请从左侧选择一个食谱查看详情</div>';
return;
}
var mealNames=['早餐','午餐','晚餐','加餐'];
var dayNames=['周一','周二','周三','周四','周五','周六','周日'];
var html='<div class="week-grid"><table><thead><tr><th style="width:50px;"></th>';
dayNames.forEach(function(d){html+='<th><div class="day-date">'+d+'</div></th>';});
html+='</tr></thead><tbody>';
mealNames.forEach(function(meal,mi){
html+='<tr><td class="meal-label">'+meal+'</td>';
for(var di=0;di<7;di++){
var dishes=recipe.meals[mi]&&recipe.meals[mi][di]||[];
html+='<td class="meal-cell"><div class="cell-dishes">';
dishes.forEach(function(d){html+='<span class="cell-dish-tag">'+d+'</span>';});
html+='</div></td>';
}
html+='</tr>';
});
html+='</tbody></table></div>';
previewEl.innerHTML=html;
}
function openReuse(){
selectedId=null;
searchInput.value='';
renderList('');
renderPreview(null);
overlay.classList.add('show');
}
function closeReuse(){overlay.classList.remove('show');}
document.getElementById('btnReuseRecipe').addEventListener('click',openReuse);
document.getElementById('reuseClose').addEventListener('click',closeReuse);
document.getElementById('reuseCancel').addEventListener('click',closeReuse);
overlay.addEventListener('click',function(e){if(e.target===overlay)closeReuse();});
listEl.addEventListener('click',function(e){
var item=e.target.closest('.reuse-recipe-item');
if(!item) return;
var id=parseInt(item.getAttribute('data-id'));
selectedId=id;
renderList(searchInput.value);
var recipe=RECIPES.find(function(r){return r.id===id;});
renderPreview(recipe);
});
searchInput.addEventListener('input',function(){renderList(this.value);});
document.getElementById('reuseConfirm').addEventListener('click',function(){
if(!selectedId){alert('请先选择一个食谱');return;}
closeReuse();
});
})();
</script>
</body>
</html>
@@ -0,0 +1,410 @@
<!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;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 页面头部 */
.page-header{display:flex;align-items:center;gap:12px;margin-bottom:24px;}
.page-header__back{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--text-secondary);text-decoration:none;transition:all .15s;}
.page-header__back:hover{border-color:var(--primary);color:var(--primary);}
.page-header__title{font-size:var(--font-xl);font-weight:600;color:var(--text-primary);}
/* 表单模块 */
.form-section{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);margin-bottom:20px;}
.form-section__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);padding:16px 24px;border-bottom:1px solid var(--border);background:#FAFAFA;border-radius:var(--radius-md) var(--radius-md) 0 0;}
.form-section__body{padding:24px;}
.form-row{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-bottom:16px;}
.form-row:last-child{margin-bottom:0;}
.form-group{display:flex;flex-direction:column;gap:6px;}
.form-group label{font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;}
.form-group .required{color:var(--danger);margin-left:2px;}
.form-input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
.form-input:focus{border-color:var(--primary);}
.form-select{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);outline:none;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;}
/* 多选标签输入 */
.multi-select{position:relative;}
.multi-select__trigger{min-height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:4px 28px 4px 8px;font-size:var(--font-sm);cursor:pointer;display:flex;flex-wrap:wrap;gap:4px;align-items:center;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;}
.multi-select__trigger:hover{border-color:var(--primary);}
.multi-select__tag{display:inline-flex;align-items:center;gap:3px;padding:2px 8px;background:var(--primary-light);border-radius:10px;font-size:var(--font-xs);color:var(--primary);}
.multi-select__tag-del{cursor:pointer;font-size:12px;opacity:0.7;}.multi-select__tag-del:hover{opacity:1;}
.multi-select__placeholder{color:var(--text-placeholder);font-size:var(--font-sm);}
.multi-select__dropdown{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:50;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);display:none;max-height:200px;overflow-y:auto;}
.multi-select__dropdown.show{display:block;}
.multi-select__option{padding:8px 12px;font-size:var(--font-sm);cursor:pointer;transition:background .1s;}
.multi-select__option:hover{background:#F8FBFF;}
.multi-select__option.selected{background:var(--primary-light);color:var(--primary);}
/* 周食谱网格 */
.week-grid{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;border:1px solid var(--border);}
.week-grid table{width:100%;border-collapse:collapse;table-layout:fixed;}
.week-grid thead th{background:#FAFAFA;padding:12px 8px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border:1px solid var(--border);}
.week-grid thead th .day-date{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.week-grid thead th .day-name{font-size:var(--font-xs);color:var(--text-secondary);margin-top:2px;}
.week-grid tbody td{border:1px solid var(--border);padding:0;vertical-align:top;height:140px;}
.week-grid tbody td.meal-label{width:64px;background:#FAFAFA;text-align:center;vertical-align:middle;font-size:var(--font-sm);font-weight:600;color:#E8740C;padding:12px 8px;white-space:nowrap;}
.week-grid tbody td.meal-cell{cursor:pointer;transition:background .15s;position:relative;}
.week-grid tbody td.meal-cell:hover{background:#FAFBFF;}
.week-grid tbody td.meal-cell .cell-hint{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#ccc;font-size:24px;display:none;pointer-events:none;}
.week-grid tbody td.meal-cell:hover .cell-hint{display:block;}
/* 单元格内餐品标签 */
.cell-dishes{display:flex;flex-wrap:wrap;gap:4px;padding:6px 8px;}
.cell-dish-tag{display:inline-flex;align-items:center;gap:3px;padding:2px 8px;background:#F0F5FF;border:1px solid #D6E4FF;border-radius:10px;font-size:var(--font-xs);color:var(--primary);max-width:100%;white-space:nowrap;}
.cell-dish-tag__del{width:14px;height:14px;border-radius:50%;border:none;background:none;cursor:pointer;font-size:12px;color:var(--text-placeholder);display:inline-flex;align-items:center;justify-content:center;line-height:1;transition:all .1s;}
.cell-dish-tag__del:hover{color:var(--danger);background:#FFF1F0;}
/* 餐品选择弹窗 */
.dish-modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;display:none;align-items:center;justify-content:center;}
.dish-modal-overlay.show{display:flex;}
.dish-modal{background:#fff;border-radius:var(--radius-lg);width:540px;max-height:70vh;display:flex;flex-direction:column;box-shadow:0 8px 24px rgba(0,0,0,0.18);}
.dish-modal__header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);}
.dish-modal__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.dish-modal__close{width:28px;height:28px;border:none;background:none;cursor:pointer;font-size:18px;color:var(--text-secondary);border-radius:var(--radius-sm);display:flex;align-items:center;justify-content:center;}
.dish-modal__close:hover{background:#F0F0F0;color:var(--text-primary);}
.dish-modal__search{padding:12px 20px;border-bottom:1px solid var(--border);}
.dish-modal__search input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px 0 32px;font-size:var(--font-sm);outline:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='none'%3E%3Ccircle cx='6' cy='6' r='4.5' stroke='%23999' stroke-width='1.2'/%3E%3Cpath d='M9.5 9.5L12.5 12.5' stroke='%23999' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat 10px center;}
.dish-modal__search input:focus{border-color:var(--primary);}
.dish-modal__list{flex:1;overflow-y:auto;padding:8px 0;}
.dish-item{display:flex;align-items:center;gap:10px;padding:10px 20px;cursor:pointer;transition:background .1s;}
.dish-item:hover{background:#F8FBFF;}
.dish-item.selected{background:var(--primary-light);}
.dish-item__check{width:18px;height:18px;border:1.5px solid var(--border);border-radius:3px;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:all .15s;}
.dish-item.selected .dish-item__check{background:var(--primary);border-color:var(--primary);}
.dish-item.selected .dish-item__check::after{content:'';display:block;width:5px;height:9px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg) translate(-1px,-1px);}
.dish-item__info{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;}
.dish-item__name{font-size:var(--font-sm);color:var(--text-primary);}
.dish-item__cat{font-size:var(--font-xs);color:var(--text-placeholder);}
.dish-item__nutri{display:flex;gap:8px;margin-top:2px;}
.dish-item__nutri span{font-size:var(--font-xs);color:var(--text-secondary);white-space:nowrap;}
.dish-item__nutri span em{font-style:normal;color:var(--text-primary);font-weight:500;}
.dish-modal__footer{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-top:1px solid var(--border);}
.dish-modal__count{font-size:var(--font-sm);color:var(--text-secondary);}
.dish-modal__actions{display:flex;gap:8px;}
/* 按钮 */
.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);}
/* 底部操作 */
.page-footer{display:flex;justify-content:flex-end;gap:12px;margin-top:24px;}
.btn-lg{height:40px;padding:0 24px;font-size:var(--font-base);font-weight:500;}
</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" class="active">健康生产</a>
<a href="din-dict-mgmt.html">营养用餐</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="prod-qc-daily.html" class="sidebar-item"><span class="mi">🛡️</span>食安监控</a>
<a href="prod-recipe-plan.html" class="sidebar-item active"><span class="mi">📋</span>食谱规划</a>
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
<a href="prod-proc-stats.html" class="sidebar-item"><span class="mi">📊</span>生产统计</a>
</aside>
<!-- PLACEHOLDER_MAIN -->
<main class="main">
<!-- 页面头部 -->
<div class="page-header">
<a href="prod-recipe-plan.html" class="page-header__back"></a>
<h1 class="page-header__title">新建食谱规划</h1>
</div>
<!-- 模块一:基本信息 -->
<div class="form-section">
<div class="form-section__title">基本信息</div>
<div class="form-section__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>
<div class="multi-select" id="msPopulation">
<div class="multi-select__trigger" id="msTrigger">
<span class="multi-select__placeholder">请选择适应人群</span>
</div>
<div class="multi-select__dropdown" id="msDropdown">
<div class="multi-select__option" data-value="普通成人">普通成人</div>
<div class="multi-select__option" data-value="老年人">老年人</div>
<div class="multi-select__option" data-value="糖尿病人群">糖尿病人群</div>
<div class="multi-select__option" data-value="高血压人群">高血压人群</div>
<div class="multi-select__option" data-value="儿童">儿童</div>
<div class="multi-select__option" data-value="孕产妇">孕产妇</div>
</div>
</div>
</div>
<div class="form-group">
<label>营养达标评级</label>
<select class="form-select">
<option value="">请选择</option>
<option></option>
<option></option>
<option>合格</option>
<option>不合格</option>
</select>
</div>
</div>
</div>
</div>
<!-- 模块二:一周菜谱 -->
<div class="form-section">
<div class="form-section__title">一周菜谱</div>
<div class="form-section__body" style="padding:0;">
<div class="week-grid" style="border:none;box-shadow:none;border-radius:0;">
<table>
<thead>
<tr>
<th style="width:64px;"></th>
<th><div class="day-date">周一</div></th>
<th><div class="day-date">周二</div></th>
<th><div class="day-date">周三</div></th>
<th><div class="day-date">周四</div></th>
<th><div class="day-date">周五</div></th>
<th><div class="day-date">周六</div></th>
<th><div class="day-date">周日</div></th>
</tr>
</thead>
<tbody>
<tr><td class="meal-label">早餐</td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td></tr>
<tr><td class="meal-label">午餐</td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td></tr>
<tr><td class="meal-label">晚餐</td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td></tr>
<tr><td class="meal-label">加餐</td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td><td class="meal-cell"><span class="cell-hint">+</span></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 底部操作 -->
<div class="page-footer">
<a href="prod-recipe-plan.html" class="btn btn-default btn-lg">取消</a>
<button class="btn btn-primary btn-lg" style="background:#E8740C;">提交审核</button>
</div>
</main>
<!-- 餐品选择弹窗 -->
<div class="dish-modal-overlay" id="dishOverlay">
<div class="dish-modal">
<div class="dish-modal__header">
<span class="dish-modal__title">选择餐品</span>
<button class="dish-modal__close" id="dishClose">&times;</button>
</div>
<div class="dish-modal__search">
<input type="text" id="dishSearch" placeholder="搜索餐品名称...">
</div>
<div class="dish-modal__list" id="dishList"></div>
<div class="dish-modal__footer">
<span class="dish-modal__count" id="dishCount">已选 0 项</span>
<div class="dish-modal__actions">
<button class="btn btn-default" id="dishCancel">取消</button>
<button class="btn btn-primary" id="dishConfirm">确认</button>
</div>
</div>
</div>
</div>
<script>
/* ===== 多选下拉 ===== */
(function(){
var trigger=document.getElementById('msTrigger');
var dropdown=document.getElementById('msDropdown');
var selected=[];
function render(){
if(!selected.length){
trigger.innerHTML='<span class="multi-select__placeholder">请选择适应人群</span>';
}else{
trigger.innerHTML=selected.map(function(v){
return '<span class="multi-select__tag">'+v+'<span class="multi-select__tag-del" data-val="'+v+'">&times;</span></span>';
}).join('');
}
dropdown.querySelectorAll('.multi-select__option').forEach(function(opt){
opt.classList.toggle('selected',selected.indexOf(opt.dataset.value)!==-1);
});
}
trigger.addEventListener('click',function(e){
if(e.target.classList.contains('multi-select__tag-del')){
selected=selected.filter(function(v){return v!==e.target.dataset.val;});
render();return;
}
dropdown.classList.toggle('show');
});
dropdown.addEventListener('click',function(e){
var opt=e.target.closest('.multi-select__option');
if(!opt) return;
var val=opt.dataset.value;
var idx=selected.indexOf(val);
if(idx===-1) selected.push(val); else selected.splice(idx,1);
render();
});
document.addEventListener('click',function(e){
if(!document.getElementById('msPopulation').contains(e.target)) dropdown.classList.remove('show');
});
})();
/* ===== 餐品选择弹窗 ===== */
(function(){
var DISHES=[
{id:1,name:'白米饭',cat:'主食/米饭',kcal:116,protein:2.6,carb:25.9,fat:0.3},
{id:2,name:'杂粮饭',cat:'主食/米饭',kcal:135,protein:4.1,carb:28.2,fat:1.0},
{id:3,name:'馒头',cat:'主食/面点',kcal:221,protein:7.0,carb:44.2,fat:1.1},
{id:4,name:'花卷',cat:'主食/面点',kcal:214,protein:6.4,carb:42.8,fat:1.3},
{id:5,name:'小米粥',cat:'主食/粥品',kcal:46,protein:1.4,carb:8.4,fat:0.7},
{id:6,name:'皮蛋瘦肉粥',cat:'主食/粥品',kcal:89,protein:5.2,carb:11.6,fat:2.4},
{id:7,name:'红烧肉',cat:'荤菜/猪肉',kcal:286,protein:14.2,carb:6.8,fat:22.5},
{id:8,name:'糖醋排骨',cat:'荤菜/猪肉',kcal:264,protein:16.8,carb:14.2,fat:15.6},
{id:9,name:'清蒸鲈鱼',cat:'荤菜/水产',kcal:105,protein:18.6,carb:0.8,fat:3.2},
{id:10,name:'宫保鸡丁',cat:'荤菜/禽类',kcal:198,protein:19.4,carb:8.6,fat:10.2},
{id:11,name:'西红柿炒蛋',cat:'素菜/蛋类',kcal:126,protein:7.8,carb:5.4,fat:8.2},
{id:12,name:'清炒时蔬',cat:'素菜/叶菜',kcal:62,protein:2.8,carb:4.6,fat:3.8},
{id:13,name:'麻婆豆腐',cat:'素菜/豆制品',kcal:142,protein:9.6,carb:5.2,fat:9.4},
{id:14,name:'凉拌黄瓜',cat:'凉菜/蔬菜',kcal:32,protein:1.2,carb:3.8,fat:1.4},
{id:15,name:'酸辣土豆丝',cat:'素菜/根茎',kcal:96,protein:2.4,carb:16.2,fat:3.0},
{id:16,name:'紫菜蛋花汤',cat:'汤品/蛋汤',kcal:38,protein:3.2,carb:2.4,fat:1.8},
{id:17,name:'冬瓜排骨汤',cat:'汤品/肉汤',kcal:72,protein:5.6,carb:3.2,fat:4.0},
{id:18,name:'水果拼盘',cat:'甜品/水果',kcal:56,protein:0.8,carb:13.2,fat:0.2},
{id:19,name:'绿豆汤',cat:'甜品/糖水',kcal:68,protein:2.4,carb:14.8,fat:0.3},
{id:20,name:'煎饺',cat:'主食/面点',kcal:198,protein:8.6,carb:22.4,fat:8.2}
];
var overlay=document.getElementById('dishOverlay');
var listEl=document.getElementById('dishList');
var searchInput=document.getElementById('dishSearch');
var countEl=document.getElementById('dishCount');
var confirmBtn=document.getElementById('dishConfirm');
var cancelBtn=document.getElementById('dishCancel');
var closeBtn=document.getElementById('dishClose');
var currentCell=null,tempSelected=[];
function getCellDishes(cell){return cell.__dishes||(cell.__dishes=[]);}
function openModal(cell){
currentCell=cell;
tempSelected=getCellDishes(cell).map(function(d){return d.id;});
searchInput.value='';
renderList('');
updateCount();
overlay.classList.add('show');
}
function closeModal(){overlay.classList.remove('show');currentCell=null;}
function renderList(keyword){
var kw=keyword.trim().toLowerCase();
var html='';
DISHES.forEach(function(d){
if(kw&&d.name.toLowerCase().indexOf(kw)===-1) return;
var sel=tempSelected.indexOf(d.id)!==-1;
html+='<div class="dish-item'+(sel?' selected':'')+'" data-id="'+d.id+'"><span class="dish-item__check"></span><span class="dish-item__info"><span class="dish-item__name">'+d.name+'</span><span class="dish-item__cat">'+d.cat+'</span><div class="dish-item__nutri"><span>热量 <em>'+d.kcal+'</em>kcal</span><span>蛋白质 <em>'+d.protein+'</em>g</span><span>碳水 <em>'+d.carb+'</em>g</span><span>脂肪 <em>'+d.fat+'</em>g</span></div></span></div>';
});
if(!html) html='<div style="padding:24px;text-align:center;color:#999;font-size:13px;">无匹配餐品</div>';
listEl.innerHTML=html;
}
function updateCount(){countEl.textContent='已选 '+tempSelected.length+' 项';}
listEl.addEventListener('click',function(e){
var item=e.target.closest('.dish-item');if(!item) return;
var id=parseInt(item.getAttribute('data-id'));
var idx=tempSelected.indexOf(id);
if(idx===-1) tempSelected.push(id); else tempSelected.splice(idx,1);
item.classList.toggle('selected');updateCount();
});
searchInput.addEventListener('input',function(){renderList(this.value);});
confirmBtn.addEventListener('click',function(){
if(!currentCell) return;
var dishes=[];
tempSelected.forEach(function(id){var d=DISHES.find(function(x){return x.id===id;});if(d) dishes.push({id:d.id,name:d.name});});
currentCell.__dishes=dishes;
renderCellDishes(currentCell);
closeModal();
});
function renderCellDishes(cell){
var old=cell.querySelector('.cell-dishes');if(old) old.remove();
var hint=cell.querySelector('.cell-hint');
var dishes=getCellDishes(cell);
if(!dishes.length){if(hint)hint.style.display='';return;}
if(hint) hint.style.display='none';
var wrap=document.createElement('div');wrap.className='cell-dishes';
dishes.forEach(function(d){
var tag=document.createElement('span');tag.className='cell-dish-tag';
tag.innerHTML=d.name+'<button class="cell-dish-tag__del" data-id="'+d.id+'">&times;</button>';
wrap.appendChild(tag);
});
cell.appendChild(wrap);
}
document.querySelector('.week-grid').addEventListener('click',function(e){
var delBtn=e.target.closest('.cell-dish-tag__del');
if(delBtn){e.stopPropagation();var cell=delBtn.closest('.meal-cell');var id=parseInt(delBtn.getAttribute('data-id'));cell.__dishes=getCellDishes(cell).filter(function(d){return d.id!==id;});renderCellDishes(cell);return;}
var cell=e.target.closest('.meal-cell');if(cell) openModal(cell);
});
cancelBtn.addEventListener('click',closeModal);
closeBtn.addEventListener('click',closeModal);
overlay.addEventListener('click',function(e){if(e.target===overlay)closeModal();});
})();
</script>
</body>
</html>
@@ -0,0 +1,250 @@
<!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;}
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
/* 页面头部 */
.page-header{display:flex;align-items:center;gap:12px;margin-bottom:24px;}
.page-header__back{width:32px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--text-secondary);text-decoration:none;transition:all .15s;}
.page-header__back:hover{border-color:var(--primary);color:var(--primary);}
.page-header__title{font-size:var(--font-xl);font-weight:600;color:var(--text-primary);}
.page-header__status{margin-left:8px;}
/* 表单模块 */
.form-section{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);margin-bottom:20px;}
.form-section__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);padding:16px 24px;border-bottom:1px solid var(--border);background:#FAFAFA;border-radius:var(--radius-md) var(--radius-md) 0 0;}
.form-section__body{padding:24px;}
.detail-row{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-bottom:20px;}
.detail-row:last-child{margin-bottom:0;}
.detail-field{display:flex;flex-direction:column;gap:4px;}
.detail-field__label{font-size:var(--font-sm);color:var(--text-placeholder);}
.detail-field__value{font-size:var(--font-sm);color:var(--text-primary);font-weight:500;min-height:22px;}
.tag{display:inline-flex;align-items:center;padding:2px 8px;border-radius:10px;font-size:var(--font-xs);}
.tag-blue{background:var(--primary-light);color:var(--primary);}
.tag-green{background:#F6FFED;color:var(--success);}
.tag-orange{background:#FFF7E6;color:var(--warning);}
/* 周食谱网格(只读) */
.week-grid{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);overflow:hidden;border:1px solid var(--border);}
.week-grid table{width:100%;border-collapse:collapse;table-layout:fixed;}
.week-grid thead th{background:#FAFAFA;padding:12px 8px;text-align:center;font-size:var(--font-sm);color:var(--text-secondary);font-weight:500;border:1px solid var(--border);}
.week-grid thead th .day-date{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
.week-grid tbody td{border:1px solid var(--border);padding:0;vertical-align:top;height:140px;}
.week-grid tbody td.meal-label{width:64px;background:#FAFAFA;text-align:center;vertical-align:middle;font-size:var(--font-sm);font-weight:600;color:#E8740C;padding:12px 8px;white-space:nowrap;}
.week-grid tbody td.meal-cell{position:relative;}
/* 单元格内餐品标签(只读,无删除按钮) */
.cell-dishes{display:flex;flex-wrap:wrap;gap:4px;padding:6px 8px;}
.cell-dish-tag{display:inline-flex;align-items:center;padding:2px 8px;background:#F0F5FF;border:1px solid #D6E4FF;border-radius:10px;font-size:var(--font-xs);color:var(--primary);white-space:nowrap;}
/* 按钮 */
.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);}
/* 底部操作 */
.page-footer{display:flex;justify-content:flex-end;gap:12px;margin-top:24px;}
.btn-lg{height:40px;padding:0 24px;font-size:var(--font-base);font-weight:500;}
</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" class="active">健康生产</a>
<a href="din-dict-mgmt.html">营养用餐</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="prod-qc-daily.html" class="sidebar-item"><span class="mi">🛡️</span>食安监控</a>
<a href="prod-recipe-plan.html" class="sidebar-item active"><span class="mi">📋</span>食谱规划</a>
<a href="prod-clean-order.html" class="sidebar-item"><span class="mi">📝</span>净菜订单</a>
<a href="prod-proc-cabinet.html" class="sidebar-item"><span class="mi">🗄️</span>净菜进柜</a>
<a href="prod-proc-combo.html" class="sidebar-item"><span class="mi">🔪</span>净菜组配</a>
<a href="prod-proc-cook.html" class="sidebar-item"><span class="mi">🍳</span>按单烹制</a>
<a href="prod-proc-serve.html" class="sidebar-item"><span class="mi">🍽️</span>上餐监控</a>
<a href="prod-proc-surplus.html" class="sidebar-item"><span class="mi">♻️</span>余量处置</a>
<a href="prod-proc-stats.html" class="sidebar-item"><span class="mi">📊</span>生产统计</a>
</aside>
<main class="main">
<!-- 页面头部 -->
<div class="page-header">
<a href="prod-recipe-plan.html" class="page-header__back"></a>
<h1 class="page-header__title">食谱规划详情</h1>
<span class="page-header__status"><span class="tag tag-green">已通过</span></span>
</div>
<!-- 模块一:基本信息 -->
<div class="form-section">
<div class="form-section__title">基本信息</div>
<div class="form-section__body">
<div class="detail-row">
<div class="detail-field">
<span class="detail-field__label">餐谱名称</span>
<span class="detail-field__value">第21周标准营养食谱</span>
</div>
<div class="detail-field">
<span class="detail-field__label">适应人群</span>
<span class="detail-field__value"><span class="tag tag-blue">普通成人</span></span>
</div>
<div class="detail-field">
<span class="detail-field__label">营养达标评级</span>
<span class="detail-field__value"><span class="tag tag-green"></span></span>
</div>
</div>
<div class="detail-row">
<div class="detail-field">
<span class="detail-field__label">安全评估</span>
<span class="detail-field__value"><span class="tag tag-green">通过</span></span>
</div>
<div class="detail-field">
<span class="detail-field__label">创建人</span>
<span class="detail-field__value">张营养师</span>
</div>
<div class="detail-field">
<span class="detail-field__label">创建时间</span>
<span class="detail-field__value">2026-05-12 09:30</span>
</div>
</div>
<div class="detail-row">
<div class="detail-field">
<span class="detail-field__label">审核人</span>
<span class="detail-field__value">李主任</span>
</div>
<div class="detail-field">
<span class="detail-field__label">审核时间</span>
<span class="detail-field__value">2026-05-12 14:00</span>
</div>
<div class="detail-field"></div>
</div>
</div>
</div>
<!-- 模块二:一周菜谱 -->
<div class="form-section">
<div class="form-section__title">一周菜谱</div>
<div class="form-section__body" style="padding:0;">
<div class="week-grid" style="border:none;box-shadow:none;border-radius:0;">
<table>
<thead>
<tr>
<th style="width:64px;"></th>
<th><div class="day-date">周一</div></th>
<th><div class="day-date">周二</div></th>
<th><div class="day-date">周三</div></th>
<th><div class="day-date">周四</div></th>
<th><div class="day-date">周五</div></th>
<th><div class="day-date">周六</div></th>
<th><div class="day-date">周日</div></th>
</tr>
</thead>
<tbody>
<!-- 早餐 -->
<tr>
<td class="meal-label">早餐</td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">白米饭</span><span class="cell-dish-tag">小米粥</span><span class="cell-dish-tag">馒头</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">皮蛋瘦肉粥</span><span class="cell-dish-tag">花卷</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">小米粥</span><span class="cell-dish-tag">煎饺</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">杂粮饭</span><span class="cell-dish-tag">馒头</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">皮蛋瘦肉粥</span><span class="cell-dish-tag">煎饺</span><span class="cell-dish-tag">花卷</span></div></td>
<td class="meal-cell"></td>
<td class="meal-cell"></td>
</tr>
<!-- 午餐 -->
<tr>
<td class="meal-label">午餐</td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">红烧肉</span><span class="cell-dish-tag">清炒时蔬</span><span class="cell-dish-tag">紫菜蛋花汤</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">宫保鸡丁</span><span class="cell-dish-tag">酸辣土豆丝</span><span class="cell-dish-tag">白米饭</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">糖醋排骨</span><span class="cell-dish-tag">麻婆豆腐</span><span class="cell-dish-tag">冬瓜排骨汤</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">清蒸鲈鱼</span><span class="cell-dish-tag">西红柿炒蛋</span><span class="cell-dish-tag">白米饭</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">红烧肉</span><span class="cell-dish-tag">凉拌黄瓜</span><span class="cell-dish-tag">紫菜蛋花汤</span></div></td>
<td class="meal-cell"></td>
<td class="meal-cell"></td>
</tr>
<!-- 晚餐 -->
<tr>
<td class="meal-label">晚餐</td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">西红柿炒蛋</span><span class="cell-dish-tag">酸辣土豆丝</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">麻婆豆腐</span><span class="cell-dish-tag">清炒时蔬</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">宫保鸡丁</span><span class="cell-dish-tag">凉拌黄瓜</span></div></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">清蒸鲈鱼</span><span class="cell-dish-tag">清炒时蔬</span></div></td>
<td class="meal-cell"></td>
<td class="meal-cell"></td>
<td class="meal-cell"></td>
</tr>
<!-- 加餐 -->
<tr>
<td class="meal-label">加餐</td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">水果拼盘</span></div></td>
<td class="meal-cell"></td>
<td class="meal-cell"><div class="cell-dishes"><span class="cell-dish-tag">绿豆汤</span><span class="cell-dish-tag">水果拼盘</span></div></td>
<td class="meal-cell"></td>
<td class="meal-cell"></td>
<td class="meal-cell"></td>
<td class="meal-cell"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 底部操作 -->
<div class="page-footer">
<a href="prod-recipe-plan.html" class="btn btn-default btn-lg">返回列表</a>
<a href="prod-recipe-plan-create.html" class="btn btn-primary btn-lg">编辑</a>
</div>
</main>
<script>
// 详情页为只读,无交互脚本
</script>
</body>
</html>
+194 -60
View File
@@ -80,7 +80,8 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
/* 页面内弹窗 */
.modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:200;align-items:center;justify-content:center;}
.modal{background:#fff;border-radius:var(--radius-lg);width:680px;max-width:90vw;box-shadow:var(--shadow-md);}
.modal-overlay.show{display:flex;}
.modal{background:#fff;border-radius:var(--radius-lg);width:420px;max-width:90vw;box-shadow:var(--shadow-md);}
.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;color:var(--text-primary);}
.modal__close{width:32px;height:32px;border:none;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);color:var(--text-placeholder);}
@@ -90,12 +91,34 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
.form-group{margin-bottom:20px;}
.form-group label{display:block;font-size:var(--font-sm);color:var(--text-primary);margin-bottom:8px;font-weight:500;}
.form-group .required{color:var(--danger);margin-left:2px;}
.audit-options{display:flex;gap:12px;margin-bottom:16px;}
.audit-opt{flex:1;padding:16px;border:2px solid var(--border);border-radius:var(--radius-md);cursor:pointer;text-align:center;transition:all .15s;}
.audit-opt:hover{border-color:var(--primary);background:#FAFBFF;}
.audit-opt.selected-pass{border-color:var(--success);background:#F6FFED;}
.audit-opt.selected-fail{border-color:var(--danger);background:#FFF1F0;}
.audit-opt__icon{font-size:24px;margin-bottom:6px;}
.audit-opt__label{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
.form-textarea{width:100%;height:80px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 12px;font-size:var(--font-sm);outline:none;resize:vertical;font-family:inherit;}
.form-textarea:focus{border-color:var(--primary);}
.form-input2{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;}
.form-input2:focus{border-color:var(--primary);}
.form-select2{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;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 12px center;}
.form-textarea2{width:100%;height:80px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 12px;font-size:var(--font-sm);outline:none;resize:vertical;font-family:inherit;}
.form-textarea2:focus{border-color:var(--primary);}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;}
/* 多选下拉 */
.multi-select{position:relative;display:inline-block;min-width:160px;}
.multi-select__trigger{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 12px;font-size:var(--font-sm);cursor:pointer;display:flex;align-items:center;gap:4px;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;overflow:hidden;white-space:nowrap;}
.multi-select__trigger:hover{border-color:var(--primary);}
.multi-select__placeholder{color:var(--text-placeholder);font-size:var(--font-sm);}
.multi-select__tag{display:inline-flex;align-items:center;gap:2px;padding:1px 6px;background:var(--primary-light);border-radius:8px;font-size:var(--font-xs);color:var(--primary);flex-shrink:0;}
.multi-select__tag-del{cursor:pointer;font-size:11px;opacity:0.7;line-height:1;}.multi-select__tag-del:hover{opacity:1;}
.multi-select__dropdown{position:absolute;top:calc(100% + 4px);left:0;min-width:100%;z-index:50;background:#fff;border:1px solid var(--border);border-radius:var(--radius-sm);box-shadow:var(--shadow-md);display:none;max-height:200px;overflow-y:auto;}
.multi-select__dropdown.show{display:block;}
.multi-select__option{padding:8px 12px;font-size:var(--font-sm);cursor:pointer;transition:background .1s;white-space:nowrap;}
.multi-select__option:hover{background:#F8FBFF;}
.multi-select__option.selected{background:var(--primary-light);color:var(--primary);}
</style>
</head>
<body>
@@ -142,26 +165,42 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 筛选栏 -->
<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>
<label>菜谱名称</label>
<input class="filter-input" type="text" placeholder="请输入菜谱名称">
</div>
<div class="filter-item">
<label>日期</label>
<input class="filter-input" type="date">
<label>适应人群</label>
<div class="multi-select" id="msPopulation">
<div class="multi-select__trigger" id="msTrigger">
<span class="multi-select__placeholder">全部</span>
</div>
<div class="multi-select__dropdown" id="msDropdown">
<div class="multi-select__option" data-value="普通成人">普通成人</div>
<div class="multi-select__option" data-value="老年人">老年人</div>
<div class="multi-select__option" data-value="糖尿病人群">糖尿病人群</div>
<div class="multi-select__option" data-value="高血压人群">高血压人群</div>
<div class="multi-select__option" data-value="儿童">儿童</div>
<div class="multi-select__option" data-value="孕产妇">孕产妇</div>
</div>
</div>
</div>
<div class="filter-item">
<label>营养达标评级</label>
<select class="filter-select">
<option value="">全部</option>
<option></option>
<option></option>
<option>合格</option>
<option>不合格</option>
</select>
</div>
<div class="filter-item">
<label>安全评估</label>
<select class="filter-select">
<option>全部</option>
<option value="">全部</option>
<option>通过</option>
<option>待复核</option>
<option>不通过</option>
<option>未通过</option>
<option>待审核</option>
</select>
</div>
<button class="btn btn-primary">查询</button>
@@ -170,9 +209,8 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary" onclick="document.getElementById('modal-plan').style.display='flex'"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="#fff" stroke-width="1.8" stroke-linecap="round"/></svg>新建规划</button>
<button class="btn btn-default">🧮 校验净菜需求</button>
<button class="btn btn-default">🤖 AI推荐优化</button>
<a href="prod-recipe-plan-create.html" class="btn btn-primary"><svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M7 2v10M2 7h10" stroke="#fff" stroke-width="1.8" stroke-linecap="round"/></svg>新建规划</a>
<button class="btn btn-default">🤖 AI推荐菜谱</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>
@@ -180,37 +218,37 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
<!-- 数据表格 -->
<div class="table-card">
<div class="table-card__header">
<div class="table-card__title">食谱规划</div>
<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>热量(kcal)</th><th>净菜需求量(kg)</th><th>营养达标评级</th><th>安全评估</th><th>整改状态</th><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>2026-04-15(周三)</td><td>早餐</td><td>第15周营养食谱</td><td>小米粥+馒头</td><td>卤蛋</td><td>凉拌黄瓜</td><td></td><td>486</td><td>8.6</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 class="td-actions"><a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)">删除</a></td>
<td>第21周标准营养食谱</td><td><span class="tag tag-blue">普通成人</span></td><td><span class="tag tag-green"></span></td><td><span class="tag tag-green">通过</span></td><td>张营养师</td><td>2026-05-12 09:30</td><td>李主任</td><td>2026-05-12 14:00</td><td>营养搭配均衡,符合标准要求</td><td class="td-actions"><a class="link" href="prod-recipe-plan-detail.html">详情</a> <a class="link" style="color:var(--danger)">删除</a></td>
</tr>
<tr>
<td>2026-04-15(周三)</td><td>午餐</td><td>第15周营养食谱</td><td>米饭</td><td>红烧肉+清蒸鱼</td><td>炒时蔬+豆腐</td><td>番茄蛋汤</td><td>856</td><td>38.2</td><td><span class="tag tag-blue"></span></td><td><span class="tag tag-green">通过</span></td><td><span class="tag tag-orange">建议优化</span></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)">删除</a></td>
<td>第21周老年营养食谱</td><td><span class="tag tag-blue">老年人</span></td><td><span class="tag tag-blue"></span></td><td><span class="tag tag-green">通过</span></td><td>王营养师</td><td>2026-05-12 10:15</td><td>李主任</td><td>2026-05-13 09:00</td><td>低盐低脂方案合理,建议增加钙质</td><td class="td-actions"><a class="link" href="prod-recipe-plan-detail.html">详情</a> <a class="link" style="color:var(--danger)">删除</a></td>
</tr>
<tr>
<td>2026-04-15(周三)</td><td>晚餐</td><td>第15周营养食谱</td><td>米饭+花卷</td><td>宫保鸡丁</td><td>蒜蓉菠菜</td><td>紫菜汤</td><td>724</td><td>28.6</td><td><span class="tag tag-blue"></span></td><td><span class="tag tag-green">通过</span></td><td><span class="tag tag-green">无需整改</span></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)">删除</a></td>
<td>第21周糖尿病专用食谱</td><td><span class="tag tag-blue">糖尿病人群</span></td><td><span class="tag tag-green"></span></td><td><span class="tag tag-orange">待审核</span></td><td>张营养师</td><td>2026-05-13 08:45</td><td></td><td></td><td></td><td class="td-actions"><a class="link" href="prod-recipe-plan-detail.html">详情</a> <a class="link" href="prod-recipe-plan-create.html">编辑</a> <a class="link btn-audit" style="color:var(--warning);cursor:pointer;">审核</a> <a class="link" style="color:var(--danger)">删除</a></td>
</tr>
<tr>
<td>2026-04-16(周四)</td><td>早餐</td><td>第15周营养食谱</td><td>燕麦粥+包子</td><td>茶叶蛋</td><td>拌菠菜</td><td></td><td>512</td><td>9.2</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 class="td-actions"><a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)">删除</a></td>
<td>20周标准营养食谱</td><td><span class="tag tag-blue">普通成人</span> <span class="tag tag-blue">高血压人群</span></td><td><span class="tag tag-blue"></span></td><td><span class="tag tag-green">通过</span></td><td>王营养师</td><td>2026-05-05 09:00</td><td>李主任</td><td>2026-05-05 16:30</td><td>整体达标,高血压方案控钠良好</td><td class="td-actions"><a class="link" href="prod-recipe-plan-detail.html">详情</a> <a class="link" style="color:var(--danger)">删除</a></td>
</tr>
<tr>
<td>2026-04-16(周四)</td><td>午餐</td><td>第15周营养食谱</td><td>米饭</td><td>糖醋排骨+虾仁</td><td>炒西兰花</td><td>冬瓜汤</td><td>892</td><td>40.4</td><td><span class="tag tag-blue"></span></td><td><span class="tag tag-orange">待复核</span></td><td><span class="tag tag-red">待整改</span></td><td class="td-actions"><a class="link">详情</a> <a class="link">编辑</a> <a class="link" style="color:var(--danger)">删除</a></td>
<td>20周儿童营养食谱</td><td><span class="tag tag-blue">儿童</span></td><td><span class="tag tag-orange">合格</span></td><td><span class="tag tag-red">未通过</span></td><td>赵营养师</td><td>2026-05-06 11:20</td><td>李主任</td><td>2026-05-07 10:00</td><td style="color:var(--danger)">蛋白质摄入不足,缺少奶制品类</td><td class="td-actions"><a class="link" href="prod-recipe-plan-detail.html">详情</a> <a class="link" style="color:var(--danger)">删除</a></td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="pagination">
<span>显示 5 条,共 42 条</span>
<span>显示 5 条,共 28</span>
<button class="page-btn"></button>
<button class="page-btn active">1</button>
<button class="page-btn">2</button>
@@ -221,56 +259,152 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
</div>
<!-- 新建规划弹窗 -->
<div class="modal-overlay" id="modal-plan" onclick="if(event.target===this)this.style.display='none'">
<!-- 审核弹窗 -->
<div class="modal-overlay" id="auditModal">
<div class="modal">
<div class="modal__header">
<div class="modal__title">新建规划</div>
<button class="modal__close" onclick="document.getElementById('modal-plan').style.display='none'">
<div class="modal__title">审核食谱规划</div>
<button class="modal__close" id="auditClose">
<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="modal__body">
<div class="form-row">
<div class="form-group"><label>日期<span class="required">*</span></label><input class="form-input2" type="date"></div>
<div class="form-group"><label>餐次</label><select class="form-select2"><option>请选择</option><option>早餐</option><option>午餐</option><option>晚餐</option><option>加餐</option></select></div>
<div class="form-group">
<label>审核结果<span class="required">*</span></label>
<div class="audit-options">
<div class="audit-opt" id="auditPass" data-result="pass">
<div class="audit-opt__icon">&#10003;</div>
<div class="audit-opt__label">通过</div>
</div>
<div class="audit-opt" id="auditFail" data-result="fail">
<div class="audit-opt__icon">&#10007;</div>
<div class="audit-opt__label">未通过</div>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group"><label>规划人数<span class="required">*</span></label><input class="form-input2" type="number" placeholder="请输入规划人数"></div>
<div class="form-group"><label>预计出餐时间<span class="required">*</span></label><input class="form-input2" type="text" placeholder="如:11:30 前"></div>
<div class="form-group" style="margin-bottom:0;">
<label>审核意见</label>
<textarea class="form-textarea" id="auditRemark" placeholder="请输入审核意见(选填)"></textarea>
</div>
<div class="form-row">
<div class="form-group"><label>预计烹饪时长(分钟)<span class="required">*</span></label><input class="form-input2" type="number" placeholder="请输入预计烹饪时长"></div>
<div class="form-group"><label>主食<span class="required">*</span></label><input class="form-input2" type="text" placeholder="请输入主食"></div>
</div>
<div class="form-row">
<div class="form-group"><label>荤菜</label><input class="form-input2" type="text" placeholder="请输入荤菜"></div>
<div class="form-group"><label>素菜</label><input class="form-input2" type="text" placeholder="请输入素菜"></div>
</div>
<div class="form-row">
<div class="form-group"><label>汤类</label><input class="form-input2" type="text" placeholder="请输入汤类"></div>
<div class="form-group"><label>餐品库来源<span class="required">*</span></label><select class="form-select2"><option>请选择</option><option>餐品总库</option><option>自定义餐品库</option><option>混合</option></select></div>
</div>
<div class="form-row">
<div class="form-group"><label>关联餐品库</label><input class="form-input2" type="text" placeholder="如:标准菜品库A / 低脂菜品库"></div>
<div class="form-group"><label>净菜需求量(kg)</label><input class="form-input2" type="number" placeholder="请输入净菜需求量"></div>
</div>
<div class="form-row">
<div class="form-group"><label>安全评估结论</label><select class="form-select2"><option>请选择</option><option>通过</option><option>待复核</option><option>不通过</option></select></div>
<div class="form-group"></div>
</div>
<div class="form-group"><label>食材清单</label><textarea class="form-textarea2" placeholder="如:鸡胸肉、菠菜、番茄(逗号分隔)"></textarea></div>
</div>
<div class="modal__footer">
<button class="btn btn-default" onclick="document.getElementById('modal-plan').style.display='none'">取消</button>
<button class="btn btn-primary">确定</button>
<button class="btn btn-default" id="auditCancel">取消</button>
<button class="btn btn-primary" id="auditSubmit">提交</button>
</div>
</div>
</div>
<script>
// 无子分组,无需折叠脚本
/* 多选下拉交互 */
(function(){
var trigger=document.getElementById('msTrigger');
var dropdown=document.getElementById('msDropdown');
var selected=[];
function render(){
if(!selected.length){
trigger.innerHTML='<span class="multi-select__placeholder">全部</span>';
}else{
trigger.innerHTML=selected.map(function(v){
return '<span class="multi-select__tag">'+v+'<span class="multi-select__tag-del" data-val="'+v+'">&times;</span></span>';
}).join('');
}
dropdown.querySelectorAll('.multi-select__option').forEach(function(opt){
opt.classList.toggle('selected',selected.indexOf(opt.dataset.value)!==-1);
});
}
trigger.addEventListener('click',function(e){
if(e.target.classList.contains('multi-select__tag-del')){
selected=selected.filter(function(v){return v!==e.target.dataset.val;});
render();return;
}
dropdown.classList.toggle('show');
});
dropdown.addEventListener('click',function(e){
var opt=e.target.closest('.multi-select__option');
if(!opt) return;
var val=opt.dataset.value;
var idx=selected.indexOf(val);
if(idx===-1) selected.push(val); else selected.splice(idx,1);
render();
});
document.addEventListener('click',function(e){
if(!document.getElementById('msPopulation').contains(e.target)) dropdown.classList.remove('show');
});
})();
/* ===== 审核弹窗 ===== */
(function(){
var modal=document.getElementById('auditModal');
var passBtn=document.getElementById('auditPass');
var failBtn=document.getElementById('auditFail');
var closeBtn=document.getElementById('auditClose');
var cancelBtn=document.getElementById('auditCancel');
var submitBtn=document.getElementById('auditSubmit');
var remarkEl=document.getElementById('auditRemark');
var auditResult='';
var currentRow=null;
function openModal(row){
currentRow=row;
auditResult='';
passBtn.className='audit-opt';
failBtn.className='audit-opt';
remarkEl.value='';
modal.classList.add('show');
}
function closeModal(){modal.classList.remove('show');currentRow=null;}
passBtn.addEventListener('click',function(){
auditResult='pass';
passBtn.className='audit-opt selected-pass';
failBtn.className='audit-opt';
});
failBtn.addEventListener('click',function(){
auditResult='fail';
failBtn.className='audit-opt selected-fail';
passBtn.className='audit-opt';
});
// 点击审核按钮
document.querySelector('.table-card').addEventListener('click',function(e){
var btn=e.target.closest('.btn-audit');
if(!btn) return;
e.preventDefault();
var row=btn.closest('tr');
openModal(row);
});
submitBtn.addEventListener('click',function(){
if(!auditResult){alert('请选择审核结果');return;}
if(!currentRow) return;
// 更新安全评估列
var cells=currentRow.querySelectorAll('td');
var safetyCell=cells[3]; // 第4列=安全评估
var actionsCell=currentRow.querySelector('.td-actions');
if(auditResult==='pass'){
safetyCell.innerHTML='<span class="tag tag-green">通过</span>';
// 审核人+时间
cells[6].textContent='当前用户';
cells[7].textContent=new Date().toISOString().slice(0,16).replace('T',' ');
// 操作列:移除编辑和审核
actionsCell.innerHTML='<a class="link" href="prod-recipe-plan-detail.html">详情</a> <a class="link" style="color:var(--danger)">删除</a>';
}else{
safetyCell.innerHTML='<span class="tag tag-red">未通过</span>';
cells[6].textContent='当前用户';
cells[7].textContent=new Date().toISOString().slice(0,16).replace('T',' ');
actionsCell.innerHTML='<a class="link" href="prod-recipe-plan-detail.html">详情</a> <a class="link" style="color:var(--danger)">删除</a>';
}
closeModal();
});
closeBtn.addEventListener('click',closeModal);
cancelBtn.addEventListener('click',closeModal);
modal.addEventListener('click',function(e){if(e.target===modal)closeModal();});
})();
</script>
</body>
</html>
</style>