feat: 营养管理主子表重构——净菜订单/外售/内供/采购/进柜/组配/烹制全面升级,小程序订单页增强

后台营养管理7个页面数据模型从扁平结构重构为主子表(一单多品),支持动态明细行构建、
展开查看、行级确认收货、验收明细弹窗等交互;采购/进柜/组配/烹制同步适配新列。
小程序端订单详情新增支付信息卡片与餐线订单渲染,我的订单新增Tab切换过滤与餐线卡片。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
fengpu
2026-07-24 17:28:14 +08:00
co-authored by Claude Opus 4.7
parent bb83ecdb37
commit 91621bc48b
15 changed files with 1843 additions and 1553 deletions
@@ -27,6 +27,7 @@
.oh-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 10px; }
.oh-date { font-size: 15px; font-weight: 700; color: #333; margin-bottom: 3px; }
.oh-id { font-size: 11px; color: #999; }
.oh-order-time { font-size: 11px; color: #999; margin-top: 2px; }
.oh-badge { font-size: 12px; padding: 3px 10px; border-radius: 12px; background: #f6ffed; color: #52c41a; font-weight: 600; }
.oh-stats { display: flex; gap: 0; padding: 10px 0; border-top: 0.5px solid #f5f5f5; border-bottom: 0.5px solid #f5f5f5; }
.oh-stat { flex: 1; text-align: center; }
@@ -116,6 +117,7 @@
<div>
<div class="oh-date">2026-04-28 午餐</div>
<div class="oh-id">NM-20260428-001</div>
<div class="oh-order-time">下单时间:2026-04-28 11:32:15</div>
</div>
<span class="oh-badge">✅ 已完成</span>
</div>
@@ -273,10 +275,26 @@
</div>
</div>
<!-- 营养建议 -->
<div class="advice-card" style="margin:0">
<div class="advice-card" style="margin:0 0 12px;">
<div class="advice-title">营养建议</div>
<div class="advice-text">⚠ 本餐脂肪摄入偏高,建议下次适当减少荤菜;蔬菜摄入量不足,建议增加一份绿叶菜。</div>
</div>
<!-- 支付信息 -->
<div class="sec-card" style="margin:0;">
<div class="sec-title">📋 支付信息</div>
<div class="info-row">
<span class="ir-label">支付方式</span>
<span class="ir-value">💳 微信支付</span>
</div>
<div class="info-row">
<span class="ir-label">支付流水号</span>
<span class="ir-value" style="font-family:monospace;font-size:11px;">4200002564202604281132180015</span>
</div>
<div class="info-row">
<span class="ir-label">支付时间</span>
<span class="ir-value">2026-04-28 11:32:18</span>
</div>
</div>
</div>
</div>
<div class="home-indicator"></div>
@@ -288,7 +306,8 @@
return m ? decodeURIComponent(m[1]) : '';
}
var type = getQuery('type') === 'clean' ? 'clean' : 'meal';
var rawType = getQuery('type');
var type = (rawType === 'clean' || rawType === 'line') ? rawType : 'meal';
/* 净菜包订单状态:delivering=待配送 / shipped=已发货 / done=已完成;缺省按待配送处理 */
var rawStatus = getQuery('status');
var status = ['delivering', 'shipped', 'done'].indexOf(rawStatus) >= 0 ? rawStatus : 'delivering';
@@ -296,7 +315,100 @@
/* 是否展示食材来源与溯源按钮:仅 已发货 / 已完成 可见 */
var showTrace = status === 'shipped' || status === 'done';
if (type === 'clean') {
if (type === 'line') {
/* ========== 餐线订单详情 ========== */
var payMap = { alipay: '支付宝', wechat: '微信支付', cash: '现金' };
var payMethod = payMap[getQuery('pay')] || '微信支付';
/* 订单头部 */
document.getElementById('orderHeader').innerHTML =
'<div class="oh-top">'
+ '<div>'
+ '<div class="oh-date">2026-05-14 午餐</div>'
+ '<div class="oh-id">XL-20260514-001</div>'
+ '<div class="oh-order-time">下单时间:2026-05-14 11:58:32</div>'
+ '</div>'
+ '<span class="oh-badge" style="background:#f6ffed;color:#52c41a;">✅ 已完成</span>'
+ '</div>'
+ '<div class="oh-stats">'
+ '<div class="oh-stat"><div class="sv" style="color:#fa8c16">85</div><div class="sl">营养评分</div></div>'
+ '<div class="oh-stat"><div class="sv" style="color:#f5222d">¥32.50</div><div class="sl">消费金额</div></div>'
+ '<div class="oh-stat"><div class="sv" style="color:#1677ff">3</div><div class="sl">菜品数</div></div>'
+ '<div class="oh-stat"><div class="sv" style="color:#555">505</div><div class="sl">总热量kcal</div></div>'
+ '</div>';
/* 菜品明细:克重计量 + 食材来源溯源 */
document.getElementById('dishSection').innerHTML =
'<div class="dish-sec-title">菜品明细 · 食材 · 溯源</div>'
/* 清蒸鱼 */
+ '<div class="dish-card">'
+ '<div class="dish-head">'
+ '<div class="dish-icon">🐟</div>'
+ '<div class="dish-info">'
+ '<div class="dish-name-row"><span class="dish-name">清蒸鱼</span></div>'
+ '<div class="dish-meta">185g · 142kcal</div>'
+ '</div>'
+ '<span class="dish-price">¥18.00</span>'
+ '</div>'
+ '<div class="ing-section">'
+ '<div class="ing-label">食材来源</div>'
+ '<div class="ing-row"><div class="ing-dot"></div><div class="ing-info"><div class="ing-name">草鱼 120g</div><div class="ing-code">ST-EX-HS01-SF-CAYO-060K-20260414-001</div></div><span class="ing-ok">✅合格</span><a class="ing-trace" href="trace-detail.html">🔗溯源</a></div>'
+ '<div class="ing-row"><div class="ing-dot"></div><div class="ing-info"><div class="ing-name">葱丝 15g</div><div class="ing-code">NC-PROC-CGSI-20260422-01</div></div><span class="ing-ok">✅合格</span><a class="ing-trace" href="trace-detail.html">🔗溯源</a></div>'
+ '<div class="ing-row"><div class="ing-dot"></div><div class="ing-info"><div class="ing-name">姜丝 10g</div><div class="ing-code">NC-PROC-JIAS-20260422-01</div></div><span class="ing-ok">✅合格</span><a class="ing-trace" href="trace-detail.html">🔗溯源</a></div>'
+ '</div>'
+ '</div>'
/* 炒时蔬 */
+ '<div class="dish-card">'
+ '<div class="dish-head">'
+ '<div class="dish-icon">🥬</div>'
+ '<div class="dish-info">'
+ '<div class="dish-name-row"><span class="dish-name">炒时蔬</span></div>'
+ '<div class="dish-meta">120g · 68kcal</div>'
+ '</div>'
+ '<span class="dish-price">¥8.00</span>'
+ '</div>'
+ '<div class="ing-section">'
+ '<div class="ing-label">食材来源</div>'
+ '<div class="ing-row"><div class="ing-dot"></div><div class="ing-info"><div class="ing-name">西兰花 100g</div><div class="ing-code">ST-EX-LY01-VG-XLHU-080K-20260416-001</div></div><span class="ing-ok">✅合格</span><a class="ing-trace" href="trace-detail.html">🔗溯源</a></div>'
+ '<div class="ing-row"><div class="ing-dot"></div><div class="ing-info"><div class="ing-name">蒜末 5g</div><div class="ing-code">ST-EX-LY02-VG-SUAN-010K-20260418-001</div></div><span class="ing-ok">✅合格</span><a class="ing-trace" href="trace-detail.html">🔗溯源</a></div>'
+ '</div>'
+ '</div>'
/* 米饭 */
+ '<div class="dish-card">'
+ '<div class="dish-head">'
+ '<div class="dish-icon">🍚</div>'
+ '<div class="dish-info">'
+ '<div class="dish-name-row"><span class="dish-name">米饭</span></div>'
+ '<div class="dish-meta">200g · 232kcal</div>'
+ '</div>'
+ '<span class="dish-price">¥2.00</span>'
+ '</div>'
+ '<div class="ing-section">'
+ '<div class="ing-label">食材来源</div>'
+ '<div class="ing-row"><div class="ing-dot"></div><div class="ing-info"><div class="ing-name">大米 200g</div><div class="ing-code">ST-EX-LY02-DG-DAMI-1000-20260413-001</div></div><span class="ing-ok">✅合格</span><a class="ing-trace" href="trace-detail.html">🔗溯源</a></div>'
+ '</div>'
+ '</div>'
/* 支付信息 */
+ '<div class="sec-card" style="margin:12px 0 0;">'
+ '<div class="sec-title">📋 支付信息</div>'
+ '<div class="info-row">'
+ '<span class="ir-label">支付方式</span>'
+ '<span class="ir-value">💳 ' + payMethod + '</span>'
+ '</div>'
+ '<div class="info-row">'
+ '<span class="ir-label">支付流水号</span>'
+ '<span class="ir-value" style="font-family:monospace;font-size:11px;">' + (payMethod === '支付宝' ? '2026051422001416320500123456' : payMethod === '微信支付' ? '4200002564202605141132180012' : 'CASH-20260514-0003') + '</span>'
+ '</div>'
+ '<div class="info-row">'
+ '<span class="ir-label">支付时间</span>'
+ '<span class="ir-value">2026-05-14 11:58:35</span>'
+ '</div>'
+ '<div class="info-row">'
+ '<span class="ir-label">取餐餐线</span>'
+ '<span class="ir-value">A区中餐线</span>'
+ '</div>'
+ '</div>';
} else if (type === 'clean') {
/* 状态徽标:文案与配色随 status 切换 */
var badgeMap = {
delivering: { text: '🚚 待配送', bg: '#e6f4ff', color: '#1677ff' },
@@ -311,6 +423,7 @@
+ '<div>'
+ '<div class="oh-date">2026-04-29 午餐时段配送</div>'
+ '<div class="oh-id">CL-20260428-017</div>'
+ '<div class="oh-order-time">下单时间:2026-04-28 14:23:08</div>'
+ '</div>'
+ '<span class="oh-badge" style="background:' + b.bg + ';color:' + b.color + ';">' + b.text + '</span>'
+ '</div>'
@@ -396,9 +509,25 @@
+ '</div>'
+ lowFatIng
+ '</div>'
+ '<div class="advice-card" style="margin:0;">'
+ '<div class="advice-card" style="margin:0 0 12px;">'
+ '<div class="advice-title">🍳 烹制提示</div>'
+ '<div class="advice-text" style="color:#1677ff;">宫保鸡丁净菜包:热锅冷油下鸡丁滑炒至变色,加入配料包翻炒 2 分钟即可出锅;糙米饭需另行准备。</div>'
+ '</div>'
/* 支付信息 */
+ '<div class="sec-card" style="margin:0;">'
+ '<div class="sec-title">📋 支付信息</div>'
+ '<div class="info-row">'
+ '<span class="ir-label">支付方式</span>'
+ '<span class="ir-value">💳 微信支付</span>'
+ '</div>'
+ '<div class="info-row">'
+ '<span class="ir-label">支付流水号</span>'
+ '<span class="ir-value" style="font-family:monospace;font-size:11px;">4200002564202604281423080017</span>'
+ '</div>'
+ '<div class="info-row">'
+ '<span class="ir-label">支付时间</span>'
+ '<span class="ir-value">2026-04-28 14:23:08</span>'
+ '</div>'
+ '</div>';
/* 订单进度时间线:按状态高亮对应节点 */
@@ -30,6 +30,7 @@
.order-card .type-tag { font-size: 10px; padding: 1px 6px; border-radius: 8px; font-weight: 600; line-height: 16px; }
.order-card .type-tag.meal { background: #e6f4ff; color: #1677ff; }
.order-card .type-tag.clean { background: #fff7e6; color: #fa8c16; }
.order-card .type-tag.line { background: #e6fffb; color: #13c2c2; }
.order-card .status { font-size: 12px; color: #52c41a; font-weight: 500; }
.order-card .status.pending { color: #fa8c16; }
.order-card .status.shipping { color: #1677ff; }
@@ -40,9 +41,12 @@
background: #fff7e6; border-radius: 6px; display: flex; align-items: center; gap: 4px;
}
.order-card .fulfill-meta.done { color: #52c41a; background: #f6ffed; }
.order-card .pay-meta { font-size: 11px; color: #666; margin-bottom: 8px; padding: 4px 8px; background: #f5f5f5; border-radius: 6px; display: flex; align-items: center; gap: 4px; }
.order-card .card-bottom { display: flex; justify-content: space-between; align-items: center; }
.order-card .total { font-size: 14px; font-weight: 700; color: #f5222d; }
.order-card .time { font-size: 11px; color: #999; }
.order-tabs .ot { cursor: pointer; }
.order-card.hidden { display: none; }
</style>
</head>
<body>
@@ -62,15 +66,15 @@
<span class="ot">待配送</span>
<span class="ot">已完成</span>
</div>
<a class="order-card" href="me-order-detail-pending.html">
<a class="order-card" href="me-order-detail-pending.html" data-status="pending">
<div class="card-top">
<div class="shop-row"><span class="shop">🍽️ 阳光食堂 · 午餐</span><span class="type-tag meal"></span></div>
<div class="shop-row"><span class="shop">🍽️ 阳光食堂 · 午餐</span><span class="type-tag meal"></span></div>
<span class="status pending">待取餐</span>
</div>
<div class="items">低脂鸡胸肉沙拉 ×1、杂粮饭 ×1、紫菜蛋花汤 ×1</div>
<div class="card-bottom"><span class="total">¥ 28.5</span><span class="time">今天 11:32</span></div>
</a>
<a class="order-card" href="me-order-detail.html?type=clean&status=delivering">
<a class="order-card" href="me-order-detail.html?type=clean&status=delivering" data-status="shipping">
<div class="card-top">
<div class="shop-row"><span class="shop">🥬 阳光食堂 · 净菜包订单</span><span class="type-tag clean">净菜包</span></div>
<span class="status shipping">待配送</span>
@@ -79,15 +83,15 @@
<div class="fulfill-meta">🚚 配送到家 · 明日(4月29日)11:00-12:00</div>
<div class="card-bottom"><span class="total">¥ 79.0</span><span class="time">今天 14:23 下单</span></div>
</a>
<a class="order-card" href="me-order-detail.html">
<a class="order-card" href="me-order-detail.html" data-status="done">
<div class="card-top">
<div class="shop-row"><span class="shop">🍽️ 阳光食堂 · 早餐</span><span class="type-tag meal"></span></div>
<div class="shop-row"><span class="shop">🍽️ 阳光食堂 · 早餐</span><span class="type-tag meal"></span></div>
<span class="status">已完成</span>
</div>
<div class="items">全麦三明治 ×1、鲜榨橙汁 ×1</div>
<div class="card-bottom"><span class="total">¥ 15.0</span><span class="time">今天 07:45</span></div>
</a>
<a class="order-card" href="me-order-detail.html?type=clean&status=done">
<a class="order-card" href="me-order-detail.html?type=clean&status=done" data-status="done">
<div class="card-top">
<div class="shop-row"><span class="shop">🥬 阳光食堂 · 净菜包订单</span><span class="type-tag clean">净菜包</span></div>
<span class="status">已完成</span>
@@ -96,16 +100,70 @@
<div class="fulfill-meta done">🏪 食堂自提 · 已取货</div>
<div class="card-bottom"><span class="total">¥ 36.0</span><span class="time">昨天 12:15</span></div>
</a>
<a class="order-card" href="me-order-detail.html">
<a class="order-card" href="me-order-detail.html" data-status="done">
<div class="card-top">
<div class="shop-row"><span class="shop">🍽️ 健康轻食 · 午餐</span><span class="type-tag meal"></span></div>
<div class="shop-row"><span class="shop">🍽️ 健康轻食 · 午餐</span><span class="type-tag meal"></span></div>
<span class="status">已完成</span>
</div>
<div class="items">三文鱼牛油果碗 ×1、蔬菜汁 ×1</div>
<div class="card-bottom"><span class="total">¥ 42.0</span><span class="time">昨天 12:15</span></div>
</a>
<!-- 餐线订单:始终已完成、克重计量、支付方式见详情页 -->
<a class="order-card" href="me-order-detail.html?type=line&pay=alipay" data-status="done">
<div class="card-top">
<div class="shop-row"><span class="shop">🍽️ 阳光食堂 · 午餐</span><span class="type-tag line">餐线</span></div>
<span class="status">已完成</span>
</div>
<div class="items">清蒸鱼 185g、炒时蔬 120g、米饭 200g</div>
<div class="card-bottom"><span class="total">¥ 32.50</span><span class="time">今天 12:05</span></div>
</a>
<a class="order-card" href="me-order-detail.html?type=line&pay=wechat" data-status="done">
<div class="card-top">
<div class="shop-row"><span class="shop">🍽️ 阳光食堂 · 早餐</span><span class="type-tag line">餐线</span></div>
<span class="status">已完成</span>
</div>
<div class="items">小米粥 300g、茶叶蛋 60g、花卷 100g</div>
<div class="card-bottom"><span class="total">¥ 8.00</span><span class="time">今天 07:30</span></div>
</a>
<a class="order-card" href="me-order-detail.html?type=line&pay=cash" data-status="done">
<div class="card-top">
<div class="shop-row"><span class="shop">🥗 健康轻食 · 午餐</span><span class="type-tag line">餐线</span></div>
<span class="status">已完成</span>
</div>
<div class="items">香煎鸡胸 150g、藜麦沙拉 200g、南瓜汤 250ml</div>
<div class="card-bottom"><span class="total">¥ 38.00</span><span class="time">昨天 11:50</span></div>
</a>
</div>
<div class="home-indicator"></div>
</div>
<script>
/* Tab 切换过滤 */
(function() {
var tabs = document.querySelectorAll('.order-tabs .ot');
var cards = document.querySelectorAll('.order-card');
if (!tabs.length || !cards.length) return;
/* 状态映射:tab 文本 → data-status 值 */
var statusMap = { '待取餐': 'pending', '待配送': 'shipping', '已完成': 'done' };
tabs.forEach(function(tab) {
tab.addEventListener('click', function() {
/* 切换 active 样式 */
tabs.forEach(function(t) { t.classList.remove('active'); });
tab.classList.add('active');
var filter = tab.textContent.trim();
cards.forEach(function(card) {
if (filter === '全部') {
card.classList.remove('hidden');
} else {
var target = statusMap[filter];
card.classList.toggle('hidden', card.getAttribute('data-status') !== target);
}
});
});
});
})();
</script>
</body>
</html>