feat: 用户管理账户体系+餐品档案结构调整+小程序人脸认证完善
- 后台用户管理:表格新增账号余额列与账户管理操作;新增批量充值弹窗、编辑会员信息弹窗 - 后台账户管理页:重构为两栏布局(用户概要+充值卡片),优化账单列表区 - 后台餐品档案:宣传视频启用开关;营养指标 Tab 合并入餐品构成;新增熟重字段;步骤数 7→6 - 小程序人脸认证:采集/认证话术统一;首页与档案页接入认证状态标签 - 小程序钱包/订单:钱包主页扩展,新增明细详情入口 - 文档:更新 CHANGELOG,新增下周工作计划 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
09f1080730
commit
e83b0f0697
@@ -106,14 +106,14 @@
|
||||
</div>
|
||||
<span class="avatar-tip">点击修改头像</span>
|
||||
</div>
|
||||
<!-- [原型演示] 人脸认证状态切换 -->
|
||||
<!-- [原型演示] 人脸采集状态切换 -->
|
||||
<div class="demo-bar">
|
||||
<span class="demo-label">演示切换:</span>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="faceToggle" onchange="toggleFaceState()">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="demo-state" id="demoStateLabel">未认证</span>
|
||||
<span class="demo-state" id="demoStateLabel">未采集</span>
|
||||
</div>
|
||||
<!-- 信息列表 -->
|
||||
<div class="info-group">
|
||||
@@ -126,10 +126,10 @@
|
||||
<div class="info-row"><span class="info-label">单位</span><span class="info-value">CQ能源</span></div>
|
||||
<div class="info-row"><span class="info-label">部门</span><span class="info-value">采油一厂</span></div>
|
||||
<div class="info-row"><span class="info-label">会员等级</span><span class="info-value gold">黄金会员</span></div>
|
||||
<!-- [交互] 已认证:点击查看人脸信息/重新录入;未认证:点击前往录入 -->
|
||||
<!-- [交互] 已采集:点击查看人脸信息/重新录入;未采集:点击前往录入 -->
|
||||
<div class="info-row editable" id="faceRow" onclick="handleFaceClick()">
|
||||
<span class="info-label">人脸信息</span>
|
||||
<span class="info-value" id="faceValue" style="color:#fa8c16;">未认证<i class="edit-icon">›</i></span>
|
||||
<span class="info-value" id="faceValue" style="color:#fa8c16;">未采集<i class="edit-icon">›</i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,7 +138,7 @@
|
||||
<script>
|
||||
var isFaceVerified = false;
|
||||
|
||||
/* [交互] 切换开关:改变人脸认证状态 */
|
||||
/* [交互] 切换开关:改变人脸采集状态 */
|
||||
function toggleFaceState() {
|
||||
isFaceVerified = document.getElementById('faceToggle').checked;
|
||||
updateFaceRow();
|
||||
@@ -150,16 +150,16 @@
|
||||
var labelEl = document.getElementById('demoStateLabel');
|
||||
if (isFaceVerified) {
|
||||
valEl.style.color = '#52c41a';
|
||||
valEl.innerHTML = '已认证<i class="edit-icon">›</i>';
|
||||
labelEl.textContent = '已认证';
|
||||
valEl.innerHTML = '已采集<i class="edit-icon">›</i>';
|
||||
labelEl.textContent = '已采集';
|
||||
} else {
|
||||
valEl.style.color = '#fa8c16';
|
||||
valEl.innerHTML = '未认证<i class="edit-icon">›</i>';
|
||||
labelEl.textContent = '未认证';
|
||||
valEl.innerHTML = '未采集<i class="edit-icon">›</i>';
|
||||
labelEl.textContent = '未采集';
|
||||
}
|
||||
}
|
||||
|
||||
/* [交互] 点击人脸信息行:已认证→人脸详情页;未认证→人脸录入页 */
|
||||
/* [交互] 点击人脸信息行:已采集→人脸详情页;未采集→人脸录入页 */
|
||||
function handleFaceClick() {
|
||||
if (isFaceVerified) {
|
||||
window.location.href = 'me-face-info.html';
|
||||
|
||||
@@ -96,13 +96,12 @@
|
||||
</div>
|
||||
<!-- 人脸信息详情 -->
|
||||
<div class="info-group">
|
||||
<div class="info-row"><span class="info-label">认证状态</span><span class="info-value green">已认证</span></div>
|
||||
<div class="info-row"><span class="info-label">采集状态</span><span class="info-value green">已采集</span></div>
|
||||
<div class="info-row"><span class="info-label">录入时间</span><span class="info-value">2026-08-01 14:30</span></div>
|
||||
<div class="info-row"><span class="info-label">清晰度</span><span class="info-value green">95%</span></div>
|
||||
</div>
|
||||
<!-- [交互] 点击跳转人脸重新录入 -->
|
||||
<div class="btn-section">
|
||||
<a class="btn-reenroll" href="me-face-verify.html">重新录入</a>
|
||||
<a class="btn-reenroll" href="me-face-verify.html">重新采集</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-indicator"></div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>人脸认证 | 智能餐养小程序</title>
|
||||
<title>人脸采集 | 智能餐养小程序</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -102,7 +102,7 @@
|
||||
}
|
||||
.verify-tip .icon { font-size: 18px; }
|
||||
|
||||
/* 认证按钮 */
|
||||
/* 采集按钮 */
|
||||
.btn-start {
|
||||
width: 200px; padding: 14px 0; border: none; border-radius: 24px;
|
||||
background: linear-gradient(135deg, #1677ff, #4096ff);
|
||||
@@ -115,7 +115,7 @@
|
||||
background: #ccc; box-shadow: none; cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 认证结果卡片 */
|
||||
/* 采集结果卡片 */
|
||||
.result-card {
|
||||
width: 100%; background: #fff; border-radius: 12px; padding: 24px 16px;
|
||||
text-align: center; margin-bottom: 16px;
|
||||
@@ -158,7 +158,7 @@
|
||||
</div>
|
||||
<div class="nav-bar">
|
||||
<a class="nav-back" href="me-home.html" id="btnBack">‹</a>
|
||||
<span class="nav-title">人脸认证</span>
|
||||
<span class="nav-title">人脸采集</span>
|
||||
<span class="nav-placeholder"></span>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
@@ -171,7 +171,7 @@
|
||||
<div class="step-dot" id="step3">3</div>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 阶段一:准备认证 -->
|
||||
<!-- [交互] 阶段一:准备采集 -->
|
||||
<div class="verify-body" id="phasePrepare">
|
||||
<div class="camera-frame">
|
||||
<div class="camera-corners">
|
||||
@@ -188,11 +188,11 @@
|
||||
<p class="verify-tip">
|
||||
<span class="icon">💡</span> 请确保光线充足、面部无遮挡<br>按提示完成眨眼、张嘴等动作
|
||||
</p>
|
||||
<!-- [交互] 点击开始人脸认证采集 -->
|
||||
<button class="btn-start" onclick="startVerify()">开始认证</button>
|
||||
<!-- [交互] 点击开始人脸采集 -->
|
||||
<button class="btn-start" onclick="startVerify()">开始采集</button>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 阶段二:认证进行中(默认隐藏) -->
|
||||
<!-- [交互] 阶段二:采集中(默认隐藏) -->
|
||||
<div class="verify-body" id="phaseCapturing" style="display:none;">
|
||||
<div class="camera-frame" style="box-shadow: 0 0 0 4px rgba(22,119,255,0.6), 0 8px 32px rgba(0,0,0,0.2);">
|
||||
<div class="camera-corners">
|
||||
@@ -216,15 +216,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- [交互] 阶段三:认证结果(默认隐藏) -->
|
||||
<!-- [交互] 阶段三:采集结果(默认隐藏) -->
|
||||
<div class="verify-body" id="phaseResult" style="display:none;">
|
||||
<div class="result-card">
|
||||
<div class="result-icon" id="resultIcon">✅</div>
|
||||
<div class="result-title" id="resultTitle">认证成功</div>
|
||||
<div class="result-title" id="resultTitle">采集成功</div>
|
||||
<div class="result-desc" id="resultDesc">人脸信息已录入,可用于后续快速验证</div>
|
||||
<div class="result-meta">
|
||||
<div class="meta-item"><div class="meta-val" id="metaTime">--</div><div class="meta-label">认证时间</div></div>
|
||||
<div class="meta-item"><div class="meta-val" style="color:#52c41a;" id="metaScore">--</div><div class="meta-label">清晰度</div></div>
|
||||
<div class="meta-item"><div class="meta-val" id="metaTime">--</div><div class="meta-label">采集时间</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
@@ -244,7 +243,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* [交互] 开始认证:切换到采集阶段,模拟采集进度 */
|
||||
/* [交互] 开始采集:切换到采集阶段,模拟采集进度 */
|
||||
function startVerify() {
|
||||
document.getElementById('phasePrepare').style.display = 'none';
|
||||
document.getElementById('phaseCapturing').style.display = 'flex';
|
||||
@@ -276,7 +275,7 @@
|
||||
}, 180);
|
||||
}
|
||||
|
||||
/* [交互] 显示认证结果 */
|
||||
/* [交互] 显示采集结果 */
|
||||
function showResult() {
|
||||
document.getElementById('phaseCapturing').style.display = 'none';
|
||||
document.getElementById('phaseResult').style.display = 'flex';
|
||||
@@ -294,7 +293,6 @@
|
||||
String(now.getDate()).padStart(2,'0')+' '+
|
||||
String(now.getHours()).padStart(2,'0')+':'+
|
||||
String(now.getMinutes()).padStart(2,'0');
|
||||
document.getElementById('metaScore').textContent = (92 + Math.floor(Math.random()*7)) + '%';
|
||||
}
|
||||
|
||||
/* [交互] 重新采集:回到准备阶段 */
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
}
|
||||
.user-dept { font-size: 12px; opacity: 0.8; margin-top: 3px; }
|
||||
.user-extra { display: flex; gap: 12px; margin-top: 4px; font-size: 11px; opacity: 0.85; }
|
||||
/* 人脸认证标签 */
|
||||
/* 人脸采集标签 */
|
||||
.face-tag {
|
||||
display: inline-flex; align-items: center; gap: 3px;
|
||||
padding: 1px 7px; border-radius: 10px; font-size: 10px; font-weight: 500;
|
||||
@@ -122,8 +122,8 @@
|
||||
<div class="user-meta">
|
||||
<div class="user-name-row">
|
||||
<span class="user-name">张三</span>
|
||||
<!-- [交互] 未认证时点击跳转人脸录入页;已认证时切换为 face-tag--verified -->
|
||||
<a class="face-tag face-tag--unverified" href="me-face-verify.html">未认证</a>
|
||||
<!-- [交互] 未采集时点击跳转人脸录入页;已采集时切换为 face-tag--verified -->
|
||||
<a class="face-tag face-tag--unverified" href="me-face-verify.html">未采集</a>
|
||||
</div>
|
||||
<div class="user-dept">CQ能源 · 采油一厂</div>
|
||||
<div class="user-extra">
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
.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-order-place { 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; }
|
||||
@@ -117,6 +118,7 @@
|
||||
<div>
|
||||
<div class="oh-date">2026-04-28 午餐</div>
|
||||
<div class="oh-id">NM-20260428-001</div>
|
||||
<div class="oh-order-place">下单场所:二号食堂</div>
|
||||
<div class="oh-order-time">下单时间:2026-04-28 11:32:15</div>
|
||||
</div>
|
||||
<span class="oh-badge">✅ 已完成</span>
|
||||
@@ -326,6 +328,7 @@
|
||||
+ '<div>'
|
||||
+ '<div class="oh-date">2026-05-14 午餐</div>'
|
||||
+ '<div class="oh-id">XL-20260514-001</div>'
|
||||
+ '<div class="oh-order-place">下单场所:二号食堂</div>'
|
||||
+ '<div class="oh-order-time">下单时间:2026-05-14 11:58:32</div>'
|
||||
+ '</div>'
|
||||
+ '<span class="oh-badge" style="background:#f6ffed;color:#52c41a;">✅ 已完成</span>'
|
||||
@@ -403,10 +406,6 @@
|
||||
+ '<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 切换 */
|
||||
@@ -423,7 +422,8 @@
|
||||
+ '<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 class="oh-order-place">下单场所:二号食堂</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>'
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
.balance-label { font-size: 14px; opacity: 0.85; margin-bottom: 8px; }
|
||||
.balance-amount { font-size: 36px; font-weight: 700; letter-spacing: -1px; }
|
||||
.balance-amount .symbol { font-size: 20px; font-weight: 500; margin-right: 4px; }
|
||||
.action-buttons { display: flex; gap: 12px; margin-top: 20px; }
|
||||
.action-btn { flex: 1; height: 36px; border-radius: 18px; border: none; font-size: 14px; font-weight: 500; cursor: pointer; }
|
||||
.action-btn.primary { background: #fff; color: #1677ff; }
|
||||
.action-btn.secondary { background: rgba(255,255,255,0.25); color: #fff; border: 1px solid rgba(255,255,255,0.5); }
|
||||
.section-title { font-size: 15px; font-weight: 600; color: #333; padding: 16px 16px 12px; }
|
||||
/* 月份分组 */
|
||||
.month-group { margin: 0 16px 4px; }
|
||||
@@ -72,10 +68,6 @@
|
||||
<div class="balance-card">
|
||||
<div class="balance-label">当前余额</div>
|
||||
<div class="balance-amount"><span class="symbol">¥</span>256.50</div>
|
||||
<div class="action-buttons">
|
||||
<button class="action-btn primary">充值</button>
|
||||
<button class="action-btn secondary">提现</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 余额明细 -->
|
||||
|
||||
@@ -96,6 +96,16 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.sale-toggle.active .sale-toggle__switch::after{transform:translateX(14px);}
|
||||
.sale-toggle__label{font-size:11px;color:var(--text-placeholder);}
|
||||
.sale-toggle.active .sale-toggle__label{color:var(--success);font-weight:500;}
|
||||
/* 宣传视频启用开关(蓝色) */
|
||||
.promo-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
|
||||
.promo-toggle__switch{width:32px;height:18px;background:#D9D9D9;border-radius:9px;position:relative;transition:background .2s;flex-shrink:0;}
|
||||
.promo-toggle__switch::after{content:'';position:absolute;left:2px;top:2px;width:14px;height:14px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 2px rgba(0,0,0,0.15);}
|
||||
.promo-toggle.active .promo-toggle__switch{background:var(--primary);}
|
||||
.promo-toggle.active .promo-toggle__switch::after{transform:translateX(14px);}
|
||||
.promo-toggle__label{font-size:12px;color:var(--text-placeholder);}
|
||||
.promo-toggle.active .promo-toggle__label{color:var(--primary);font-weight:500;}
|
||||
/* 宣传视频 section 标题行(左标题+右开关) */
|
||||
.promo-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
|
||||
.btn-text{background:none;border:none;cursor:pointer;font-size:12px;padding:2px 6px;border-radius:var(--radius-sm);transition:all .15s;}
|
||||
.btn-text--primary{color:var(--primary);}.btn-text--primary:hover{background:var(--primary-light);}
|
||||
.btn-text--danger{color:var(--danger);}.btn-text--danger:hover{background:#FFF1F0;}
|
||||
@@ -260,10 +270,9 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
<div class="step active" onclick="switchTab(0)"><span class="step__num">1</span><span class="step__label">基本档案</span></div>
|
||||
<div class="step" onclick="switchTab(1)"><span class="step__num">2</span><span class="step__label">餐品构成</span></div>
|
||||
<div class="step" onclick="switchTab(2)"><span class="step__num">3</span><span class="step__label">烹饪工艺</span></div>
|
||||
<div class="step" onclick="switchTab(3)"><span class="step__num">4</span><span class="step__label">营养指标</span></div>
|
||||
<div class="step" onclick="switchTab(4)"><span class="step__num">5</span><span class="step__label">味道档案</span></div>
|
||||
<div class="step" onclick="switchTab(5)"><span class="step__num">6</span><span class="step__label">口感档案</span></div>
|
||||
<div class="step" onclick="switchTab(6)"><span class="step__num">7</span><span class="step__label">色泽档案</span></div>
|
||||
<div class="step" onclick="switchTab(3)"><span class="step__num">4</span><span class="step__label">味道档案</span></div>
|
||||
<div class="step" onclick="switchTab(4)"><span class="step__num">5</span><span class="step__label">口感档案</span></div>
|
||||
<div class="step" onclick="switchTab(5)"><span class="step__num">6</span><span class="step__label">色泽档案</span></div>
|
||||
</div>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
@@ -354,7 +363,7 @@ var INNOV = innovId ? INNOV_MAP[innovId] : null;
|
||||
|
||||
var initTab = parseInt(urlParams.get('tab')) || 0;
|
||||
var currentStep = 0;
|
||||
var totalSteps = 7;
|
||||
var totalSteps = 6;
|
||||
if(initTab >= 0 && initTab < totalSteps) currentStep = initTab;
|
||||
/* 适配人群数据 */
|
||||
var crowdList = [];
|
||||
@@ -405,14 +414,24 @@ var STEP_HTML = [
|
||||
<div class="upload-area__hint">支持 MP4 / MOV,不超过 500MB</div>
|
||||
</div>
|
||||
<div class="form-subtitle">🎬 宣传视频</div>
|
||||
<div class="promo-head">
|
||||
<div class="promo-toggle" onclick="togglePromoVideo(this)">
|
||||
<span class="promo-toggle__switch"></span>
|
||||
<span class="promo-toggle__label">未启用</span>
|
||||
</div>
|
||||
<span style="font-size:var(--font-xs);color:var(--text-placeholder);">控制宣传视频在员工端是否可见</span>
|
||||
</div>
|
||||
<div class="upload-area" onclick="triggerPromoVideoUpload()">
|
||||
<div class="upload-area__icon">⇧</div>
|
||||
<div class="upload-area__text">点击或拖拽上传宣传视频</div>
|
||||
<div class="upload-area__hint">支持 MP4 / MOV,不超过 500MB</div>
|
||||
</div>`,
|
||||
|
||||
// Step 1: 餐品构成
|
||||
// Step 1: 餐品构成(含熟重 + 营养指标)
|
||||
`<div class="form-card__title">🧩 餐品构成</div>
|
||||
<div class="form-grid" style="grid-template-columns:repeat(4,1fr);margin-bottom:8px;">
|
||||
<div class="form-group"><label class="form-label">熟重(g)<span class="required">*</span></label><input class="form-input" type="number" min="0" step="0.1" placeholder="请输入熟重"></div>
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
|
||||
<span style="font-size:var(--font-sm);font-weight:600;">食材清单<span class="required">*</span></span>
|
||||
<button class="btn btn-primary" style="height:30px;font-size:var(--font-xs);" onclick="openIngredientDialog()">➕ 新增食材</button>
|
||||
@@ -420,7 +439,19 @@ var STEP_HTML = [
|
||||
<table class="ingredient-table">
|
||||
<thead><tr><th>食材名称</th><th>净菜类型</th><th>用量(g/份)</th><th>食材类型</th><th>过敏原</th><th>备注</th><th>操作</th></tr></thead>
|
||||
<tbody><tr><td colspan="7" style="text-align:center;color:var(--text-placeholder);padding:32px;">暂无食材,请点击"新增食材"添加</td></tr></tbody>
|
||||
</table>`,
|
||||
</table>
|
||||
<div class="form-subtitle">📊 营养指标</div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group"><label class="form-label">热量 (kcal/100g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">蛋白质 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">碳水化合物 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">脂肪 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">维生素C (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">矿物质 (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">膳食纤维 (g)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">钠 (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">营养等级</label><select class="form-select"><option value="">请选择</option><option value="达标">达标</option><option value="偏差">偏差</option><option value="未达标">未达标</option></select></div>
|
||||
</div>`,
|
||||
|
||||
// Step 2: 烹饪工艺
|
||||
`<div class="form-card__title">🍳 烹饪工艺</div>
|
||||
@@ -436,21 +467,7 @@ var STEP_HTML = [
|
||||
<div class="form-group"><label class="form-label">工艺步骤数</label><input class="form-input" type="number" placeholder="请输入步骤数"></div>
|
||||
</div>`,
|
||||
|
||||
// Step 3: 营养指标
|
||||
`<div class="form-card__title">📊 营养指标</div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group"><label class="form-label">热量 (kcal/100g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">蛋白质 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">碳水化合物 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">脂肪 (g)<span class="required">*</span></label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">维生素C (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">矿物质 (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">膳食纤维 (g)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">钠 (mg)</label><input class="form-input" type="number" placeholder="0.0"></div>
|
||||
<div class="form-group"><label class="form-label">营养等级</label><select class="form-select"><option value="">请选择</option><option value="达标">达标</option><option value="偏差">偏差</option><option value="未达标">未达标</option></select></div>
|
||||
</div>`,
|
||||
|
||||
// Step 4: 味道档案
|
||||
// Step 3: 味道档案
|
||||
`<div class="form-card__title">👅 味道档案</div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group"><label class="form-label">酸度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
|
||||
@@ -463,7 +480,7 @@ var STEP_HTML = [
|
||||
<div class="form-group"><label class="form-label">综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
|
||||
</div>`,
|
||||
|
||||
// Step 5: 口感档案
|
||||
// Step 4: 口感档案
|
||||
`<div class="form-card__title">🫦 口感档案</div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group"><label class="form-label">软硬度 (1-5)<span class="required">*</span></label><input class="form-input" type="number" min="1" max="5" placeholder="1-5"></div>
|
||||
@@ -475,7 +492,7 @@ var STEP_HTML = [
|
||||
<div class="form-group"><label class="form-label">综合评级<span class="required">*</span></label><select class="form-select"><option value="">请选择</option><option value="excellent">优秀</option><option value="good">良好</option><option value="average">一般</option><option value="poor">较差</option><option value="bad">不合格</option></select></div>
|
||||
</div>`,
|
||||
|
||||
// Step 6: 色泽档案
|
||||
// Step 5: 色泽档案
|
||||
`<div class="form-card__title">🎨 色泽档案</div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group"><label class="form-label">主色调<span class="required">*</span></label><input class="form-input" type="text" placeholder="如:深红褐色"></div>
|
||||
@@ -498,7 +515,7 @@ function renderStep(){
|
||||
function lockConvertStep(){
|
||||
var c=document.getElementById('stepContent');
|
||||
c.classList.remove('step-readonly');
|
||||
var LOCKED=[1,3,4,5,6];
|
||||
var LOCKED=[1,3,4,5];
|
||||
if(LOCKED.indexOf(currentStep)===-1) return;
|
||||
c.classList.add('step-readonly');
|
||||
var tip=document.createElement('div');
|
||||
@@ -562,6 +579,12 @@ function toggleCleanSale(el){
|
||||
var label = el.querySelector('.sale-toggle__label');
|
||||
if(label) label.textContent = active ? '上架' : '下架';
|
||||
}
|
||||
/* 宣传视频启用/未启用切换 */
|
||||
function togglePromoVideo(el){
|
||||
var active = el.classList.toggle('active');
|
||||
var label = el.querySelector('.promo-toggle__label');
|
||||
if(label) label.textContent = active ? '已启用' : '未启用';
|
||||
}
|
||||
function cancelSpec(btn){btn.closest('tr').remove();}
|
||||
function toggleDefault(el){
|
||||
var tbody=document.getElementById('specBody');
|
||||
@@ -714,6 +737,7 @@ var EDIT_DATA = {
|
||||
allergen:'无', tag:'高蛋白', status:'上架',
|
||||
crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],
|
||||
specs:[{spec:'标准份',weight:200,price:12,cleanPrice:9,cleanSale:false,isDefault:true},{spec:'小份',weight:120,price:8,cleanPrice:6,cleanSale:false,isDefault:false},{spec:'大份',weight:300,price:18,cleanPrice:13,cleanSale:false,isDefault:false}],
|
||||
cookedWeight:200,
|
||||
composition:[{name:'五花肉',netType:'红烧块',amount:150,pct:'60%',type:'主材',allergen:'无',note:'—'},{name:'冰糖',netType:'糖浆',amount:20,pct:'8%',type:'调料',allergen:'无',note:'上色'},{name:'酱油',netType:'调味汁',amount:15,pct:'6%',type:'调料',allergen:'含大豆',note:'调味'},{name:'料酒',netType:'料酒汁',amount:10,pct:'4%',type:'调料',allergen:'无',note:'去腥'}],
|
||||
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,oil:15,salt:3,temp:100,output:200,steps:6,desc:'1.五花肉切块焯水;2.锅中煎至金黄;3.加冰糖炒色;4.加酱油料酒;5.加水小火炖40分钟;6.大火收汁'},
|
||||
nutri:{calorie:385,protein:18.2,carb:12.4,fat:28.6,vitC:2.1,mineral:45,fiber:1.8,sodium:486},
|
||||
@@ -756,6 +780,7 @@ if(pageMode==='convert' && INNOV){
|
||||
EDIT_DATA.status = '上架';
|
||||
EDIT_DATA.crowd = [];
|
||||
EDIT_DATA.specs = [{spec:'标准份',weight:350,price:15,cleanPrice:10,cleanSale:false,isDefault:true},{spec:'小份',weight:200,price:10,cleanPrice:7,cleanSale:false,isDefault:false}];
|
||||
EDIT_DATA.cookedWeight = INNOV.cookedWeight || 200;
|
||||
EDIT_DATA.composition = INNOV.composition;
|
||||
EDIT_DATA.cook = INNOV.cook;
|
||||
EDIT_DATA.nutri = INNOV.nutri;
|
||||
@@ -804,7 +829,9 @@ function fillEditData(){
|
||||
});
|
||||
}
|
||||
} else if(currentStep===1){
|
||||
// 餐品构成 - 预填食材表格
|
||||
// 餐品构成 - 预填熟重 + 食材表格 + 营养指标
|
||||
// inputs[0] = 熟重;inputs[1..8] = 营养指标 8 项
|
||||
if(inputs[0] && d.cookedWeight!==undefined) inputs[0].value = d.cookedWeight;
|
||||
var tbody = container.querySelector('table tbody');
|
||||
if(tbody){
|
||||
tbody.innerHTML = '';
|
||||
@@ -816,6 +843,11 @@ function fillEditData(){
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
// 营养指标(inputs[1..8])
|
||||
var n = d.nutri;
|
||||
var nvals = [n.calorie, n.protein, n.carb, n.fat, n.vitC, n.mineral, n.fiber, n.sodium];
|
||||
for(var k=0;k<nvals.length;k++){ if(inputs[1+k] && nvals[k]!==undefined) inputs[1+k].value = nvals[k]; }
|
||||
if(selects[0] && INNOV && INNOV.nutriGrade) setSelectByText(selects[0], INNOV.nutriGrade);
|
||||
} else if(currentStep===2){
|
||||
// 烹饪工艺
|
||||
var c = d.cook;
|
||||
@@ -824,24 +856,18 @@ function fillEditData(){
|
||||
var ta = container.querySelector('textarea');
|
||||
if(ta) ta.value = c.desc||'';
|
||||
} else if(currentStep===3){
|
||||
// 营养指标
|
||||
var n = d.nutri;
|
||||
var nvals = [n.calorie, n.protein, n.carb, n.fat, n.vitC, n.mineral, n.fiber, n.sodium];
|
||||
inputs.forEach(function(inp, i){ if(nvals[i]!==undefined) inp.value = nvals[i]; });
|
||||
if(selects[0] && INNOV && INNOV.nutriGrade) setSelectByText(selects[0], INNOV.nutriGrade);
|
||||
} else if(currentStep===4){
|
||||
// 味道档案
|
||||
var t = d.taste;
|
||||
var tvals = [t.sour, t.sweet, t.bitter, t.spicy, t.salty, t.smell, t.flavorScore];
|
||||
inputs.forEach(function(inp, i){ if(tvals[i]!==undefined) inp.value = tvals[i]; });
|
||||
if(selects[0]) setSelectByValue(selects[0], t.flavorGrade);
|
||||
} else if(currentStep===5){
|
||||
} else if(currentStep===4){
|
||||
// 口感档案
|
||||
var tx = d.texture;
|
||||
var txvals = [tx.hardness, tx.sticky, tx.crispy, tx.smooth, tx.elastic, tx.textureScore];
|
||||
inputs.forEach(function(inp, i){ if(txvals[i]!==undefined) inp.value = txvals[i]; });
|
||||
if(selects[0]) setSelectByValue(selects[0], tx.textureGrade);
|
||||
} else if(currentStep===6){
|
||||
} else if(currentStep===5){
|
||||
// 色泽档案
|
||||
var cl = d.color;
|
||||
var cvals = [cl.mainColor, cl.uniformity, cl.gloss, cl.freshness, cl.appearance, cl.colorScore];
|
||||
|
||||
@@ -108,6 +108,9 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.info-cell__value{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
|
||||
.info-section{margin-bottom:20px;}
|
||||
.info-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid #F0F0F0;display:flex;align-items:center;justify-content:space-between;}
|
||||
/* 餐品构成 Tab 顶部标题栏(含编辑按钮,控制整个 Tab 区域) */
|
||||
.composition-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;}
|
||||
.composition-head__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
|
||||
.info-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
|
||||
.info-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
|
||||
.info-table td{padding:10px 12px;border-bottom:1px solid var(--border);color:var(--text-primary);}
|
||||
@@ -299,10 +302,9 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<div class="detail-tab active" onclick="switchDetailTab(0)">🍽️ 基本档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(1)">🧩 餐品构成</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(2)">🍳 烹饪工艺</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(3)">📊 营养指标</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(4)">👅 味道档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(5)">🫦 口感档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(6)">🎨 色泽档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(3)">👅 味道档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(4)">🫦 口感档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(5)">🎨 色泽档案</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-right__body" id="detailBody"></div>
|
||||
@@ -313,7 +315,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<script>
|
||||
// 餐品数据
|
||||
const DISHES = [
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,cleanPrice:9,cleanSale:false,isDefault:true},{name:'小份',weight:120,price:8,cleanPrice:6,cleanSale:false},{name:'大份',weight:300,price:18,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',weightBasePrice:'6.00',weightVipPrice:'5.40',crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,cleanPrice:9,cleanSale:false,isDefault:true},{name:'小份',weight:120,price:8,cleanPrice:6,cleanSale:false},{name:'大份',weight:300,price:18,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',weightBasePrice:'6.00',weightVipPrice:'5.40',promoVideoEnabled:true,crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',cookedWeight:200,
|
||||
composition:[{name:'猪五花肉',netType:'红烧块',amount:160,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'生姜',netType:'姜片',amount:5,pct:'2.5%',state:'净菜',type:'辅材',allergen:'无',note:'—'},{name:'生抽',netType:'调味汁',amount:15,pct:'7.5%',state:'原料',type:'调料',allergen:'含麸质',note:'酱油'},{name:'冰糖',netType:'糖浆',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'上色提味'},{name:'料酒',netType:'料酒汁',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'去腥'}],
|
||||
nutri:{calorie:385,protein:18.2,carb:12.4,fat:28.6,vitC:0.8,mineral:42,fiber:0.3,sodium:486,nutriGrade:'偏差',note:'铁2.4mg、锌1.8mg'},
|
||||
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,oil:15,salt:3,temp:'100→80',output:200,steps:6},
|
||||
@@ -321,7 +323,7 @@ const DISHES = [
|
||||
color:{mainColor:'红褐色',uniformity:4,gloss:4,freshness:4,appearance:4,colorScore:4.0,colorGrade:'良好'},
|
||||
texture:{hardness:2,sticky:3,crispy:1,smooth:4,elastic:3,textureScore:3.6,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{name:'标准份',weight:150,price:6,cleanPrice:4.5,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:4,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'3.00',weightVipPrice:'2.70',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{name:'标准份',weight:150,price:6,cleanPrice:4.5,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:4,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'3.00',weightVipPrice:'2.70',promoVideoEnabled:false,crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',cookedWeight:140,
|
||||
composition:[{name:'时令青菜',netType:'青菜段',amount:120,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'蒜末',netType:'蒜末',amount:5,pct:'3.3%',state:'净菜',type:'调料',allergen:'无',note:'—'},{name:'食用油',netType:'净油',amount:8,pct:'5.3%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'1.3%',state:'原料',type:'调料',allergen:'无',note:'—'}],
|
||||
nutri:{calorie:68,protein:2.1,carb:8.6,fat:3.2,vitC:28.5,mineral:86,fiber:2.4,sodium:186,nutriGrade:'达标',note:'铁1.2mg、钙45mg'},
|
||||
cook:{mainMethod:'炒',subMethod:'—',heat:'大火',duration:5,oil:8,salt:2,temp:'200',output:150,steps:4},
|
||||
@@ -329,7 +331,7 @@ const DISHES = [
|
||||
color:{mainColor:'翠绿色',uniformity:5,gloss:4,freshness:5,appearance:4,colorScore:4.5,colorGrade:'优秀'},
|
||||
texture:{hardness:3,sticky:1,crispy:4,smooth:3,elastic:2,textureScore:3.4,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{name:'标准份',weight:300,price:5,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',weightBasePrice:'1.67',weightVipPrice:'1.50',crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{name:'标准份',weight:300,price:5,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',weightBasePrice:'1.67',weightVipPrice:'1.50',promoVideoEnabled:true,crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',cookedWeight:300,
|
||||
composition:[{name:'番茄',netType:'番茄块',amount:150,pct:'50%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'鸡蛋',netType:'鸡蛋液',amount:60,pct:'20%',state:'原料',type:'主材',allergen:'含蛋类',note:'—'},{name:'食用油',netType:'净油',amount:5,pct:'1.7%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'0.7%',state:'原料',type:'调料',allergen:'无',note:'—'}],
|
||||
nutri:{calorie:45,protein:3.8,carb:4.2,fat:2.1,vitC:18.2,mineral:32,fiber:1.1,sodium:280,nutriGrade:'达标',note:'铁0.8mg、钾210mg'},
|
||||
cook:{mainMethod:'煮',subMethod:'炒',heat:'中火',duration:12,oil:5,salt:2,temp:'100',output:300,steps:5},
|
||||
@@ -337,7 +339,7 @@ const DISHES = [
|
||||
color:{mainColor:'橙红色',uniformity:4,gloss:3,freshness:5,appearance:4,colorScore:4.0,colorGrade:'良好'},
|
||||
texture:{hardness:1,sticky:2,crispy:1,smooth:5,elastic:2,textureScore:3.8,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{name:'标准份',weight:200,price:3,cleanPrice:2,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:2,cleanPrice:1.5,cleanSale:false},{name:'大份',weight:300,price:4,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'1.50',weightVipPrice:'1.35',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{name:'标准份',weight:200,price:3,cleanPrice:2,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:2,cleanPrice:1.5,cleanSale:false},{name:'大份',weight:300,price:4,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'1.50',weightVipPrice:'1.35',promoVideoEnabled:false,crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',cookedWeight:200,
|
||||
composition:[{name:'大米',netType:'熟米饭',amount:100,pct:'100%',state:'原料',type:'主材',allergen:'无',note:'—'}],
|
||||
nutri:{calorie:232,protein:4.8,carb:51.2,fat:0.6,vitC:0,mineral:12,fiber:0.4,sodium:3,nutriGrade:'达标',note:'—'},
|
||||
cook:{mainMethod:'蒸',subMethod:'—',heat:'大火→中火',duration:30,oil:0,salt:0,temp:'100',output:200,steps:2},
|
||||
@@ -345,7 +347,7 @@ const DISHES = [
|
||||
color:{mainColor:'乳白色',uniformity:5,gloss:4,freshness:4,appearance:4,colorScore:4.2,colorGrade:'良好'},
|
||||
texture:{hardness:2,sticky:3,crispy:1,smooth:3,elastic:2,textureScore:3.2,textureGrade:'一般'}
|
||||
},
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{name:'标准份',weight:180,price:14,cleanPrice:10,cleanSale:false,isDefault:true},{name:'大份',weight:280,price:20,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',weightBasePrice:'7.78',weightVipPrice:'7.00',crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{name:'标准份',weight:180,price:14,cleanPrice:10,cleanSale:false,isDefault:true},{name:'大份',weight:280,price:20,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',weightBasePrice:'7.78',weightVipPrice:'7.00',promoVideoEnabled:true,crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',cookedWeight:180,
|
||||
composition:[{name:'猪里脊肉',netType:'里脊丝',amount:120,pct:'66.7%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'淀粉',netType:'淀粉糊',amount:20,pct:'11.1%',state:'原料',type:'辅材',allergen:'无',note:'挂糊'},{name:'白糖',netType:'糖浆',amount:25,pct:'13.9%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'},{name:'食醋',netType:'调味汁',amount:15,pct:'8.3%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'}],
|
||||
nutri:{calorie:420,protein:22.4,carb:38.2,fat:18.6,vitC:1.2,mineral:28,fiber:0.6,sodium:620,nutriGrade:'未达标',note:'铁1.8mg'},
|
||||
cook:{mainMethod:'炸',subMethod:'炒',heat:'大火',duration:15,oil:50,salt:1,temp:'180',output:180,steps:7},
|
||||
@@ -385,7 +387,7 @@ function renderDetail(){
|
||||
const s = document.getElementById('detailStatus');
|
||||
s.textContent = d.status; s.className='tag tag-'+(d.status==='启用'?'green':'gray');
|
||||
const body = document.getElementById('detailBody');
|
||||
const renderers = [renderBasic,renderComposition,renderCook,renderNutri,renderTaste,renderTexture,renderColor];
|
||||
const renderers = [renderBasic,renderComposition,renderCook,renderTaste,renderTexture,renderColor];
|
||||
body.innerHTML = renderers[currentTab](d);
|
||||
}
|
||||
|
||||
@@ -423,13 +425,42 @@ function renderBasic(d){
|
||||
<div class="info-section"><div class="info-section__title">🎬 制作流程</div>
|
||||
<div class="media-grid">
|
||||
<div class="media-card"><div class="media-card__thumb">🎬</div></div>
|
||||
</div></div>`;
|
||||
</div></div>
|
||||
<div class="info-section"><div class="info-section__title">🎬 宣传视频 <span class="promo-status-tag">${d.promoVideoEnabled?'<span class="tag tag-blue">已启用</span>':'<span class="tag tag-gray">未启用</span>'}</span></div>
|
||||
<div class="media-grid">
|
||||
<div class="media-card"><div class="media-card__thumb">🎬</div><div class="media-card__info"><div class="media-card__title">${d.name} 宣传视频</div><div class="media-card__meta">1分30秒 · 48MB · <span class="link">播放</span></div></div></div>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function renderComposition(d){
|
||||
const rows = (d.composition||[]).map(c=>`<tr><td>${c.name}</td><td>${c.netType}</td><td>${c.amount}</td><td>${c.pct}</td><td><span class="tag tag-${c.type==='主材'?'blue':c.type==='调料'?'orange':'gray'}">${c.type||'辅材'}</span></td><td>${c.allergen}</td><td>${c.note}</td></tr>`).join('');
|
||||
return `<div class="info-section"><div class="info-section__title">食材构成(共 ${(d.composition||[]).length} 项) <span><a class="link" href="din-dish-add.html?mode=edit&tab=1">编辑</a></span></div>
|
||||
<table class="info-table"><thead><tr><th>食材名称</th><th>净菜类型</th><th>用量(g/份)</th><th>占比(%)</th><th>食材类型</th><th>过敏原</th><th>备注</th></tr></thead><tbody>${rows}</tbody></table></div>`;
|
||||
return `<div class="composition-head">
|
||||
<div class="composition-head__title">🧩 餐品构成</div>
|
||||
<a class="link" href="din-dish-add.html?mode=edit&tab=1">编辑</a>
|
||||
</div>
|
||||
<div class="info-section"><div class="info-section__title">熟重</div>
|
||||
<div class="info-grid" style="grid-template-columns:repeat(4,1fr);">
|
||||
<div class="info-cell"><div class="info-cell__label">熟重</div><div class="info-cell__value">${(d.cookedWeight!==undefined&&d.cookedWeight!==null)?(d.cookedWeight+'g'):'—'}</div></div>
|
||||
</div></div>
|
||||
<div class="info-section"><div class="info-section__title">食材构成(共 ${(d.composition||[]).length} 项)</div>
|
||||
<table class="info-table"><thead><tr><th>食材名称</th><th>净菜类型</th><th>用量(g/份)</th><th>占比(%)</th><th>食材类型</th><th>过敏原</th><th>备注</th></tr></thead><tbody>${rows}</tbody></table></div>
|
||||
${renderNutriSection(d)}`;
|
||||
}
|
||||
|
||||
function renderNutriSection(d){
|
||||
const n = d.nutri;
|
||||
return `<div class="info-section"><div class="info-section__title">📊 营养指标</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">热量 (kcal/100g)</div><div class="info-cell__value">${n.calorie}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">蛋白质 (g)</div><div class="info-cell__value">${n.protein}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">碳水化合物 (g)</div><div class="info-cell__value">${n.carb}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">脂肪 (g)</div><div class="info-cell__value">${n.fat}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">维生素C (mg)</div><div class="info-cell__value">${n.vitC}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">矿物质 (mg)</div><div class="info-cell__value">${n.mineral}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">膳食纤维 (g)</div><div class="info-cell__value">${n.fiber}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">钠 (mg)</div><div class="info-cell__value">${n.sodium}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">营养等级</div><div class="info-cell__value">${n.nutriGrade==='达标'?'<span class="tag tag-green">达标</span>':n.nutriGrade==='偏差'?'<span class="tag tag-orange">偏差</span>':'<span class="tag tag-red">未达标</span>'}</div></div>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function renderCook(d){
|
||||
@@ -448,26 +479,10 @@ function renderCook(d){
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function renderNutri(d){
|
||||
const n = d.nutri;
|
||||
return `<div class="info-section"><div class="info-section__title">营养指标 <span><a class="link" href="din-dish-add.html?mode=edit&tab=3">编辑</a></span></div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">热量 (kcal/100g)</div><div class="info-cell__value">${n.calorie}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">蛋白质 (g)</div><div class="info-cell__value">${n.protein}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">碳水化合物 (g)</div><div class="info-cell__value">${n.carb}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">脂肪 (g)</div><div class="info-cell__value">${n.fat}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">维生素C (mg)</div><div class="info-cell__value">${n.vitC}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">矿物质 (mg)</div><div class="info-cell__value">${n.mineral}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">膳食纤维 (g)</div><div class="info-cell__value">${n.fiber}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">钠 (mg)</div><div class="info-cell__value">${n.sodium}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">营养等级</div><div class="info-cell__value">${n.nutriGrade==='达标'?'<span class="tag tag-green">达标</span>':n.nutriGrade==='偏差'?'<span class="tag tag-orange">偏差</span>':'<span class="tag tag-red">未达标</span>'}</div></div>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function renderTaste(d){
|
||||
const t = d.taste;
|
||||
const gradeTag = t.flavorGrade==='优秀'?'green':t.flavorGrade==='良好'?'blue':t.flavorGrade==='一般'?'orange':'red';
|
||||
return `<div class="info-section"><div class="info-section__title">味道档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=4">编辑</a></span></div>
|
||||
return `<div class="info-section"><div class="info-section__title">味道档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=3">编辑</a></span></div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">酸度 (1-5)</div><div class="info-cell__value">${t.sour}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">甜度 (1-5)</div><div class="info-cell__value">${t.sweet}</div></div>
|
||||
@@ -498,7 +513,7 @@ function renderColor(d){
|
||||
function renderTexture(d){
|
||||
const t = d.texture;
|
||||
const gradeTag = t.textureGrade==='优秀'?'green':t.textureGrade==='良好'?'blue':t.textureGrade==='一般'?'orange':'red';
|
||||
return `<div class="info-section"><div class="info-section__title">口感档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=6">编辑</a></span></div>
|
||||
return `<div class="info-section"><div class="info-section__title">口感档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=4">编辑</a></span></div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">软硬度 (1-5)</div><div class="info-cell__value">${t.hardness}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">黏糯度 (1-5)</div><div class="info-cell__value">${t.sticky}</div></div>
|
||||
|
||||
@@ -108,6 +108,9 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.info-cell__value{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
|
||||
.info-section{margin-bottom:20px;}
|
||||
.info-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:10px;padding-bottom:6px;border-bottom:1px solid #F0F0F0;display:flex;align-items:center;justify-content:space-between;}
|
||||
/* 餐品构成 Tab 顶部标题栏(含编辑按钮,控制整个 Tab 区域) */
|
||||
.composition-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;}
|
||||
.composition-head__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
|
||||
.info-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
|
||||
.info-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);}
|
||||
.info-table td{padding:10px 12px;border-bottom:1px solid var(--border);color:var(--text-primary);}
|
||||
@@ -323,10 +326,9 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<div class="detail-tab active" onclick="switchDetailTab(0)">🍽️ 基本档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(1)">🧩 餐品构成</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(2)">🍳 烹饪工艺</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(3)">📊 营养指标</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(4)">👅 味道档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(5)">🫦 口感档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(6)">🎨 色泽档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(3)">👅 味道档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(4)">🫦 口感档案</div>
|
||||
<div class="detail-tab" onclick="switchDetailTab(5)">🎨 色泽档案</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-right__body" id="detailBody"></div>
|
||||
@@ -337,7 +339,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<script>
|
||||
// 餐品数据
|
||||
const DISHES = [
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,cleanPrice:9,cleanSale:false,isDefault:true},{name:'小份',weight:120,price:8,cleanPrice:6,cleanSale:false},{name:'大份',weight:300,price:18,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',weightBasePrice:'6.00',weightVipPrice:'5.40',crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',
|
||||
{id:'DISH001',name:'红烧肉',icon:'🥩',category:'荤菜',tags:['高蛋白'],specs:[{name:'标准份',weight:200,price:12,cleanPrice:9,cleanSale:false,isDefault:true},{name:'小份',weight:120,price:8,cleanPrice:6,cleanSale:false},{name:'大份',weight:300,price:18,cleanPrice:13,cleanSale:false}],method:'炖',meal:'午/晚',allergen:'无',status:'启用',source:'手动添加',weightBasePrice:'6.00',weightVipPrice:'5.40',promoVideoEnabled:true,crowd:[{name:'成人',level:'推荐'},{name:'高血脂人群',level:'慎食'},{name:'高血压人群',level:'适量'}],crowdNote:'高血脂人群控制摄入量',cookedWeight:200,
|
||||
composition:[{name:'猪五花肉',netType:'红烧块',amount:160,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'生姜',netType:'姜片',amount:5,pct:'2.5%',state:'净菜',type:'辅材',allergen:'无',note:'—'},{name:'生抽',netType:'调味汁',amount:15,pct:'7.5%',state:'原料',type:'调料',allergen:'含麸质',note:'酱油'},{name:'冰糖',netType:'糖浆',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'上色提味'},{name:'料酒',netType:'料酒汁',amount:10,pct:'5%',state:'原料',type:'调料',allergen:'无',note:'去腥'}],
|
||||
nutri:{calorie:385,protein:18.2,carb:12.4,fat:28.6,vitC:0.8,mineral:42,fiber:0.3,sodium:486,nutriGrade:'偏差',note:'铁2.4mg、锌1.8mg'},
|
||||
cook:{mainMethod:'炖',subMethod:'煎',heat:'中火→小火',duration:45,oil:15,salt:3,temp:'100→80',output:200,steps:6},
|
||||
@@ -345,7 +347,7 @@ const DISHES = [
|
||||
color:{mainColor:'红褐色',uniformity:4,gloss:4,freshness:4,appearance:4,colorScore:4.0,colorGrade:'良好'},
|
||||
texture:{hardness:2,sticky:3,crispy:1,smooth:4,elastic:3,textureScore:3.6,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{name:'标准份',weight:150,price:6,cleanPrice:4.5,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:4,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'3.00',weightVipPrice:'2.70',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',
|
||||
{id:'DISH002',name:'清炒时蔬',icon:'🥬',category:'素菜',tags:['低脂','素食'],specs:[{name:'标准份',weight:150,price:6,cleanPrice:4.5,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:4,cleanPrice:3,cleanSale:false}],method:'炒',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'3.00',weightVipPrice:'2.70',promoVideoEnabled:false,crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'推荐'},{name:'肾结石人群',level:'慎食'}],crowdNote:'肾结石人群慎食(草酸含量高)',cookedWeight:140,
|
||||
composition:[{name:'时令青菜',netType:'青菜段',amount:120,pct:'80%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'蒜末',netType:'蒜末',amount:5,pct:'3.3%',state:'净菜',type:'调料',allergen:'无',note:'—'},{name:'食用油',netType:'净油',amount:8,pct:'5.3%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'1.3%',state:'原料',type:'调料',allergen:'无',note:'—'}],
|
||||
nutri:{calorie:68,protein:2.1,carb:8.6,fat:3.2,vitC:28.5,mineral:86,fiber:2.4,sodium:186,nutriGrade:'达标',note:'铁1.2mg、钙45mg'},
|
||||
cook:{mainMethod:'炒',subMethod:'—',heat:'大火',duration:5,oil:8,salt:2,temp:'200',output:150,steps:4},
|
||||
@@ -353,7 +355,7 @@ const DISHES = [
|
||||
color:{mainColor:'翠绿色',uniformity:5,gloss:4,freshness:5,appearance:4,colorScore:4.5,colorGrade:'优秀'},
|
||||
texture:{hardness:3,sticky:1,crispy:4,smooth:3,elastic:2,textureScore:3.4,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{name:'标准份',weight:300,price:5,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',weightBasePrice:'1.67',weightVipPrice:'1.50',crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',
|
||||
{id:'DISH003',name:'番茄鸡蛋汤',icon:'🍅',category:'汤品',tags:['低卡'],specs:[{name:'标准份',weight:300,price:5,cleanPrice:3.5,cleanSale:false,isDefault:true}],method:'煮',meal:'午/晚',allergen:'含蛋类',status:'启用',source:'系统内置',weightBasePrice:'1.67',weightVipPrice:'1.50',promoVideoEnabled:true,crowd:[{name:'全人群',level:'推荐'},{name:'儿童',level:'推荐'},{name:'老年人',level:'推荐'}],crowdNote:'',cookedWeight:300,
|
||||
composition:[{name:'番茄',netType:'番茄块',amount:150,pct:'50%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'鸡蛋',netType:'鸡蛋液',amount:60,pct:'20%',state:'原料',type:'主材',allergen:'含蛋类',note:'—'},{name:'食用油',netType:'净油',amount:5,pct:'1.7%',state:'原料',type:'调料',allergen:'无',note:'—'},{name:'食盐',netType:'盐粒',amount:2,pct:'0.7%',state:'原料',type:'调料',allergen:'无',note:'—'}],
|
||||
nutri:{calorie:45,protein:3.8,carb:4.2,fat:2.1,vitC:18.2,mineral:32,fiber:1.1,sodium:280,nutriGrade:'达标',note:'铁0.8mg、钾210mg'},
|
||||
cook:{mainMethod:'煮',subMethod:'炒',heat:'中火',duration:12,oil:5,salt:2,temp:'100',output:300,steps:5},
|
||||
@@ -361,7 +363,7 @@ const DISHES = [
|
||||
color:{mainColor:'橙红色',uniformity:4,gloss:3,freshness:5,appearance:4,colorScore:4.0,colorGrade:'良好'},
|
||||
texture:{hardness:1,sticky:2,crispy:1,smooth:5,elastic:2,textureScore:3.8,textureGrade:'良好'}
|
||||
},
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{name:'标准份',weight:200,price:3,cleanPrice:2,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:2,cleanPrice:1.5,cleanSale:false},{name:'大份',weight:300,price:4,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'1.50',weightVipPrice:'1.35',crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',
|
||||
{id:'DISH004',name:'米饭',icon:'🍚',category:'主食',tags:['主食'],specs:[{name:'标准份',weight:200,price:3,cleanPrice:2,cleanSale:false,isDefault:true},{name:'小份',weight:100,price:2,cleanPrice:1.5,cleanSale:false},{name:'大份',weight:300,price:4,cleanPrice:3,cleanSale:false}],method:'蒸',meal:'早/午/晚',allergen:'无',status:'启用',source:'系统内置',weightBasePrice:'1.50',weightVipPrice:'1.35',promoVideoEnabled:false,crowd:[{name:'全人群',level:'推荐'},{name:'糖尿病人群',level:'适量'}],crowdNote:'糖尿病人群注意控制量',cookedWeight:200,
|
||||
composition:[{name:'大米',netType:'熟米饭',amount:100,pct:'100%',state:'原料',type:'主材',allergen:'无',note:'—'}],
|
||||
nutri:{calorie:232,protein:4.8,carb:51.2,fat:0.6,vitC:0,mineral:12,fiber:0.4,sodium:3,nutriGrade:'达标',note:'—'},
|
||||
cook:{mainMethod:'蒸',subMethod:'—',heat:'大火→中火',duration:30,oil:0,salt:0,temp:'100',output:200,steps:2},
|
||||
@@ -369,7 +371,7 @@ const DISHES = [
|
||||
color:{mainColor:'乳白色',uniformity:5,gloss:4,freshness:4,appearance:4,colorScore:4.2,colorGrade:'良好'},
|
||||
texture:{hardness:2,sticky:3,crispy:1,smooth:3,elastic:2,textureScore:3.2,textureGrade:'一般'}
|
||||
},
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{name:'标准份',weight:180,price:14,cleanPrice:10,cleanSale:false,isDefault:true},{name:'大份',weight:280,price:20,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',weightBasePrice:'7.78',weightVipPrice:'7.00',crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',
|
||||
{id:'DISH005',name:'糖醋里脊',icon:'🍖',category:'荤菜',tags:['高糖'],specs:[{name:'标准份',weight:180,price:14,cleanPrice:10,cleanSale:false,isDefault:true},{name:'大份',weight:280,price:20,cleanPrice:14,cleanSale:false}],method:'炸',meal:'午',allergen:'无',status:'停用',source:'味养创新',weightBasePrice:'7.78',weightVipPrice:'7.00',promoVideoEnabled:true,crowd:[{name:'成人',level:'推荐'},{name:'糖尿病人群',level:'慎食'},{name:'高血脂人群',level:'适量'}],crowdNote:'糖尿病人群慎食,含糖量高',cookedWeight:180,
|
||||
composition:[{name:'猪里脊肉',netType:'里脊丝',amount:120,pct:'66.7%',state:'净菜',type:'主材',allergen:'无',note:'—'},{name:'淀粉',netType:'淀粉糊',amount:20,pct:'11.1%',state:'原料',type:'辅材',allergen:'无',note:'挂糊'},{name:'白糖',netType:'糖浆',amount:25,pct:'13.9%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'},{name:'食醋',netType:'调味汁',amount:15,pct:'8.3%',state:'原料',type:'调料',allergen:'无',note:'糖醋汁'}],
|
||||
nutri:{calorie:420,protein:22.4,carb:38.2,fat:18.6,vitC:1.2,mineral:28,fiber:0.6,sodium:620,nutriGrade:'未达标',note:'铁1.8mg'},
|
||||
cook:{mainMethod:'炸',subMethod:'炒',heat:'大火',duration:15,oil:50,salt:1,temp:'180',output:180,steps:7},
|
||||
@@ -409,7 +411,7 @@ function renderDetail(){
|
||||
const s = document.getElementById('detailStatus');
|
||||
s.textContent = d.status; s.className='tag tag-'+(d.status==='启用'?'green':'gray');
|
||||
const body = document.getElementById('detailBody');
|
||||
const renderers = [renderBasic,renderComposition,renderCook,renderNutri,renderTaste,renderTexture,renderColor];
|
||||
const renderers = [renderBasic,renderComposition,renderCook,renderTaste,renderTexture,renderColor];
|
||||
body.innerHTML = renderers[currentTab](d);
|
||||
}
|
||||
|
||||
@@ -448,7 +450,7 @@ function renderBasic(d){
|
||||
<div class="media-grid">
|
||||
<div class="media-card"><div class="media-card__thumb">🎬</div></div>
|
||||
</div></div>
|
||||
<div class="info-section"><div class="info-section__title">🎬 宣传视频</div>
|
||||
<div class="info-section"><div class="info-section__title">🎬 宣传视频 <span class="promo-status-tag">${d.promoVideoEnabled?'<span class="tag tag-blue">已启用</span>':'<span class="tag tag-gray">未启用</span>'}</span></div>
|
||||
<div class="media-grid">
|
||||
<div class="media-card"><div class="media-card__thumb">🎬</div><div class="media-card__info"><div class="media-card__title">${d.name} 宣传视频</div><div class="media-card__meta">1分30秒 · 48MB · <span class="link">播放</span></div></div></div>
|
||||
</div></div>`;
|
||||
@@ -456,8 +458,33 @@ function renderBasic(d){
|
||||
|
||||
function renderComposition(d){
|
||||
const rows = (d.composition||[]).map(c=>`<tr><td>${c.name}</td><td>${c.netType}</td><td>${c.amount}</td><td>${c.pct}</td><td><span class="tag tag-${c.type==='主材'?'blue':c.type==='调料'?'orange':'gray'}">${c.type||'辅材'}</span></td><td>${c.allergen}</td><td>${c.note}</td></tr>`).join('');
|
||||
return `<div class="info-section"><div class="info-section__title">食材构成(共 ${(d.composition||[]).length} 项) <span><a class="link" href="din-dish-add.html?mode=edit&tab=1">编辑</a></span></div>
|
||||
<table class="info-table"><thead><tr><th>食材名称</th><th>净菜类型</th><th>用量(g/份)</th><th>占比(%)</th><th>食材类型</th><th>过敏原</th><th>备注</th></tr></thead><tbody>${rows}</tbody></table></div>`;
|
||||
return `<div class="composition-head">
|
||||
<div class="composition-head__title">🧩 餐品构成</div>
|
||||
<a class="link" href="din-dish-add.html?mode=edit&tab=1">编辑</a>
|
||||
</div>
|
||||
<div class="info-section"><div class="info-section__title">熟重</div>
|
||||
<div class="info-grid" style="grid-template-columns:repeat(4,1fr);">
|
||||
<div class="info-cell"><div class="info-cell__label">熟重</div><div class="info-cell__value">${(d.cookedWeight!==undefined&&d.cookedWeight!==null)?(d.cookedWeight+'g'):'—'}</div></div>
|
||||
</div></div>
|
||||
<div class="info-section"><div class="info-section__title">食材构成(共 ${(d.composition||[]).length} 项)</div>
|
||||
<table class="info-table"><thead><tr><th>食材名称</th><th>净菜类型</th><th>用量(g/份)</th><th>占比(%)</th><th>食材类型</th><th>过敏原</th><th>备注</th></tr></thead><tbody>${rows}</tbody></table></div>
|
||||
${renderNutriSection(d)}`;
|
||||
}
|
||||
|
||||
function renderNutriSection(d){
|
||||
const n = d.nutri;
|
||||
return `<div class="info-section"><div class="info-section__title">📊 营养指标</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">热量 (kcal/100g)</div><div class="info-cell__value">${n.calorie}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">蛋白质 (g)</div><div class="info-cell__value">${n.protein}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">碳水化合物 (g)</div><div class="info-cell__value">${n.carb}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">脂肪 (g)</div><div class="info-cell__value">${n.fat}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">维生素C (mg)</div><div class="info-cell__value">${n.vitC}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">矿物质 (mg)</div><div class="info-cell__value">${n.mineral}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">膳食纤维 (g)</div><div class="info-cell__value">${n.fiber}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">钠 (mg)</div><div class="info-cell__value">${n.sodium}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">营养等级</div><div class="info-cell__value">${n.nutriGrade==='达标'?'<span class="tag tag-green">达标</span>':n.nutriGrade==='偏差'?'<span class="tag tag-orange">偏差</span>':'<span class="tag tag-red">未达标</span>'}</div></div>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function renderCook(d){
|
||||
@@ -476,26 +503,10 @@ function renderCook(d){
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function renderNutri(d){
|
||||
const n = d.nutri;
|
||||
return `<div class="info-section"><div class="info-section__title">营养指标 <span><a class="link" href="din-dish-add.html?mode=edit&tab=3">编辑</a></span></div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">热量 (kcal/100g)</div><div class="info-cell__value">${n.calorie}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">蛋白质 (g)</div><div class="info-cell__value">${n.protein}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">碳水化合物 (g)</div><div class="info-cell__value">${n.carb}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">脂肪 (g)</div><div class="info-cell__value">${n.fat}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">维生素C (mg)</div><div class="info-cell__value">${n.vitC}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">矿物质 (mg)</div><div class="info-cell__value">${n.mineral}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">膳食纤维 (g)</div><div class="info-cell__value">${n.fiber}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">钠 (mg)</div><div class="info-cell__value">${n.sodium}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">营养等级</div><div class="info-cell__value">${n.nutriGrade==='达标'?'<span class="tag tag-green">达标</span>':n.nutriGrade==='偏差'?'<span class="tag tag-orange">偏差</span>':'<span class="tag tag-red">未达标</span>'}</div></div>
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
function renderTaste(d){
|
||||
const t = d.taste;
|
||||
const gradeTag = t.flavorGrade==='优秀'?'green':t.flavorGrade==='良好'?'blue':t.flavorGrade==='一般'?'orange':'red';
|
||||
return `<div class="info-section"><div class="info-section__title">味道档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=4">编辑</a></span></div>
|
||||
return `<div class="info-section"><div class="info-section__title">味道档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=3">编辑</a></span></div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">酸度 (1-5)</div><div class="info-cell__value">${t.sour}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">甜度 (1-5)</div><div class="info-cell__value">${t.sweet}</div></div>
|
||||
@@ -526,7 +537,7 @@ function renderColor(d){
|
||||
function renderTexture(d){
|
||||
const t = d.texture;
|
||||
const gradeTag = t.textureGrade==='优秀'?'green':t.textureGrade==='良好'?'blue':t.textureGrade==='一般'?'orange':'red';
|
||||
return `<div class="info-section"><div class="info-section__title">口感档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=6">编辑</a></span></div>
|
||||
return `<div class="info-section"><div class="info-section__title">口感档案 <span><a class="link" href="din-dish-add.html?mode=edit&tab=4">编辑</a></span></div>
|
||||
<div class="info-grid">
|
||||
<div class="info-cell"><div class="info-cell__label">软硬度 (1-5)</div><div class="info-cell__value">${t.hardness}</div></div>
|
||||
<div class="info-cell"><div class="info-cell__label">黏糯度 (1-5)</div><div class="info-cell__value">${t.sticky}</div></div>
|
||||
|
||||
@@ -64,42 +64,48 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.page-header-bar__title{font-size:var(--font-lg);font-weight:700;color:var(--text-primary);}
|
||||
.page-header-bar__back{color:var(--primary);text-decoration:none;font-size:var(--font-sm);display:flex;align-items:center;gap:4px;}
|
||||
.page-header-bar__back:hover{color:#40a9ff;}
|
||||
/* 顶部账户操作区 */
|
||||
.account-top{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-bottom:16px;}
|
||||
/* 用户信息卡片 */
|
||||
.user-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);display:flex;align-items:center;gap:16px;}
|
||||
.user-card__avatar{width:48px;height:48px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;color:#fff;font-size:20px;font-weight:600;flex-shrink:0;}
|
||||
/* 顶部账户操作区:两栏布局(左用户概要 1 / 右余额充值 2) */
|
||||
.account-top{display:grid;grid-template-columns:1fr 2fr;gap:16px;margin-bottom:16px;align-items:stretch;}
|
||||
/* 用户概要卡片:紧凑纵向布局 */
|
||||
.user-card{background:#fff;border-radius:var(--radius-md);padding:16px 18px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;justify-content:center;gap:12px;}
|
||||
/* 头部行:头像 + 姓名/编号/标签 */
|
||||
.user-card__head{display:flex;align-items:center;gap:12px;}
|
||||
.user-card__avatar{width:44px;height:44px;border-radius:50%;background:var(--primary);display:flex;align-items:center;justify-content:center;color:#fff;font-size:18px;font-weight:600;flex-shrink:0;}
|
||||
.user-card__info{flex:1;min-width:0;}
|
||||
.user-card__name{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
|
||||
.user-card__code{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;}
|
||||
.user-card__tags{display:flex;gap:6px;margin-top:6px;}
|
||||
.user-card__balance{text-align:right;}
|
||||
.user-card__balance-label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.user-card__balance-value{font-size:var(--font-xxl);font-weight:700;color:var(--text-primary);margin-top:4px;}
|
||||
/* 账户设置卡片 */
|
||||
.settings-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);}
|
||||
.settings-card__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:14px;}
|
||||
.settings-row{display:flex;gap:12px;margin-bottom:12px;flex-wrap:wrap;}
|
||||
.settings-item{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0;}
|
||||
.settings-item label{font-size:var(--font-xs);color:var(--text-secondary);}
|
||||
.settings-item select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 24px 0 8px;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 8px center;cursor:pointer;width:100%;}
|
||||
.settings-item select:focus{border-color:var(--primary);}
|
||||
.settings-item .readonly{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);height:32px;display:flex;align-items:center;}
|
||||
/* 充值卡片 */
|
||||
.recharge-card{background:#fff;border-radius:var(--radius-md);padding:20px 24px;box-shadow:var(--shadow-sm);}
|
||||
.recharge-card__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:14px;}
|
||||
.recharge-amount{display:flex;align-items:center;gap:0;margin-bottom:12px;}
|
||||
.recharge-amount .currency{font-size:var(--font-lg);font-weight:600;color:var(--text-primary);margin-right:2px;}
|
||||
.recharge-amount input{border:none;border-bottom:2px solid var(--border);font-size:26px;font-weight:700;outline:none;width:140px;padding:2px 0;}
|
||||
.recharge-amount input:focus{border-bottom-color:var(--primary);}
|
||||
.recharge-form-row{display:flex;gap:8px;margin-bottom:12px;}
|
||||
.recharge-form-item{display:flex;flex-direction:column;gap:4px;flex:1;}
|
||||
/* 底部统计:余额 + 累计消费 并排 */
|
||||
.user-card__stats{display:flex;gap:8px;}
|
||||
.user-card__stat{flex:1;display:flex;flex-direction:column;gap:2px;padding:8px 10px;background:#FAFAFA;border-radius:var(--radius-sm);}
|
||||
.user-card__stat-label{font-size:var(--font-xs);color:var(--text-secondary);}
|
||||
.user-card__stat-value{font-size:var(--font-base);font-weight:600;color:var(--text-primary);}
|
||||
.user-card__stat-value--primary{color:var(--primary);}
|
||||
/* 充值卡片:右侧放大,横向利用空间 */
|
||||
.recharge-card{background:#fff;border-radius:var(--radius-md);padding:16px 20px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:12px;}
|
||||
.recharge-card__head{display:flex;align-items:center;justify-content:space-between;gap:16px;}
|
||||
.recharge-card__title{font-size:var(--font-base);font-weight:600;color:var(--text-primary);flex-shrink:0;}
|
||||
/* 充值前 → 充值后 对比行 */
|
||||
.recharge-card__balance-row{display:flex;align-items:center;gap:10px;background:#FAFAFA;border-radius:var(--radius-sm);padding:6px 12px;flex:1;max-width:fit-content;}
|
||||
.recharge-card__balance-item{display:flex;flex-direction:column;gap:2px;}
|
||||
.recharge-card__balance-item--primary{text-align:right;}
|
||||
.recharge-card__balance-label{font-size:var(--font-xs);color:var(--text-secondary);white-space:nowrap;}
|
||||
.recharge-card__balance-value{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);white-space:nowrap;}
|
||||
.recharge-card__balance-item--primary .recharge-card__balance-value{color:var(--primary);}
|
||||
.recharge-card__arrow{color:var(--text-placeholder);font-size:var(--font-sm);}
|
||||
/* 充值主体区:横向表单 */
|
||||
.recharge-card__body{display:grid;grid-template-columns:1.4fr 1fr 1.4fr auto;gap:12px;align-items:end;}
|
||||
.recharge-form-item{display:flex;flex-direction:column;gap:4px;}
|
||||
.recharge-form-item label{font-size:var(--font-xs);color:var(--text-secondary);}
|
||||
.recharge-form-item input,.recharge-form-item select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);outline:none;width:100%;}
|
||||
.recharge-form-item input,.recharge-form-item select{height:34px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);outline:none;width:100%;}
|
||||
.recharge-form-item select{padding-right:24px;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 8px center;cursor:pointer;}
|
||||
.recharge-form-item input:focus,.recharge-form-item select:focus{border-color:var(--primary);}
|
||||
.recharge-preview{font-size:var(--font-xs);color:var(--text-secondary);margin-bottom:12px;display:none;}
|
||||
.recharge-preview span{color:var(--primary);font-weight:600;}
|
||||
/* 充值金额项(外框 + 内嵌 ¥ 与输入框) */
|
||||
.recharge-form-item--amount .recharge-amount{display:flex;align-items:center;height:34px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;background:#fff;}
|
||||
.recharge-form-item--amount .recharge-amount:focus-within{border-color:var(--primary);}
|
||||
.recharge-form-item--amount .currency{font-size:var(--font-base);font-weight:600;color:var(--text-primary);margin-right:2px;}
|
||||
.recharge-form-item--amount input{border:none;font-size:var(--font-base);font-weight:600;outline:none;width:100%;padding:0;background:transparent;height:34px;}
|
||||
.recharge-card__submit{height:34px;padding:0 20px;font-size:var(--font-sm);align-self:end;}
|
||||
/* 账单列表区 */
|
||||
.bill-section{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
|
||||
.bill-section__header{display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--border);}
|
||||
@@ -215,75 +221,57 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 顶部:三栏布局 -->
|
||||
<!-- 顶部:两栏布局(左用户概要 1 / 右余额充值 2) -->
|
||||
<div class="account-top">
|
||||
<!-- 用户信息 -->
|
||||
<!-- 左侧:用户概要信息(紧凑) -->
|
||||
<div class="user-card">
|
||||
<div class="user-card__avatar" id="userAvatar">张</div>
|
||||
<div class="user-card__info">
|
||||
<div class="user-card__name" id="userName">张伟</div>
|
||||
<div class="user-card__code" id="userCode">EMP001</div>
|
||||
<div class="user-card__tags">
|
||||
<span class="tag tag-red" id="userLevel">钻石</span>
|
||||
<span class="tag tag-green" id="userStatus">正常</span>
|
||||
<div class="user-card__head">
|
||||
<div class="user-card__avatar" id="userAvatar">张</div>
|
||||
<div class="user-card__info">
|
||||
<div class="user-card__name" id="userName">张伟</div>
|
||||
<div class="user-card__code" id="userCode">EMP001</div>
|
||||
<div class="user-card__tags">
|
||||
<span class="tag tag-red" id="userLevel">钻石</span>
|
||||
<span class="tag tag-green" id="userStatus">正常</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-card__balance">
|
||||
<div class="user-card__balance-label">当前余额</div>
|
||||
<div class="user-card__balance-value" id="userBalance">¥256.50</div>
|
||||
<div class="user-card__stats">
|
||||
<div class="user-card__stat">
|
||||
<div class="user-card__stat-label">当前余额</div>
|
||||
<div class="user-card__stat-value user-card__stat-value--primary" id="userBalance">¥256.50</div>
|
||||
</div>
|
||||
<div class="user-card__stat">
|
||||
<div class="user-card__stat-label">累计消费</div>
|
||||
<div class="user-card__stat-value" id="userSpend">¥12,860</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 账户设置 -->
|
||||
<div class="settings-card">
|
||||
<div class="settings-card__title">账户设置</div>
|
||||
<div class="settings-row">
|
||||
<div class="settings-item">
|
||||
<label>会员等级</label>
|
||||
<select id="editLevel" onchange="onLevelChange()">
|
||||
<option value="普通会员">普通会员</option>
|
||||
<option value="银卡会员">银卡会员</option>
|
||||
<option value="金卡会员">金卡会员</option>
|
||||
<option value="钻石会员">钻石会员</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="settings-item">
|
||||
<label>会员状态</label>
|
||||
<select id="editStatus" onchange="onStatusChange()">
|
||||
<option value="正常">正常</option>
|
||||
<option value="休眠">休眠</option>
|
||||
<option value="冻结">冻结</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="settings-item">
|
||||
<label>有效期</label>
|
||||
<input type="date" id="editExpire" style="height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);outline:none;width:100%;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<div class="settings-item">
|
||||
<label>当前积分</label>
|
||||
<div class="readonly" id="editPoints">8,628</div>
|
||||
</div>
|
||||
<div class="settings-item">
|
||||
<label>累计消费</label>
|
||||
<div class="readonly" id="editSpend">¥12,860</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="saveSettings()" style="height:30px;font-size:12px;">保存设置</button>
|
||||
</div>
|
||||
|
||||
<!-- 会员充值 -->
|
||||
<!-- 右侧:余额充值(放大) -->
|
||||
<div class="recharge-card">
|
||||
<div class="recharge-card__title">会员充值</div>
|
||||
<div class="recharge-amount">
|
||||
<span class="currency">¥</span>
|
||||
<input type="number" id="rechargeAmount" placeholder="0.00" min="0.01" step="0.01" oninput="updateRechargePreview()">
|
||||
<div class="recharge-card__head">
|
||||
<div class="recharge-card__title">余额充值</div>
|
||||
<div class="recharge-card__balance-row">
|
||||
<div class="recharge-card__balance-item">
|
||||
<div class="recharge-card__balance-label">充值前</div>
|
||||
<div class="recharge-card__balance-value" id="beforeBalance">¥256.50</div>
|
||||
</div>
|
||||
<div class="recharge-card__arrow">→</div>
|
||||
<div class="recharge-card__balance-item recharge-card__balance-item--primary">
|
||||
<div class="recharge-card__balance-label">充值后</div>
|
||||
<div class="recharge-card__balance-value" id="afterBalance">¥256.50</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="recharge-preview" id="rechargePreview">
|
||||
充值后余额 <span id="previewAfter">¥256.50</span>
|
||||
</div>
|
||||
<div class="recharge-form-row">
|
||||
<div class="recharge-card__body">
|
||||
<div class="recharge-form-item recharge-form-item--amount">
|
||||
<label>充值金额</label>
|
||||
<div class="recharge-amount">
|
||||
<span class="currency">¥</span>
|
||||
<input type="number" id="rechargeAmount" placeholder="0.00" min="0.01" step="0.01" oninput="updateRechargePreview()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="recharge-form-item">
|
||||
<label>支付方式</label>
|
||||
<select id="rechargeMethod">
|
||||
@@ -294,8 +282,8 @@ tbody tr:hover{background:#F8FBFF;}
|
||||
<label>备注</label>
|
||||
<input type="text" id="rechargeRemark" placeholder="选填">
|
||||
</div>
|
||||
<button class="btn btn-primary recharge-card__submit" onclick="confirmRecharge()">确认充值</button>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="confirmRecharge()" style="height:30px;font-size:12px;width:100%;">确认充值</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -429,22 +417,24 @@ function getQueryParam(key) {
|
||||
document.getElementById('userCode').textContent = data.code;
|
||||
document.getElementById('userBalance').textContent = '¥' + data.balance.toFixed(2);
|
||||
|
||||
/* 等级/状态 */
|
||||
var levelColors = {'普通会员':'tag-gray','银卡会员':'tag-blue','金卡会员':'tag-orange','钻石会员':'tag-red'};
|
||||
/* 等级/状态(账户设置已移除,等级文案统一为短称谓:钻石/金卡/银卡/普通) */
|
||||
var shortLevelMap = {'钻石会员':'钻石','金卡会员':'金卡','银卡会员':'银卡','普通会员':'普通'};
|
||||
var levelColors = {'钻石':'tag-red','金卡':'tag-orange','银卡':'tag-blue','普通':'tag-gray'};
|
||||
var shortLevel = shortLevelMap[data.level] || data.level;
|
||||
var levelEl = document.getElementById('userLevel');
|
||||
levelEl.textContent = data.level;
|
||||
levelEl.className = 'tag ' + (levelColors[data.level] || 'tag-gray');
|
||||
levelEl.textContent = shortLevel;
|
||||
levelEl.className = 'tag ' + (levelColors[shortLevel] || 'tag-gray');
|
||||
var statusColors = {'正常':'tag-green','休眠':'tag-gray','冻结':'tag-red','即将到期':'tag-orange'};
|
||||
var statusEl = document.getElementById('userStatus');
|
||||
statusEl.textContent = data.status;
|
||||
statusEl.className = 'tag ' + (statusColors[data.status] || 'tag-green');
|
||||
|
||||
/* 设置表单 */
|
||||
document.getElementById('editLevel').value = data.level;
|
||||
document.getElementById('editStatus').value = data.status;
|
||||
document.getElementById('editPoints').textContent = data.points;
|
||||
document.getElementById('editSpend').textContent = data.spend;
|
||||
if (data.expire) document.getElementById('editExpire').value = data.expire;
|
||||
/* 累计消费指标 */
|
||||
document.getElementById('userSpend').textContent = data.spend;
|
||||
|
||||
/* 充值卡余额对比(初始:充值前 = 充值后 = 当前余额) */
|
||||
document.getElementById('beforeBalance').textContent = '¥' + data.balance.toFixed(2);
|
||||
document.getElementById('afterBalance').textContent = '¥' + data.balance.toFixed(2);
|
||||
|
||||
renderBills();
|
||||
})();
|
||||
@@ -517,38 +507,13 @@ function resetBillFilter() {
|
||||
renderBills();
|
||||
}
|
||||
|
||||
/* ========== 账户设置 ========== */
|
||||
function onLevelChange() {
|
||||
/* 实时更新标签 */
|
||||
}
|
||||
function onStatusChange() {
|
||||
/* 实时更新标签 */
|
||||
}
|
||||
function saveSettings() {
|
||||
var newLevel = document.getElementById('editLevel').value;
|
||||
var newStatus = document.getElementById('editStatus').value;
|
||||
var newExpire = document.getElementById('editExpire').value;
|
||||
var data = window._currentUser.data;
|
||||
data.level = newLevel;
|
||||
data.status = newStatus;
|
||||
data.expire = newExpire;
|
||||
var levelColors = {'普通会员':'tag-gray','银卡会员':'tag-blue','金卡会员':'tag-orange','钻石会员':'tag-red'};
|
||||
var le = document.getElementById('userLevel'); le.textContent = newLevel; le.className = 'tag ' + (levelColors[newLevel]||'tag-gray');
|
||||
var statusColors = {'正常':'tag-green','休眠':'tag-gray','冻结':'tag-red','即将到期':'tag-orange'};
|
||||
var se = document.getElementById('userStatus'); se.textContent = newStatus; se.className = 'tag ' + (statusColors[newStatus]||'tag-green');
|
||||
showToast('账户设置保存成功');
|
||||
}
|
||||
|
||||
/* ========== 手动充值 ========== */
|
||||
function updateRechargePreview() {
|
||||
var amount = parseFloat(document.getElementById('rechargeAmount').value) || 0;
|
||||
var preview = document.getElementById('rechargePreview');
|
||||
if (amount > 0) {
|
||||
preview.style.display = 'block';
|
||||
document.getElementById('previewAfter').textContent = '¥' + (window._currentUser.data.balance + amount).toFixed(2);
|
||||
} else {
|
||||
preview.style.display = 'none';
|
||||
}
|
||||
var balance = window._currentUser.data.balance;
|
||||
var after = amount > 0 ? (balance + amount).toFixed(2) : balance.toFixed(2);
|
||||
document.getElementById('beforeBalance').textContent = '¥' + balance.toFixed(2);
|
||||
document.getElementById('afterBalance').textContent = '¥' + after;
|
||||
}
|
||||
|
||||
function confirmRecharge() {
|
||||
@@ -598,7 +563,9 @@ function submitRecharge() {
|
||||
|
||||
closeRechargeModal();
|
||||
document.getElementById('rechargeAmount').value = '';
|
||||
document.getElementById('rechargePreview').style.display = 'none';
|
||||
/* 重置充值卡余额对比:充值前 = 充值后 = 最新余额 */
|
||||
document.getElementById('beforeBalance').textContent = '¥' + data.balance.toFixed(2);
|
||||
document.getElementById('afterBalance').textContent = '¥' + data.balance.toFixed(2);
|
||||
currentPage = 1;
|
||||
renderBills();
|
||||
showToast('充值成功!已到账 ¥' + amount.toFixed(2));
|
||||
|
||||
@@ -123,6 +123,58 @@ tbody td.td-actions{overflow:visible;}
|
||||
.modal-body{padding:24px;overflow-y:auto;flex:1;}
|
||||
.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid var(--border);}
|
||||
|
||||
/* 编辑会员信息弹窗表单 */
|
||||
.vip-edit-form{display:flex;flex-direction:column;gap:16px;}
|
||||
.vip-edit-form__row{display:flex;gap:16px;}
|
||||
.vip-edit-form__item{display:flex;flex-direction:column;gap:6px;flex:1;min-width:0;}
|
||||
.vip-edit-form__item--full{flex:1;}
|
||||
.vip-edit-form__item label{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.vip-edit-form__item select,
|
||||
.vip-edit-form__item input{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 28px 0 10px;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;width:100%;}
|
||||
.vip-edit-form__item input{padding:0 10px;appearance:auto;background:#fff;cursor:text;}
|
||||
.vip-edit-form__item select:focus,
|
||||
.vip-edit-form__item input:focus{border-color:var(--primary);}
|
||||
.vip-edit-form__readonly{height:36px;display:flex;align-items:center;font-size:var(--font-sm);color:var(--text-primary);font-weight:500;background:#FAFAFA;border-radius:var(--radius-sm);padding:0 10px;}
|
||||
|
||||
/* 批量充值弹窗 */
|
||||
.batch-recharge-modal{width:880px;max-width:94vw;height:80vh;max-height:680px;display:flex;flex-direction:column;}
|
||||
.batch-recharge-modal .modal-body{padding:0;display:flex;flex-direction:column;overflow:hidden;flex:1;}
|
||||
.br-filter{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:12px 20px;border-bottom:1px solid var(--border);background:#FAFAFA;}
|
||||
.br-filter .filter-item{display:flex;align-items:center;gap:6px;}
|
||||
.br-filter .filter-item label{font-size:var(--font-xs);color:var(--text-secondary);white-space:nowrap;}
|
||||
.br-filter .filter-item input,.br-filter .filter-item select{height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);outline:none;background:#fff;min-width:120px;}
|
||||
.br-filter .filter-item select{padding-right:24px;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 8px center;cursor:pointer;}
|
||||
.br-filter .btn{height:30px;padding:0 12px;font-size:var(--font-sm);}
|
||||
.br-table-wrap{flex:1;overflow:auto;padding:0 20px;}
|
||||
.br-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
|
||||
.br-table thead th{position:sticky;top:0;background:#fff;padding:10px 8px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);font-size:var(--font-xs);z-index:1;}
|
||||
.br-table tbody td{padding:10px 8px;border-bottom:1px solid #f5f5f5;color:var(--text-primary);}
|
||||
.br-table tbody tr:hover{background:#F8FBFF;}
|
||||
.br-table input[type="checkbox"]{cursor:pointer;}
|
||||
.br-table .br-balance-before{color:var(--text-secondary);}
|
||||
.br-table .br-balance-after{color:var(--primary);font-weight:600;}
|
||||
.br-table .br-arrow{color:var(--text-placeholder);padding:0 4px;}
|
||||
.br-empty{text-align:center;padding:40px 0;color:var(--text-placeholder);font-size:var(--font-sm);}
|
||||
.br-config{border-top:1px solid var(--border);padding:12px 20px;background:#fff;}
|
||||
.br-config__row{display:grid;grid-template-columns:1.2fr 1fr 1.6fr;gap:12px;margin-bottom:10px;}
|
||||
.br-config__item{display:flex;flex-direction:column;gap:4px;}
|
||||
.br-config__item label{font-size:var(--font-xs);color:var(--text-secondary);}
|
||||
.br-config__item input,.br-config__item select{height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-sm);outline:none;width:100%;}
|
||||
.br-config__item select{padding-right:24px;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 8px center;cursor:pointer;}
|
||||
.br-config__item input:focus,.br-config__item select:focus{border-color:var(--primary);}
|
||||
.br-config__amount{display:flex;align-items:center;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;background:#fff;}
|
||||
.br-config__amount:focus-within{border-color:var(--primary);}
|
||||
.br-config__amount .currency{font-size:var(--font-base);font-weight:600;color:var(--text-primary);margin-right:2px;}
|
||||
.br-config__amount input{border:none;font-size:var(--font-base);font-weight:600;outline:none;width:100%;padding:0;background:transparent;height:32px;}
|
||||
.br-summary{display:flex;align-items:center;justify-content:space-between;background:#E6F7FF;border-radius:var(--radius-sm);padding:8px 12px;font-size:var(--font-sm);}
|
||||
.br-summary__count{color:var(--text-secondary);}
|
||||
.br-summary__count b{color:var(--primary);font-weight:600;margin:0 2px;}
|
||||
.br-summary__total{display:flex;align-items:center;gap:8px;}
|
||||
.br-summary__total-label{color:var(--text-secondary);}
|
||||
.br-summary__total-before{color:var(--text-secondary);}
|
||||
.br-summary__total-arrow{color:var(--text-placeholder);}
|
||||
.br-summary__total-after{color:var(--primary);font-weight:600;font-size:var(--font-base);}
|
||||
|
||||
/* 页面信息栏 */
|
||||
.page-header-bar{display:flex;align-items:center;justify-content:space-between;background:#fff;border-radius:var(--radius-md);padding:16px 24px;margin-bottom:16px;box-shadow:var(--shadow-sm);}
|
||||
.page-header-bar__left{display:flex;align-items:center;gap:12px;}
|
||||
@@ -449,6 +501,7 @@ tbody td.td-actions{overflow:visible;}
|
||||
</div>
|
||||
<div class="action-bar">
|
||||
<button class="btn btn-primary">➕ 新增用户</button>
|
||||
<button class="btn btn-primary" onclick="openBatchRecharge()">💰 批量充值</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>
|
||||
@@ -459,22 +512,22 @@ tbody td.td-actions{overflow:visible;}
|
||||
<div class="table-card__content">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>姓名</th><th>员工/用户编号</th><th>性别</th><th>年龄</th><th>单位部门</th><th>BMI</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>BMI</th><th>活动水平</th><th>人员类别</th><th>饮食禁忌</th><th>状态</th><th>账号余额</th><th>操作</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>张伟</td><td>EMP003</td><td>男</td><td>38</td><td>—</td><td>22.9</td><td>中度活动</td><td><span class="tag tag-orange">会员用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>李娜</td><td>EMP001</td><td>女</td><td>34</td><td>—</td><td>19.8</td><td>轻度活动</td><td><span class="tag tag-orange">会员用户</span></td><td><span class="tag tag-red">高钠</span></td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>王磊</td><td>EMP002</td><td>男</td><td>41</td><td>—</td><td>26.8</td><td>久坐</td><td><span class="tag tag-gray">普通用户</span></td><td><span class="tag tag-red">高脂</span></td><td><span class="tag tag-orange">预警</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>赵敏</td><td>EMP004</td><td>女</td><td>36</td><td>—</td><td>18.8</td><td>轻度活动</td><td><span class="tag tag-orange">会员用户</span></td><td><span class="tag tag-red">高糖</span></td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>陈刚</td><td>EMP005</td><td>男</td><td>29</td><td>—</td><td>21.4</td><td>重度活动</td><td><span class="tag tag-gray">普通用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>刘芳</td><td>EMP006</td><td>女</td><td>32</td><td>—</td><td>20.6</td><td>中度活动</td><td><span class="tag tag-orange">会员用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>孙磊</td><td>EMP007</td><td>男</td><td>45</td><td>—</td><td>24.3</td><td>久坐</td><td><span class="tag tag-gray">普通用户</span></td><td><span class="tag tag-red">高钠</span></td><td><span class="tag tag-orange">预警</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>钱芳</td><td>EMP008</td><td>女</td><td>27</td><td>—</td><td>17.9</td><td>轻度活动</td><td><span class="tag tag-orange">会员用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>张运维</td><td>EMP009</td><td>男</td><td>35</td><td>总部机关/运维部</td><td>23.5</td><td>中度活动</td><td><span class="tag tag-blue">员工</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>李后勤</td><td>EMP010</td><td>男</td><td>42</td><td>总部机关/后勤部</td><td>25.1</td><td>中度活动</td><td><span class="tag tag-blue">员工</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>王保安</td><td>WK003</td><td>男</td><td>48</td><td>—</td><td>24.8</td><td>重度活动</td><td><span class="tag tag-blue">工作人员</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>赵清洁</td><td>WK004</td><td>女</td><td>50</td><td>—</td><td>22.0</td><td>重度活动</td><td><span class="tag tag-blue">工作人员</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>林教授</td><td>EXT001</td><td>男</td><td>55</td><td>外部单位/访客组</td><td>23.8</td><td>轻度活动</td><td><span class="tag tag-purple">外部人员</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>张伟</td><td>13812345601</td><td>EMP003</td><td>男</td><td>38</td><td>—</td><td>22.9</td><td>中度活动</td><td><span class="tag tag-orange">会员用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥256.50</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>李娜</td><td>13912345602</td><td>EMP001</td><td>女</td><td>34</td><td>—</td><td>19.8</td><td>轻度活动</td><td><span class="tag tag-orange">会员用户</span></td><td><span class="tag tag-red">高钠</span></td><td><span class="tag tag-green">正常</span></td><td>¥512.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>王磊</td><td>18812345603</td><td>EMP002</td><td>男</td><td>41</td><td>—</td><td>26.8</td><td>久坐</td><td><span class="tag tag-gray">普通用户</span></td><td><span class="tag tag-red">高脂</span></td><td><span class="tag tag-orange">预警</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>赵敏</td><td>13712345604</td><td>EMP004</td><td>女</td><td>36</td><td>—</td><td>18.8</td><td>轻度活动</td><td><span class="tag tag-orange">会员用户</span></td><td><span class="tag tag-red">高糖</span></td><td><span class="tag tag-green">正常</span></td><td>¥320.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>陈刚</td><td>15812345605</td><td>EMP005</td><td>男</td><td>29</td><td>—</td><td>21.4</td><td>重度活动</td><td><span class="tag tag-gray">普通用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>刘芳</td><td>13612345606</td><td>EMP006</td><td>女</td><td>32</td><td>—</td><td>20.6</td><td>中度活动</td><td><span class="tag tag-orange">会员用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥678.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>孙磊</td><td>18612345607</td><td>EMP007</td><td>男</td><td>45</td><td>—</td><td>24.3</td><td>久坐</td><td><span class="tag tag-gray">普通用户</span></td><td><span class="tag tag-red">高钠</span></td><td><span class="tag tag-orange">预警</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>钱芳</td><td>15912345608</td><td>EMP008</td><td>女</td><td>27</td><td>—</td><td>17.9</td><td>轻度活动</td><td><span class="tag tag-orange">会员用户</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>张运维</td><td>13512345609</td><td>EMP009</td><td>男</td><td>35</td><td>总部机关/运维部</td><td>23.5</td><td>中度活动</td><td><span class="tag tag-blue">员工</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>李后勤</td><td>18712345610</td><td>EMP010</td><td>男</td><td>42</td><td>总部机关/后勤部</td><td>25.1</td><td>中度活动</td><td><span class="tag tag-blue">员工</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>王保安</td><td>15612345611</td><td>WK003</td><td>男</td><td>48</td><td>—</td><td>24.8</td><td>重度活动</td><td><span class="tag tag-blue">工作人员</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>赵清洁</td><td>18912345612</td><td>WK004</td><td>女</td><td>50</td><td>—</td><td>22.0</td><td>重度活动</td><td><span class="tag tag-blue">工作人员</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
<tr><td>林教授</td><td>13312345613</td><td>EXT001</td><td>男</td><td>55</td><td>外部单位/访客组</td><td>23.8</td><td>轻度活动</td><td><span class="tag tag-purple">外部人员</span></td><td>无</td><td><span class="tag tag-green">正常</span></td><td>¥0.00</td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="edit">编辑</a> <a class="link" data-action="account">账户管理</a> <span class="more-wrap"><a class="link" data-action="more">更多 ▾</a><div class="more-tip"><a class="more-tip__item" data-action="recipe">推荐食谱</a><a class="more-tip__item" data-action="face">人脸采集</a><a class="more-tip__item more-tip__item--danger" data-action="delete">删除</a></div></span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -517,15 +570,15 @@ tbody td.td-actions{overflow:visible;}
|
||||
<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><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></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>MB-202401-001</td><td>张伟</td><td>EMP001</td><td><span class="tag tag-red">钻石</span></td><td>8,628</td><td>12,860</td><td>¥1,286</td><td>2024-01-15</td><td>2027-01-14</td><td>2026-04-28 11:32</td><td>¥256.50</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202405-008</td><td>李娜</td><td>EMP002</td><td><span class="tag tag-orange">金卡</span></td><td>3,286</td><td>4,820</td><td>¥682</td><td>2024-05-08</td><td>2027-05-07</td><td>2026-04-28 12:08</td><td>¥512.00</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202508-015</td><td>赵敏</td><td>EMP004</td><td><span class="tag tag-blue">银卡</span></td><td>1,420</td><td>2,180</td><td>¥486</td><td>2025-08-12</td><td>2027-08-11</td><td>2026-04-28 11:30</td><td>¥320.00</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202510-026</td><td>刘芳</td><td>EMP006</td><td><span class="tag tag-blue">银卡</span></td><td>986</td><td>1,520</td><td>¥320</td><td>2025-10-20</td><td>2027-10-19</td><td>2026-04-28 11:48</td><td>¥678.00</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202312-003</td><td>孙磊</td><td>EMP010</td><td><span class="tag tag-orange">金卡</span></td><td>5,120</td><td>7,860</td><td>¥0</td><td>2023-12-08</td><td>2026-12-07</td><td>2026-04-15 12:20</td><td>¥156.00</td><td><span class="tag tag-orange">即将到期</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202402-005</td><td>周明</td><td>EMP008</td><td><span class="tag tag-red">钻石</span></td><td>12,486</td><td>16,820</td><td>¥0</td><td>2024-02-10</td><td>2027-02-09</td><td>2026-03-28 12:15</td><td>¥3,200.00</td><td><span class="tag tag-gray">休眠</span></td><td class="td-actions"><a class="link" data-action="detail">详情</a> <a class="link" data-action="account">账户管理</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202401-001</td><td>张伟</td><td>EMP001</td><td><span class="tag tag-red">钻石</span></td><td>8,628</td><td>12,860</td><td>¥1,286</td><td>2024-01-15</td><td>2027-01-14</td><td>2026-04-28 11:32</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="edit">编辑</a> <a class="link" data-action="detail">详情</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202405-008</td><td>李娜</td><td>EMP002</td><td><span class="tag tag-orange">金卡</span></td><td>3,286</td><td>4,820</td><td>¥682</td><td>2024-05-08</td><td>2027-05-07</td><td>2026-04-28 12:08</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="edit">编辑</a> <a class="link" data-action="detail">详情</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202508-015</td><td>赵敏</td><td>EMP004</td><td><span class="tag tag-blue">银卡</span></td><td>1,420</td><td>2,180</td><td>¥486</td><td>2025-08-12</td><td>2027-08-11</td><td>2026-04-28 11:30</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="edit">编辑</a> <a class="link" data-action="detail">详情</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202510-026</td><td>刘芳</td><td>EMP006</td><td><span class="tag tag-blue">银卡</span></td><td>986</td><td>1,520</td><td>¥320</td><td>2025-10-20</td><td>2027-10-19</td><td>2026-04-28 11:48</td><td><span class="tag tag-green">正常</span></td><td class="td-actions"><a class="link" data-action="edit">编辑</a> <a class="link" data-action="detail">详情</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202312-003</td><td>孙磊</td><td>EMP010</td><td><span class="tag tag-orange">金卡</span></td><td>5,120</td><td>7,860</td><td>¥0</td><td>2023-12-08</td><td>2026-12-07</td><td>2026-04-15 12:20</td><td><span class="tag tag-orange">即将到期</span></td><td class="td-actions"><a class="link" data-action="edit">编辑</a> <a class="link" data-action="detail">详情</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
<tr><td>MB-202402-005</td><td>周明</td><td>EMP008</td><td><span class="tag tag-red">钻石</span></td><td>12,486</td><td>16,820</td><td>¥0</td><td>2024-02-10</td><td>2027-02-09</td><td>2026-03-28 12:15</td><td><span class="tag tag-gray">休眠</span></td><td class="td-actions"><a class="link" data-action="edit">编辑</a> <a class="link" data-action="detail">详情</a> <a class="link" data-action="face">人脸采集</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -736,6 +789,13 @@ const Modal = {
|
||||
close() {
|
||||
const o = document.querySelector('.modal-overlay');
|
||||
if (!o) return;
|
||||
// 清理批量充值弹窗的级联监听
|
||||
if (typeof closeBrCascaderOnOutside === 'function') {
|
||||
document.removeEventListener('click', closeBrCascaderOnOutside);
|
||||
}
|
||||
brCasEl = null;
|
||||
brCasLeftEl = null;
|
||||
brCasRightEl = null;
|
||||
o.classList.add('closing');
|
||||
setTimeout(() => o.remove(), 200);
|
||||
}
|
||||
@@ -983,6 +1043,11 @@ document.querySelectorAll('.tab-panel')[0].querySelectorAll('tbody tr').forEach(
|
||||
editLink.addEventListener('click', () => { showEditUser(row); });
|
||||
}
|
||||
}
|
||||
// 账户管理
|
||||
const accountLink = row.querySelector('[data-action="account"]');
|
||||
if (accountLink) accountLink.addEventListener('click', () => {
|
||||
window.location.href = 'din-user-account.html?name=' + encodeURIComponent(name);
|
||||
});
|
||||
// 更多按钮
|
||||
const moreLink = row.querySelector('[data-action="more"]');
|
||||
if (moreLink) {
|
||||
@@ -1021,19 +1086,377 @@ document.querySelectorAll('.tab-panel')[0].querySelectorAll('tbody tr').forEach(
|
||||
document.querySelectorAll('.tab-panel')[2].querySelectorAll('tbody tr').forEach(row => {
|
||||
var tds = row.querySelectorAll('td');
|
||||
var name = tds[1] ? tds[1].textContent : '';
|
||||
// 编辑
|
||||
var editLink = row.querySelector('[data-action="edit"]');
|
||||
if (editLink) editLink.addEventListener('click', function() { openVipEditModal(row); });
|
||||
// 详情
|
||||
var detailLink = row.querySelector('[data-action="detail"]');
|
||||
if (detailLink) detailLink.addEventListener('click', function() { showUserProfile(name); });
|
||||
// 账户管理
|
||||
var accountLink = row.querySelector('[data-action="account"]');
|
||||
if (accountLink) accountLink.addEventListener('click', function() {
|
||||
window.location.href = 'din-user-account.html?name=' + encodeURIComponent(name);
|
||||
});
|
||||
// 人脸采集
|
||||
var faceLink = row.querySelector('[data-action="face"]');
|
||||
if (faceLink) faceLink.addEventListener('click', function() { showFaceModal(name); });
|
||||
});
|
||||
|
||||
/* ========== 编辑会员信息弹窗(Tab3) ========== */
|
||||
function openVipEditModal(row) {
|
||||
var tds = row.querySelectorAll('td');
|
||||
var name = tds[1].textContent;
|
||||
var cardNo = tds[0].textContent;
|
||||
var levelText = tds[3].querySelector('.tag').textContent;
|
||||
var expireText = tds[8].textContent.trim();
|
||||
var statusText = tds[10].querySelector('.tag').textContent;
|
||||
// 会员等级映射(标签文案 -> select option 值)
|
||||
var levelMap = {'钻石':'钻石会员','金卡':'金卡会员','银卡':'银卡会员','普通':'普通会员'};
|
||||
var levelValue = levelMap[levelText] || '普通会员';
|
||||
|
||||
var html = ''
|
||||
+ '<div class="vip-edit-form">'
|
||||
+ ' <div class="vip-edit-form__row">'
|
||||
+ ' <div class="vip-edit-form__item"><label>会员卡号</label><div class="vip-edit-form__readonly">'+cardNo+'</div></div>'
|
||||
+ ' <div class="vip-edit-form__item"><label>姓名</label><div class="vip-edit-form__readonly">'+name+'</div></div>'
|
||||
+ ' </div>'
|
||||
+ ' <div class="vip-edit-form__row">'
|
||||
+ ' <div class="vip-edit-form__item">'
|
||||
+ ' <label>会员等级</label>'
|
||||
+ ' <select id="vipEditLevel">'
|
||||
+ ' <option value="普通">普通</option><option value="银卡">银卡</option><option value="金卡">金卡</option><option value="钻石">钻石</option>'
|
||||
+ ' </select>'
|
||||
+ ' </div>'
|
||||
+ ' <div class="vip-edit-form__item">'
|
||||
+ ' <label>会员状态</label>'
|
||||
+ ' <select id="vipEditStatus">'
|
||||
+ ' <option value="正常">正常</option><option value="即将到期">即将到期</option><option value="休眠">休眠</option>'
|
||||
+ ' </select>'
|
||||
+ ' </div>'
|
||||
+ ' </div>'
|
||||
+ ' <div class="vip-edit-form__row">'
|
||||
+ ' <div class="vip-edit-form__item vip-edit-form__item--full">'
|
||||
+ ' <label>会员有效期</label>'
|
||||
+ ' <input type="date" id="vipEditExpire">'
|
||||
+ ' </div>'
|
||||
+ ' </div>'
|
||||
+ '</div>';
|
||||
|
||||
var footer = '<button class="btn btn-default" onclick="Modal.close()">取消</button>'
|
||||
+ '<button class="btn btn-primary" onclick="saveVipEdit()">保存</button>';
|
||||
|
||||
Modal.open('编辑会员信息', html, 'vip-edit-modal', footer);
|
||||
|
||||
// 填充初始值
|
||||
document.getElementById('vipEditLevel').value = levelText;
|
||||
document.getElementById('vipEditStatus').value = statusText;
|
||||
if (expireText) {
|
||||
// 兼容 "2027-01-14" 格式
|
||||
document.getElementById('vipEditExpire').value = expireText;
|
||||
}
|
||||
|
||||
// 暂存当前行引用,保存时回写
|
||||
window._vipEditRow = row;
|
||||
}
|
||||
|
||||
function saveVipEdit() {
|
||||
var row = window._vipEditRow;
|
||||
if (!row) { Modal.close(); return; }
|
||||
var tds = row.querySelectorAll('td');
|
||||
var newLevel = document.getElementById('vipEditLevel').value;
|
||||
var newStatus = document.getElementById('vipEditStatus').value;
|
||||
var newExpire = document.getElementById('vipEditExpire').value;
|
||||
|
||||
// 等级标签颜色映射(与原表保持一致)
|
||||
var levelTagClass = {'钻石':'tag-red','金卡':'tag-orange','银卡':'tag-blue','普通':'tag-gray'}[newLevel] || 'tag-gray';
|
||||
var levelEl = tds[3].querySelector('.tag');
|
||||
levelEl.textContent = newLevel;
|
||||
levelEl.className = 'tag ' + levelTagClass;
|
||||
|
||||
// 状态标签颜色映射
|
||||
var statusTagClass = {'正常':'tag-green','即将到期':'tag-orange','休眠':'tag-gray'}[newStatus] || 'tag-green';
|
||||
var statusEl = tds[10].querySelector('.tag');
|
||||
statusEl.textContent = newStatus;
|
||||
statusEl.className = 'tag ' + statusTagClass;
|
||||
|
||||
// 有效期至
|
||||
if (newExpire) tds[8].textContent = newExpire;
|
||||
|
||||
Modal.close();
|
||||
showToast('会员信息已保存');
|
||||
}
|
||||
|
||||
/* ========== 批量充值弹窗 ========== */
|
||||
// 候选用户池(来自 Tab1 个人账户列表)
|
||||
var batchRechargeUsers = [
|
||||
{name:'张伟', phone:'13812345601', code:'EMP003', category:'会员用户', unit:'', dept:'', balance:256.50},
|
||||
{name:'李娜', phone:'13912345602', code:'EMP001', category:'会员用户', unit:'', dept:'', balance:512.00},
|
||||
{name:'王磊', phone:'18812345603', code:'EMP002', category:'普通用户', unit:'', dept:'', balance:0},
|
||||
{name:'赵敏', phone:'13712345604', code:'EMP004', category:'会员用户', unit:'', dept:'', balance:320.00},
|
||||
{name:'陈刚', phone:'15812345605', code:'EMP005', category:'普通用户', unit:'', dept:'', balance:0},
|
||||
{name:'刘芳', phone:'13612345606', code:'EMP006', category:'会员用户', unit:'', dept:'', balance:678.00},
|
||||
{name:'孙磊', phone:'18612345607', code:'EMP007', category:'普通用户', unit:'', dept:'', balance:0},
|
||||
{name:'钱芳', phone:'15912345608', code:'EMP008', category:'会员用户', unit:'', dept:'', balance:0},
|
||||
{name:'张运维', phone:'13512345609', code:'EMP009', category:'员工', unit:'总部机关', dept:'运维部', balance:0},
|
||||
{name:'李后勤', phone:'18712345610', code:'EMP010', category:'员工', unit:'总部机关', dept:'后勤部', balance:0},
|
||||
{name:'王保安', phone:'15612345611', code:'WK003', category:'工作人员', unit:'', dept:'', balance:0},
|
||||
{name:'赵清洁', phone:'18912345612', code:'WK004', category:'工作人员', unit:'', dept:'', balance:0},
|
||||
{name:'林教授', phone:'13312345613', code:'EXT001', category:'外部人员', unit:'外部单位', dept:'访客组', balance:0}
|
||||
];
|
||||
// 选中状态、筛选条件
|
||||
var brSelected = {}; // { code: true }
|
||||
var brFilter = {name:'', code:'', category:'', status:''};
|
||||
|
||||
function openBatchRecharge() {
|
||||
// 重置状态
|
||||
brSelected = {};
|
||||
brFilter = {name:'', code:'', category:'', unit:'', dept:''};
|
||||
// 重置弹窗内级联选择状态
|
||||
brCasSelectedUnit = '';
|
||||
brCasSelectedDept = '';
|
||||
|
||||
var html = ''
|
||||
+ '<div class="br-filter">'
|
||||
+ ' <div class="filter-item"><label>姓名</label><input type="text" id="brFname" placeholder="请输入姓名" oninput="onBrFilterChange()"></div>'
|
||||
+ ' <div class="filter-item"><label>用户编号</label><input type="text" id="brFcode" placeholder="请输入编号" oninput="onBrFilterChange()"></div>'
|
||||
+ ' <div class="filter-item"><label>单位部门</label><div class="cascader" id="brCascader">'
|
||||
+ ' <div class="cascader__trigger" onclick="toggleBrCascader()"><span class="cascader__text">全部</span><span class="cascader__arrow">▾</span></div>'
|
||||
+ ' <div class="cascader__panel"><div class="cascader__col cascader__col--left" id="brCasLeft"></div><div class="cascader__col cascader__col--right" id="brCasRight"></div></div>'
|
||||
+ ' </div></div>'
|
||||
+ ' <div class="filter-item"><label>人员类别</label><select id="brFcat" onchange="onBrFilterChange()">'
|
||||
+ ' <option value="">全部</option><option>会员用户</option><option>普通用户</option><option>员工</option><option>工作人员</option><option>外部人员</option>'
|
||||
+ ' </select></div>'
|
||||
+ ' <button class="btn btn-default" onclick="resetBrFilter()">重置</button>'
|
||||
+ '</div>'
|
||||
+ '<div class="br-table-wrap"><table class="br-table" id="brTable"></table></div>'
|
||||
+ '<div class="br-config">'
|
||||
+ ' <div class="br-config__row">'
|
||||
+ ' <div class="br-config__item">'
|
||||
+ ' <label>充值金额(每人)</label>'
|
||||
+ ' <div class="br-config__amount"><span class="currency">¥</span><input type="number" id="brAmount" placeholder="0.00" min="0.01" step="0.01" oninput="onBrAmountChange()"></div>'
|
||||
+ ' </div>'
|
||||
+ ' <div class="br-config__item">'
|
||||
+ ' <label>支付方式</label>'
|
||||
+ ' <select id="brMethod"><option>微信支付</option><option>现金支付</option><option>银行转账</option><option>支付宝支付</option><option>后台挂账</option></select>'
|
||||
+ ' </div>'
|
||||
+ ' <div class="br-config__item">'
|
||||
+ ' <label>备注</label>'
|
||||
+ ' <input type="text" id="brRemark" placeholder="选填">'
|
||||
+ ' </div>'
|
||||
+ ' </div>'
|
||||
+ ' <div class="br-summary" id="brSummary"></div>'
|
||||
+ '</div>';
|
||||
|
||||
var footer = '<button class="btn btn-default" onclick="Modal.close()">取消</button>'
|
||||
+ '<button class="btn btn-primary" id="brSubmit" onclick="submitBatchRecharge()">确认充值</button>';
|
||||
|
||||
Modal.open('批量充值', html, 'batch-recharge-modal', footer);
|
||||
// 初始化弹窗内级联控件
|
||||
brCasEl = document.getElementById('brCascader');
|
||||
brCasLeftEl = document.getElementById('brCasLeft');
|
||||
brCasRightEl = document.getElementById('brCasRight');
|
||||
renderBrCasLeft();
|
||||
renderBrCasRight();
|
||||
updateBrCasText();
|
||||
// 绑定外部点击关闭
|
||||
document.addEventListener('click', closeBrCascaderOnOutside);
|
||||
renderBrTable();
|
||||
updateBrSummary();
|
||||
}
|
||||
|
||||
/* ========== 弹窗内级联选择器(独立实例,避免与外层冲突) ========== */
|
||||
var brCasEl = null, brCasLeftEl = null, brCasRightEl = null;
|
||||
var brCasSelectedUnit = '', brCasSelectedDept = '';
|
||||
function renderBrCasLeft() {
|
||||
var html = '<div class="cascader__col-item'+(brCasSelectedUnit===''?' active':'')+'" data-val="">全部</div>';
|
||||
Object.keys(CASCADER_DATA).forEach(function(u){
|
||||
html += '<div class="cascader__col-item'+(brCasSelectedUnit===u?' active':'')+'" data-val="'+u+'">'+u+'</div>';
|
||||
});
|
||||
brCasLeftEl.innerHTML = html;
|
||||
brCasLeftEl.querySelectorAll('.cascader__col-item').forEach(function(item){
|
||||
item.onclick = function(e){ e.stopPropagation(); selectBrUnit(this.dataset.val); };
|
||||
});
|
||||
}
|
||||
function renderBrCasRight() {
|
||||
if (!brCasSelectedUnit) { brCasRightEl.innerHTML = ''; return; }
|
||||
var depts = CASCADER_DATA[brCasSelectedUnit] || [];
|
||||
var html = '<div class="cascader__col-item'+(brCasSelectedDept===''?' active':'')+'" data-val="">全部</div>';
|
||||
depts.forEach(function(d){
|
||||
html += '<div class="cascader__col-item'+(brCasSelectedDept===d?' active':'')+'" data-val="'+d+'">'+d+'</div>';
|
||||
});
|
||||
brCasRightEl.innerHTML = html;
|
||||
brCasRightEl.querySelectorAll('.cascader__col-item').forEach(function(item){
|
||||
item.onclick = function(e){ e.stopPropagation(); selectBrDept(this.dataset.val); };
|
||||
});
|
||||
}
|
||||
function selectBrUnit(val) {
|
||||
brCasSelectedUnit = val;
|
||||
brCasSelectedDept = '';
|
||||
brFilter.unit = val;
|
||||
brFilter.dept = '';
|
||||
renderBrCasLeft();
|
||||
renderBrCasRight();
|
||||
if (!val) { updateBrCasText(); closeBrCascader(); renderBrTable(); updateBrSummary(); }
|
||||
}
|
||||
function selectBrDept(val) {
|
||||
brCasSelectedDept = val;
|
||||
brFilter.dept = val;
|
||||
renderBrCasRight();
|
||||
updateBrCasText();
|
||||
closeBrCascader();
|
||||
renderBrTable();
|
||||
updateBrSummary();
|
||||
}
|
||||
function updateBrCasText() {
|
||||
var txt = '全部';
|
||||
if (brCasSelectedUnit && brCasSelectedDept) txt = brCasSelectedUnit + '/' + brCasSelectedDept;
|
||||
else if (brCasSelectedUnit) txt = brCasSelectedUnit;
|
||||
if (brCasEl) brCasEl.querySelector('.cascader__text').textContent = txt;
|
||||
}
|
||||
function toggleBrCascader() {
|
||||
if (!brCasEl) return;
|
||||
var isOpen = brCasEl.classList.contains('open');
|
||||
if (isOpen) { closeBrCascader(); }
|
||||
else { renderBrCasLeft(); renderBrCasRight(); brCasEl.classList.add('open'); }
|
||||
}
|
||||
function closeBrCascader() {
|
||||
if (brCasEl) brCasEl.classList.remove('open');
|
||||
}
|
||||
function closeBrCascaderOnOutside(e) {
|
||||
// 仅在弹窗打开期间生效
|
||||
if (!brCasEl) return;
|
||||
if (!brCasEl.contains(e.target)) closeBrCascader();
|
||||
}
|
||||
|
||||
function getFilteredBrUsers() {
|
||||
return batchRechargeUsers.filter(function(u){
|
||||
if (brFilter.name && u.name.indexOf(brFilter.name) < 0) return false;
|
||||
if (brFilter.code && u.code.toLowerCase().indexOf(brFilter.code.toLowerCase()) < 0) return false;
|
||||
if (brFilter.category && u.category !== brFilter.category) return false;
|
||||
if (brFilter.unit && u.unit !== brFilter.unit) return false;
|
||||
if (brFilter.dept && u.dept !== brFilter.dept) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function onBrFilterChange() {
|
||||
brFilter.name = document.getElementById('brFname').value.trim();
|
||||
brFilter.code = document.getElementById('brFcode').value.trim();
|
||||
brFilter.category = document.getElementById('brFcat').value;
|
||||
renderBrTable();
|
||||
updateBrSummary();
|
||||
}
|
||||
|
||||
function resetBrFilter() {
|
||||
brFilter = {name:'', code:'', category:'', unit:'', dept:''};
|
||||
brCasSelectedUnit = '';
|
||||
brCasSelectedDept = '';
|
||||
document.getElementById('brFname').value = '';
|
||||
document.getElementById('brFcode').value = '';
|
||||
document.getElementById('brFcat').value = '';
|
||||
renderBrCasLeft();
|
||||
renderBrCasRight();
|
||||
updateBrCasText();
|
||||
renderBrTable();
|
||||
updateBrSummary();
|
||||
}
|
||||
|
||||
function renderBrTable() {
|
||||
var list = getFilteredBrUsers();
|
||||
var amount = parseFloat(document.getElementById('brAmount') && document.getElementById('brAmount').value) || 0;
|
||||
var allChecked = list.length > 0 && list.every(function(u){return brSelected[u.code];});
|
||||
|
||||
var thead = '<thead><tr>'
|
||||
+ '<th style="width:36px;"><input type="checkbox" '+(allChecked?'checked':'')+' onchange="onBrSelectAll(this.checked)"></th>'
|
||||
+ '<th>姓名</th><th>手机号</th><th>用户编号</th><th>人员类别</th><th>单位部门</th><th style="width:200px;">充值前后余额</th>'
|
||||
+ '</tr></thead><tbody>';
|
||||
|
||||
var rows = '';
|
||||
list.forEach(function(u){
|
||||
var checked = brSelected[u.code] ? 'checked' : '';
|
||||
var afterBalance = (u.balance + amount).toFixed(2);
|
||||
var balanceCell = '<span class="br-balance-before">¥'+u.balance.toFixed(2)+'</span>'
|
||||
+ '<span class="br-arrow">→</span>'
|
||||
+ '<span class="br-balance-after">¥'+afterBalance+'</span>';
|
||||
var deptText = (u.unit && u.dept) ? (u.unit + '/' + u.dept) : (u.unit || '—');
|
||||
rows += '<tr>'
|
||||
+ '<td><input type="checkbox" '+checked+' onchange="onBrSelectRow(\''+u.code+'\', this.checked)"></td>'
|
||||
+ '<td>'+u.name+'</td>'
|
||||
+ '<td>'+(u.phone || '—')+'</td>'
|
||||
+ '<td>'+u.code+'</td>'
|
||||
+ '<td>'+u.category+'</td>'
|
||||
+ '<td>'+deptText+'</td>'
|
||||
+ '<td>'+balanceCell+'</td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
if (!list.length) {
|
||||
rows = '<tr><td colspan="7" class="br-empty">暂无符合条件的用户</td></tr>';
|
||||
}
|
||||
document.getElementById('brTable').innerHTML = thead + rows + '</tbody>';
|
||||
}
|
||||
|
||||
function onBrSelectAll(checked) {
|
||||
var list = getFilteredBrUsers();
|
||||
list.forEach(function(u){
|
||||
if (checked) brSelected[u.code] = true;
|
||||
else delete brSelected[u.code];
|
||||
});
|
||||
renderBrTable();
|
||||
updateBrSummary();
|
||||
}
|
||||
|
||||
function onBrSelectRow(code, checked) {
|
||||
if (checked) brSelected[code] = true;
|
||||
else delete brSelected[code];
|
||||
renderBrTable();
|
||||
updateBrSummary();
|
||||
}
|
||||
|
||||
function onBrAmountChange() {
|
||||
// 金额变化时同步刷新表格中的"充值后余额"与底部汇总
|
||||
renderBrTable();
|
||||
updateBrSummary();
|
||||
}
|
||||
|
||||
function updateBrSummary() {
|
||||
var amount = parseFloat(document.getElementById('brAmount').value) || 0;
|
||||
var selected = batchRechargeUsers.filter(function(u){return brSelected[u.code];});
|
||||
var count = selected.length;
|
||||
var totalBefore = selected.reduce(function(s, u){return s + u.balance;}, 0);
|
||||
var totalAfter = totalBefore + amount * count;
|
||||
|
||||
var summaryEl = document.getElementById('brSummary');
|
||||
if (!summaryEl) return;
|
||||
if (count === 0) {
|
||||
summaryEl.innerHTML = '<div class="br-summary__count">未选择任何用户</div>';
|
||||
} else {
|
||||
summaryEl.innerHTML = ''
|
||||
+ '<div class="br-summary__count">已选 <b>'+count+'</b> 人,每人充值 <b>¥'+amount.toFixed(2)+'</b></div>'
|
||||
+ '<div class="br-summary__total">'
|
||||
+ ' <span class="br-summary__total-label">合计:</span>'
|
||||
+ ' <span class="br-summary__total-before">¥'+totalBefore.toFixed(2)+'</span>'
|
||||
+ ' <span class="br-summary__total-arrow">→</span>'
|
||||
+ ' <span class="br-summary__total-after">¥'+totalAfter.toFixed(2)+'</span>'
|
||||
+ '</div>';
|
||||
}
|
||||
|
||||
// 提交按钮置灰控制
|
||||
var submitBtn = document.getElementById('brSubmit');
|
||||
if (submitBtn) {
|
||||
var valid = count > 0 && amount > 0;
|
||||
submitBtn.disabled = !valid;
|
||||
submitBtn.style.opacity = valid ? '1' : '0.5';
|
||||
submitBtn.style.cursor = valid ? 'pointer' : 'not-allowed';
|
||||
}
|
||||
}
|
||||
|
||||
function submitBatchRecharge() {
|
||||
var amount = parseFloat(document.getElementById('brAmount').value);
|
||||
if (!amount || amount <= 0) { showToast('请输入有效的充值金额'); return; }
|
||||
var count = batchRechargeUsers.filter(function(u){return brSelected[u.code];}).length;
|
||||
if (count === 0) { showToast('请至少选择一个用户'); return; }
|
||||
var method = document.getElementById('brMethod').value;
|
||||
var totalAdd = (amount * count).toFixed(2);
|
||||
|
||||
Modal.close();
|
||||
showToast('批量充值成功!已为 '+count+' 名用户充值 '+method+' 共 ¥'+totalAdd);
|
||||
}
|
||||
|
||||
/* ========== 人脸采集弹窗 ========== */
|
||||
// 模拟:张伟、李娜有人脸数据,其他人无
|
||||
const faceDataMap = { '张伟': true, '李娜': true };
|
||||
|
||||
@@ -4,8 +4,31 @@
|
||||
|
||||
### 后台营养管理
|
||||
|
||||
- **`din-dish-own.html`**(自有餐品库 · 新增页面):营养用餐侧边栏新增「自有餐品库」菜单项,位于「餐品总库」与「算法标准」之间。页面内容暂复制自 `din-dish-all.html`(餐品总库),后续将做差异化。同步更新全部 26 个营养用餐分组页面的侧边栏导航及 `index.html` SITE_MAP。
|
||||
- **`sup-clean-internal.html`**(净菜内供):备货完成操作由直接修改状态重构为弹窗交互——新增「补全备货信息」弹窗,顶部展示申领单号,必填选择「包装批次号」后方可将所有「备货中」菜品状态置为「待配送」,提示文案「选择包装批次号后,所有备货中的菜品将变为『待配送』」。新增 `_readyClaimNo` 状态变量与 `doClaimReady()` 函数支持校验与状态提交。
|
||||
- **`sup-clean-external.html`**(净菜外售):备货完成操作同步重构为弹窗交互(复用 `readyModal` 弹窗),不再展示菜品行字段,仅展示订单号并必填选择「包装批次号」;新增 `doReadyExt()` 函数按行级「备货中→待配送」状态联动。原「订单号+菜品」复合信息条简化为仅展示订单号。
|
||||
- **`din-dish-own.html`**(自有餐品库 · 新增页面,原 `din-dish-free.html` 重命名):营养用餐侧边栏新增「自有餐品库」菜单项,位于「餐品总库」与「算法标准」之间。页面内容暂复制自 `din-dish-all.html`(餐品总库),后续将做差异化。同步更新全部 26 个营养用餐分组页面的侧边栏导航及 `index.html` SITE_MAP。
|
||||
- **`din-content-center.html`**(内容中心 · 新增页面):营养用餐侧边栏「终端管理」升级为二级可展开菜单组,下设「设备管理」(原 `din-terminal.html`)和「内容中心」(新增)两个三级菜单项。内容中心用于管理投放到终端设备上的物料信息(图片/文字/HTML/视频),支持按设备类型或指定设备配置关联范围,提供物料发布/下架/预览等操作。同步更新全部 27 个营养用餐分组页面的侧边栏导航及 `index.html` SITE_MAP。
|
||||
- **`din-dish-add.html`**(餐品新增/编辑):制作步骤区新增「宣传视频」上传区,与原有「制作视频」并列;支持上传餐品宣传推广用途的独立视频素材,区别于教学类的「制作视频」。
|
||||
- **`din-user-mgmt.html`**(用户管理):会员表格新增「账号余额」列;操作列由「查看 / 编辑」重构为「详情 / 账户管理 / 人脸采集」三个操作,分别对应会员详情、账户管理页与人脸采集入口。
|
||||
- **`din-user-account.html`**(账户管理 · 新增页面):账户管理详情页,采用三列布局——左侧「用户信息卡片」(头像/姓名/工号/会员标签/账户余额),中间「账户设置卡片」(账户状态/会员等级/支付密码/限额配置),右侧「充值卡片」(充值金额/充值方式/赠送金额/实收金额/备注/充值预览)。底部「账单列表区」含筛选栏(资金类型/交易类型/时间范围/流水号)+ 表格(流水号/时间/类型/收支/金额/支付方式/备注)+ 分页组件。新增账号余额管理与全量交易流水查询能力。
|
||||
- **`din-dish-add.html` / `din-dish-own.html` / `din-dish-all.html`**(餐品档案):宣传视频启用控制 + 餐品构成/营养指标结构合并。
|
||||
- **宣传视频启用开关**:新增/编辑页 Step 0「宣传视频」上传区上方新增启用/未启用开关(蓝色胶囊,默认未启用),控制宣传视频在员工端的可见性;自有餐品库与餐品总库详情页「基本档案」Tab 中「宣传视频」section 标题同步展示「已启用/未启用」状态标签(蓝色/灰色,只读)。`din-dish-all.html` 基本档案 Tab 原本缺失「宣传视频」section,本次同步补齐与 own 一致。新增 `.promo-toggle` / `.promo-toggle__switch` / `.promo-toggle__label` 样式、`togglePromoVideo()` 函数、`promoVideoEnabled` 字段。
|
||||
- **餐品构成 + 营养指标合并**:详情页 Tab 数由 7 项缩减为 6 项——移除独立的「📊 营养指标」Tab,将营养指标 info-grid(热量/蛋白质/碳水/脂肪/维C/矿物质/纤维/钠/营养等级)合并至「🧩 餐品构成」Tab 食材清单下方。`renderNutri` 重构为内部 section 渲染函数 `renderNutriSection(d)`,由 `renderComposition` 末尾 `${renderNutriSection(d)}` 调用;`renderers` 数组同步移除 `renderNutri`。
|
||||
- **熟重字段**:新增/编辑页 Step 1「餐品构成」顶部新增「熟重(g)」录入输入框(number 类型,支持小数);详情页「餐品构成」Tab 顶部新增「熟重」只读信息单元(4 列网格)。新增/编辑页 STEP_HTML 中 Step 1 的 inputs 序列调整为 `[0]熟重 + [1..8]营养指标`,`fillEditData` 中 Step 1 分支同步改用 `inputs[0]` 预填熟重、`inputs[1+k]` 预填营养指标 8 项。
|
||||
- **餐品构成 Tab 顶部统一编辑入口**:详情页「餐品构成」Tab 顶部新增独立标题栏 `.composition-head`(左标题"🧩 餐品构成" + 右编辑链接),编辑按钮控制整个 Tab 区域(熟重 + 食材构成 + 营养指标);原「食材构成」section 标题里的「编辑」链接同步移除,避免编辑入口语义重叠。
|
||||
- **步骤序号重排**:新增/编辑页步骤数 `totalSteps` 由 7 调整为 6(基本档案/餐品构成/烹饪工艺/味道档案/口感档案/色泽档案),步骤条、`fillEditData` 中 `currentStep===3/4/5/6` 判断相应改为 `3/4/5`,原 `currentStep===6` 色泽分支合并至 `===5`。
|
||||
- **味养创新转化锁定步骤同步**:`lockConvertStep()` 中 LOCKED 数组由 `[1,3,4,5,6]` 调整为 `[1,3,4,5]`(营养指标已合并入 Step 1,不再独立锁定);`EDIT_DATA` 新增 `cookedWeight` 字段,convert 模式同步赋值 `INNOV.cookedWeight || 200`。
|
||||
- **编辑链接 tab 索引同步**:详情页各 Tab 编辑链接(跳转 `din-dish-add.html?mode=edit&tab=N`)中 tab 索引因步骤数变化同步更新——餐品构成 `tab=1`(不变)、烹饪工艺 `tab=2`(不变)、味道档案 `tab=4→3`、口感档案 `tab=6→4`、色泽档案 `tab=5`(不变,原营养指标 tab=3 已删除)。
|
||||
- **DISHES 数据扩展**:`din-dish-own.html` 与 `din-dish-all.html` 的 5 条餐品示例数据(红烧肉/清炒时蔬/番茄鸡蛋汤/米饭/糖醋里脊)统一新增 `promoVideoEnabled`(boolean)与 `cookedWeight`(number,单位 g)两个字段。
|
||||
|
||||
### 营养管理小程序
|
||||
|
||||
- **`me-home.html`**(我的主页):用户卡片姓名行新增「未认证 / 已认证」人脸标签(橙色/绿色双色徽标),未认证状态点击跳转人脸录入页 `me-face-verify.html`;原「黄金会员」徽标下移至 `user-extra` 行与积分/余额并列展示。
|
||||
- **`me-account.html`**(我的档案):个人信息列表上方新增演示切换栏(黄色背景 + toggle 开关),可切换人脸认证状态用于原型演示;信息列表新增「人脸信息」行——已认证状态显示绿色「已认证」并跳转 `me-face-info.html`,未认证状态显示橙色「未认证」并跳转 `me-face-verify.html`。新增 `isFaceVerified` 状态、`toggleFaceState()` / `updateFaceRow()` / `handleFaceClick()` 函数支持状态联动。
|
||||
- **`me-face-verify.html`**(人脸认证引导/采集 · 新增页面):人脸认证流程页,含认证引导、采集动画与采集结果反馈。
|
||||
- **`me-face-info.html`**(人脸认证信息 · 新增页面):人脸认证信息展示页,展示已绑定的认证资料与重新录入入口。
|
||||
- **`me-wallet.html`**(我的钱包):钱包主页扩展收支明细/账户卡片等内容,强化账户余额与流水入口呈现。
|
||||
- **`me-wallet-detail.html`**(钱包明细详情 · 新增页面):钱包明细详情页,展示单笔收支明细的全字段信息。
|
||||
|
||||
## [2026-07-24]
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
# 智能部下周工作计划(2026年8月3日 — 8月7日)最终版
|
||||
|
||||
> **依据**:(1)7月28日开业筹备第一次碰头会会议报告 + 老徐手记;(2)《智养餐饮平台系统反馈报告》156条业务反馈;(3)8月1日营养管理专项问题梳理;(4)8月2日开业碰头会第二次会议调整意见
|
||||
> **用途**:8月3日(周一)晨会任务下发,每项预留负责人与完成时间,晨会时补充填写
|
||||
|
||||
---
|
||||
|
||||
## 一、P0:最高优先级(阻塞试营业,下周必须攻克)
|
||||
|
||||
> 以下任务为试营业的硬阻塞项,压倒一切,必须在本周内完成或取得决定性进展。
|
||||
|
||||
| 编号 | 任务 | 负责人 | 完成时间 | 说明 | 备注 |
|
||||
|------|------|--------|----------|------|----------|
|
||||
| **P0-1** | **全链路定价体系建设** | | | 🆕 **今日会议最高优先级新增,本周必须攻克**。打通从采购到售卖的全链条价格体系,分六个环节逐级传导——**①毛菜采购价**:实付金额÷验收重量=核算单价(自动计算,不可手动改),同时保留供应商报价单价字段供对比核查;**②毛菜入库价**:核算单价随毛菜入库延续,入库即带价;**③净菜价**:按净菜率从毛菜入库价折算净菜成本单价;**④餐品净菜包成本价**:根据餐品构成中各净菜用量与净菜单价汇总计算;**⑤餐品制作实际成本价**:对接配比秤实际投料数据(主辅料+调料用量+熟重),对比理论成本与实际成本偏差,以实际投料为准更新成本;**⑥餐品最终定价**:经营人员逐菜品设定毛利率→系统自动生成售卖价。先做应急版(跳过净菜中间加价环节,直接毛菜采购价→餐品成本→毛利率→售价,确保能卖饭),后续迭代完善全链条 | 产品设计 |
|
||||
| **P0-2** | **餐品库体系改造** | | 8.3 | 🆕 **整合原P0-2/P1-16/P2-1三条任务,提至最高优先级,周一必须完成**。改造现有单一餐品库为两级架构——**①系统级餐品总库**:包含两类餐品,须在设计上严格区分、不可混用:(a)正规流程餐品——通过系统标准流程维护,具备完整食材构成与配比重量数据,用于餐厅经营、净菜组配、成本核算等全部业务环节;(b)第三方餐品库——来自个人端小程序"自记餐"功能导入的外部餐品数据,缺失餐品构成信息,餐品本身直接设定营养数据(热量/蛋白质/脂肪等),仅供自记餐场景使用,不得混入经营流程。**②餐厅/食堂餐品库**:从系统级总库中选取本餐厅使用的餐品形成子集。餐厅提交餐品净菜包订单时,必须将餐品构成信息(用料清单+配比重量)附加到订单上,净菜加工场组配时按构成信息进行组配。同步完善:分类管理增删改查、餐品名称同组织唯一性校验、搜索食材功能修复。**周一必须完成现有200+菜品录入**(含食材构成+配比重量),食材库缺失的原材料可自己加(营养数据可缺失但重量价格必须有)。徐总带2-3个示范后交王喜团队批量完成 | 产品已补全设计 |
|
||||
| **P0-3** | **支付功能修复与完善** | | | ⬆ **今日会议提级**。两项——(1)联合支付已修复但APP未更新,尽快更新上线;(2)余额支付功能下周内完成开发。先把联合支付修复上线,内部测试版随时升级 | 产品设计/已完成 |
|
||||
| **P0-4** | **结算台问题专项修复** | | | 三项——(1)吐盘机人脸识别成功但餐线不显示打餐数据,导致无法结账;(2)付款后界面仍停留在未支付状态;(3)实际取餐信息与结算台显示不对应 | |
|
||||
| **P0-5** | **营养秤重量失真修复** | | | 打餐重量严重失真(实际100g→系统显示620g),疑为未识别人脸时压秤后补识别导致。需根因分析+修复 | |
|
||||
| **P0-6** | **营养秤人脸识别不稳定修复** | | | 3号营养秤人员识别不上;不同秤之间人脸数据未同步。需逐台验证+修复同步机制 | |
|
||||
| **P0-7** | **试营业模拟问题即刻修复** | | | 员工参与试营业模拟过程中发现的系统问题第一时间修复:小问题当日修复,大问题提至最高优先级处理。小问题不过夜 | |
|
||||
| **P0-8** | **配比秤餐品制作数据全量存储** | | | 配比秤接入后,餐品制作全过程关键数据(主辅料清单及用量、调料清单及用量、餐品熟重)后台必须完整存储。当前菜品构成数据缺失严重,此项是后续更新菜品构成的唯一可靠数据来源,同时为P0-1第⑤环节提供实际成本数据 | 验证 |
|
||||
| **P0-9** | **部署独立开发与测试环境** | | | 现有智养餐饮平台环境作为试营业生产环境,需尽快部署独立的开发、测试环境,与生产环境隔离 | |
|
||||
| **P0-10** | **清理现有环境测试数据** | | | 将当前环境的旧测试数据(菜品定价、用户记录、交易流水、"内部供应商"等)彻底清理,确保试营业数据真实准确 | |
|
||||
| **P0-11** | **组配秤任务逻辑修复** | | | 三项——(1)实际配比4道菜系统显示7道;(2)实际完成4道但显示已完成3道,计数不同步;(3)称重接近目标时未去皮导致实际重量偏小 | |
|
||||
| **P0-12** | **取餐"价格=重量"BUG修复** | | | 🆕 营养管理专项P0。取餐记录中价格等于重量、热量等于重量,缺少乘以单价逻辑,直接影响收费 | |
|
||||
| **P0-13** | **食谱规划/管理菜单位置调整** | | | 🆕 营养管理专项P0。当前菜单顺序反了:应是"营养用餐(定食堂+人数+食谱)→下达生产部门→健康生产(按食谱+人数计算食材用量)",不是现在的"健康生产→食谱规划→营养用餐" | 优先级放后 |
|
||||
| **P0-14** | **53条手动食物数据逐条核实** | | | 🆕 营养管理专项P0。可食部为空、颜色千篇一律、味道/口感写死等数据质量问题,必须逐条核实补全:可食部+营养成分+颜色+味道 | |
|
||||
| **P0-15** | **3429条内置食物数据来源核查启动** | | | 🆕 营养管理专项P0。声称"中国营养学会"但混入肯德基、麦当劳、九珍果汁等商业产品数据,需核查并去除。坚果等基本品类缺失需补全。本周启动 | |
|
||||
| **P0-16** | **毛菜存储增加取用变化记录** | | | 🆕 营养管理专项P0。当前溯源只显示概要("入库3.4公斤,余量2.1公斤,已领用0——少的1.4公斤去哪了?"),需记录完整变化过程:时间+取走多少+取走人 | 优先级放后 |
|
||||
| **P0-17** | **规格/单位字段拆分** | | | 🆕 营养管理专项P0。当前规格与单位混淆("规格都是斤,数量是1"),需拆分为三个字段:规格(克重)+单位(袋/瓶/筐/批)+数量 | 产品设计 |
|
||||
| **P0-18** | **每日经营统计功能开发** | | | 至少实现通过数据库查询每日导出当日经营统计结果(就餐人数、收入、菜品销量等核心指标) | 产品设计 |
|
||||
| | 小程序查看餐线订单 | | | 支持查看餐线订单 | 产品设计 /已完成 |
|
||||
| | 小程序支持人脸录入、授权手机号 | | | 对比人脸信息,关联临时用户 | 产品设计 /已完成 |
|
||||
| | 净菜厨房柜 | | | 厨房净菜柜对接、确认 | |
|
||||
| | 净菜柜、毛菜柜 | | | 确认多台的影响范围 | |
|
||||
| | 小程序屏蔽功能 | | | 订餐、点餐、净菜包购买 | |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 二、P1:高优先级(影响核心业务流程,本周全力推进)
|
||||
|
||||
| 编号 | 任务 | 负责人 | 完成时间 | 说明 | 备注 |
|
||||
|------|------|--------|----------|------|------|
|
||||
| **P1-1** | **后厨补餐屏接入试营业系统** | | | 补餐屏(小米电视)已放置后厨,需完成程序接入并确定安装位置(建议挂厨师附近墙上) | 1 |
|
||||
| **P1-2** | **食谱→净菜→毛菜采购量自动生成** | | | 🆕 王喜团队反馈三大核心需求之一。下食谱后系统自动生成净菜需求量,净菜量自动反算毛菜采购量。已有钱博士提供的算法,王喜先手算验证几次再开发 | 优先级放后 |
|
||||
| **P1-3** | **净菜数据维护配合** | | | 🆕 放开权限给王喜团队维护净菜名称。每个毛菜下挂多种净菜(如青椒→青椒丝/段/块),只维护实际用到的几十到一百种。徐总带2-3个示范后交团队 | 1 |
|
||||
| **P1-4** | **取餐屏APP改造(支持视频播放)** | | | 🆕 黄总团队制作菜品AI生成视频,需改造APP待机画面支持播放视频(从后台下载),支持操作人员随时更换。黄总每天发视频素材,这边对接上线 | 产品设计/已完成 |
|
||||
| **P1-5** | **算力与存储采购推进** | | | 🆕 会上被点名批评"不要再拖了"。大半年前提出按10倍储备采购,至今未执行。让小江尽快确认需求,确认后立即采购。存储和算力价格在上涨 | |
|
||||
| **P1-6** | **结算后营养小票打印功能** | | | 第一阶段:终端侧按既定模板打印结算信息与营养信息 | 产品设计 |
|
||||
| **P1-7** | **大厅排行榜APP接入试营业系统** | | | 排行榜展示APP接入试营业系统环境,显示实时排行数据,带一屏改版数字调整 | 产品设计/已完成 |
|
||||
| **P1-8** | **餐线上方带鱼屏改版** | | | 需求及界面效果已完成,显示内容调整为"菜品价格+热量信息",因屏幕清晰度一般不做复杂展示 | 产品设计/已完成 |
|
||||
| **P1-9** | **毛菜验收秤校验修复** | | | 四项——(1)未全部验收即可提交,缺少必验校验;(2)缺少重量校验导致0重量也可验收;(3)多次拍照建模触发闪退;(4)错误识别菜品重量覆盖其他菜品 | |
|
||||
| **P1-10** | **净菜加工秤数据修复** | | | 两项——(1)毛菜足量但净菜加工后显示"还需0.0千克";(2)清除库存不足任务后仍出现库存不足,后台数据未正常同步 | |
|
||||
| **P1-11** | **毛菜加工时间记录修正** | | | 三项——(1)开始时间为统一提交时间而非首次放置时间;(2)同种蔬菜重复出现两次记录(应合并相同食材);(3)加工秤应只读取主辅材数据 | |
|
||||
| **P1-12** | **取餐管理功能修复** | | | 三项——(1)数据分页选择100行实际只展示30行;(2)用户取餐但未得分;(3)无法通过用户名查询取餐记录 | |
|
||||
| **P1-13** | **后台数据展示修复** | | | 四项——(1)净菜订单数据看板"已完成"恒为0;(2)毛菜入库后后台显示未入库(含存放区域缺失);(3)入库时间显示为非最终入柜时间;(4)毛菜入库排序逻辑正序改倒序 | |
|
||||
| **P1-14** | **环境更新与回退机制建立** | | | 对试营业生产环境的更新操作须慎重,每次更新前做好备份,确保出现问题可回退至上一版本。本周完成方案并持续执行 | |
|
||||
| **P1-15** | **食物成分数据规范** | | | 四项——(1)宏量营养素改为必填项;(2)食材名称增加唯一性校验;(3)净材率去掉最大值校验(干木耳泡发场景); | |
|
||||
| **P1-16** | **各节点补齐关键信息字段** | | | 🆕 营养管理专项P1。每个业务节点统一补齐:时间+操作人+设备编号+重量变化+单价。补齐缺失字段:采购人、入库人/验收人、审核人。本周设计、下周开发 | |
|
||||
| **P1-17** | **食材统一加"国家营养库食材编码"字段** | | | 🆕 营养管理专项P1。为后续数据溯源和权威性验证打基础 | |
|
||||
| **P1-18** | **补全基本品类食材数据** | | | 🆕 营养管理专项P1。坚果等基本品类在3429条内置数据中缺失,需补全 | |
|
||||
| **P1-19** | **库存负值保护** | | | 🆕 营养管理专项P1。取格子时小于等于0一律等于0,程序加保护逻辑 | |
|
||||
| **P1-20** | **页面返回逻辑修复** | | | 🆕 营养管理专项P1。"明明在A页面,返回后跳到B",全局排查修复 | |
|
||||
| **P1-21** | **菜品数据梳理核对** | | | 现有菜品数据(构成、营养信息、定价)多为随手录入,需逐一核对核实,配合菜品成本核算工作。完成首轮核对 | |
|
||||
| **P1-22** | **小程序发布准备** | | | (1)确定小程序名称和图标设计;(2)梳理腾讯小程序上架审核所需资质(如ICP经营许可证等),列出缺失项并启动办理流程。等资质到位后发布正式版 | |
|
||||
|
||||
---
|
||||
|
||||
## 三、P2:中等优先级(提升易用性与规范化)
|
||||
|
||||
| 编号 | 任务 | 负责人 | 完成时间 | 说明 |
|
||||
|------|------|--------|----------|------|
|
||||
| **P2-1** | **采购单来源与规格修正** | | | 采购单来源仅支持手动/内供/外售(去掉食谱搭配来源),规格改为下拉菜单 |
|
||||
| **P2-2** | **净菜加工字段清理** | | | 删除无实际功能的质检结果字段;开始/结束时间清晰命名 |
|
||||
| **P2-3** | **65寸广告机使用方式交接** | | | 将65寸广告机(柱子广告机)的日常操作方式(开关机、内容更新、轮播设置等)交接给餐厅工作人员 |
|
||||
| **P2-4** | **界面优化(序号/折叠/去重)** | | | 🆕 营养管理专项P3提至P2。所有列表统一加序号、左侧菜单支持折叠、入库信息去重显示、"食材溯源"等冗余标题去除 |
|
||||
| **P2-5** | **UI数据字段修正** | | | 🆕 营养管理专项。年龄自动计算(输入身份证号/出生日期→自动算年龄)、毛菜类型修正("面片"标为毛菜需修正)、养殖/种植分类修正("辣椒"标为"养殖"需改为"种植") |
|
||||
| **P2-6** | **视频/图片字段预留** | | | 🆕 营养管理专项。各业务节点预留视频/图片字段 |
|
||||
| **P2-7** | **AI辅助食材用量计算验证** | | | 🆕 输入"食谱+人数"让AI算食材用量,验证可行性 |
|
||||
|
||||
---
|
||||
|
||||
## 四、持续配合事项
|
||||
|
||||
| 编号 | 任务 | 负责人 | 完成时间 | 说明 |
|
||||
|------|------|--------|----------|------|
|
||||
| **C-1** | **配合菜品成本核算** | | | 配合王喜、吕学锋、唐修哲完成200个菜品的成本核算与试营业定价,从后台捋价格,提供系统侧数据支持 |
|
||||
| **C-2** | **每日参加开业筹备例会** | | | 暂定每天10:30召开,汇报技术侧进展与阻塞问题 |
|
||||
| **C-3** | **业务反馈问题持续跟踪** | | | 每日跟进业务反馈清单中"待处理"问题,更新解决状态;当天新反馈的BUG当日响应 |
|
||||
| **C-4** | **餐盘柜接入试营业系统** | | | 优先级较低,其他任务完成后推进 |
|
||||
|
||||
---
|
||||
|
||||
## 五、优先级汇总
|
||||
|
||||
| 优先级 | 任务数 | 任务编号 |
|
||||
|--------|--------|----------|
|
||||
| **P0(最高)** | 18 | P0-1~P0-18 |
|
||||
| **P1(高)** | 22 | P1-1~P1-22 |
|
||||
| **P2(中)** | 7 | P2-1~P2-7 |
|
||||
| **持续配合** | 4 | C-1~C-4 |
|
||||
| **合计** | **51** | — |
|
||||
|
||||
> ⚠️ 本周任务总数51项,其中P0级18项为试营业硬阻塞项,必须全力保障、逐项销号。
|
||||
> **两大头号工程**:P0-1(全链路定价)合并了原五条价格任务,覆盖毛菜采购价→入库价→净菜价→净菜包成本→实际制作成本→最终定价;P0-2(餐品库改造)合并了原三条餐品库任务,建立"系统级总库(正规流程+自记餐第三方)→餐厅餐品库(订单附加构成信息)"两级架构。
|
||||
|
||||
---
|
||||
|
||||
## 六、今日(8月2日)待办
|
||||
|
||||
| # | 事项 | 要求 |
|
||||
|---|------|------|
|
||||
| 1 | **问题全量汇集** | 把昨天会上的问题+今天会上提的所有问题,全部重新汇集整理成清单,今晚发到群里 |
|
||||
| 2 | **大分工+小分工** | 大分工=各模块负责人(抓落实),小分工=具体操作执行人。今天下午叫上杨幂、张传志等相关人员一起梳理 |
|
||||
| 3 | **核心问题重点落实** | 定价问题和餐品库改造是头号紧急事项,必须下周攻克 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## 附:业务反馈问题待处理跟进清单(本周重点销项)
|
||||
|
||||
| 序号 | 模块 | 核心问题 | 状态 | 对应任务 |
|
||||
|------|------|----------|------|----------|
|
||||
| 1 | 结算台 | 吐盘机人脸识别成功但餐线不显示打餐数据 | 待审核 | P0-4 |
|
||||
| 2 | 结算台 | 付款后界面仍停留在未支付状态 | 待审核 | P0-4 |
|
||||
| 3 | 结算台 | 实际取餐信息与结算台显示不对应 | 待审核 | P0-4 |
|
||||
| 4 | 营养秤 | 打餐重量失真(100g→620g) | 待审核 | P0-5 |
|
||||
| 5 | 营养秤 | 人员打餐信息与实际操作人员不符 | 待处理 | P0-6 |
|
||||
| 6 | 营养秤 | 3号秤人员识别不上 | 待处理 | P0-6 |
|
||||
| 7 | 毛菜/净菜货柜 | 放置时间应取最近一次数据 | 待审核 | P1-13 |
|
||||
| 8 | 组配秤 | 去皮缺失/任务计数错误(3条) | 待处理 | P0-11 |
|
||||
| 9 | 毛菜验收秤 | 未验收可提交/0重量验收/拍照闪退/重量覆盖 | 待处理 | P1-9 |
|
||||
| 10 | 净菜加工秤 | 显示错误/库存数据异常 | 待处理 | P1-10 |
|
||||
| 11 | 取餐管理 | 分页bug/未得分/查询失效 | 待处理 | P1-12 |
|
||||
| 12 | 后台-健康生产 | 净菜订单看板统计失效 | 待处理 | P1-13 |
|
||||
| 13 | 营养管理-全链路定价 | 采购单价=手动输入不可靠;入库后无价格;缺少净菜成本/制作成本/自动定价 | 待处理 | **P0-1** |
|
||||
| 14 | 营养管理-餐品库 | 单一餐品库无层级区分;缺少自记餐第三方餐品隔离;订单未附加构成信息 | 待处理 | **P0-2** |
|
||||
| 15 | 营养管理-取餐 | "价格=重量""热量=重量"数据BUG | 待处理 | P0-12 |
|
||||
| 16 | 营养管理-食谱 | 食谱规划→健康生产菜单顺序反了 | 待处理 | P0-13 |
|
||||
| 17 | 营养管理-食物库 | 53条手动数据可食部/颜色/味道随意填写 | 待处理 | P0-14 |
|
||||
| 18 | 营养管理-食物库 | 3429条内置数据混入肯德基/麦当劳等商业数据 | 待处理 | P0-15 |
|
||||
| 19 | 营养管理-溯源 | 毛菜存储缺少取用变化记录(入库3.4→余2.1→领用0,少1.4去向不明) | 待处理 | P0-16 |
|
||||
|
||||
---
|
||||
|
||||
*整理时间:2026年8月2日*
|
||||
*下发时间:2026年8月3日(周一)晨会*
|
||||
Reference in New Issue
Block a user