feat: 称重打包页新增秤重显示、操作列、下一步弹窗流程,全局精简文案

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhangyan
2026-06-16 16:11:51 +08:00
co-authored by Claude Opus 4.6
parent 2465e051dd
commit 801a75d7f3
9 changed files with 1920 additions and 1578 deletions
@@ -0,0 +1,288 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>净菜配比秤 - 登录 | 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #05080f; color: #fff;
min-height: 100vh; min-width: 1280px;
display: flex; align-items: center; justify-content: center;
padding: 20px;
}
/* PAD 设备外壳 */
.device-frame {
width: 1280px; height: 720px;
background: #0e1326;
border-radius: 24px;
box-shadow: 0 24px 64px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04);
overflow: hidden;
position: relative;
display: flex; flex-direction: column;
}
/* 顶部导航栏 */
.top-bar {
display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px;
}
.top-bar__title {
flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85);
}
/* 主内容:登录面板居中 */
.login-main {
display: flex; align-items: center; justify-content: center;
height: calc(100% - 44px); padding: 24px 32px;
}
/* 登录面板 */
.login-panel {
width: 480px; display: flex; flex-direction: column; gap: 20px;
background: #1a2b4a; border-radius: 16px; padding: 32px 40px;
}
.login-panel__title {
font-size: 18px; font-weight: 600; color: rgba(255,255,255,0.85); text-align: center;
}
.login-panel__subtitle {
font-size: 12px; color: rgba(255,255,255,0.4); text-align: center; margin-top: -12px;
}
/* 登录方式切换 Tab */
.login-tabs {
display: flex; gap: 0; border-bottom: 1px solid rgba(255,255,255,0.1);
}
.login-tab {
flex: 1; padding: 12px; text-align: center; font-size: 13px; color: rgba(255,255,255,0.5);
cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.2s;
}
.login-tab:hover { color: rgba(255,255,255,0.8); }
.login-tab.active {
color: #4a90d9; border-bottom-color: #4a90d9;
}
/* 人脸识别区 */
.face-section {
display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 24px 0;
}
.face-section.hidden { display: none; }
.face-frame {
width: 200px; height: 200px; border-radius: 50%;
border: 3px dashed rgba(74,144,217,0.4);
display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 8px;
background: rgba(26,43,74,0.3); cursor: pointer; transition: all 0.3s;
}
.face-frame:hover {
border-color: #4a90d9; background: rgba(74,144,217,0.1);
}
.face-frame__icon { font-size: 44px; color: rgba(255,255,255,0.3); }
.face-frame__text { font-size: 13px; color: rgba(255,255,255,0.4); text-align: center; }
@keyframes ringPulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(74,144,217,0.3); }
50% { box-shadow: 0 0 0 12px rgba(74,144,217,0); }
}
.face-frame.detecting {
border-color: #4a90d9; border-style: solid;
animation: ringPulse 1.5s infinite;
}
.face-frame.success {
border-color: #52c41a; border-style: solid; background: rgba(82,196,26,0.08);
}
.face-info {
text-align: center; min-height: 48px;
background: rgba(0,0,0,0.15); border-radius: 10px; padding: 10px 16px; width: 100%;
}
.face-info__status { font-size: 12px; color: rgba(255,255,255,0.35); display: block; }
.face-info__name { font-size: 15px; font-weight: 700; color: #52c41a; display: none; }
.face-info__name.show { display: block; }
.face-info__detail { font-size: 11px; color: rgba(255,255,255,0.5); margin-top: 4px; display: none; }
.face-info__detail.show { display: block; }
/* 账号密码表单 */
.password-section { display: flex; flex-direction: column; gap: 14px; padding: 16px 0; }
.password-section.hidden { display: none; }
.form-group { display: flex; flex-direction: column; gap: 6px; }
.form-label { font-size: 12px; color: rgba(255,255,255,0.6); }
.form-input {
background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.1);
border-radius: 8px; padding: 12px 14px; font-size: 14px; color: #fff;
outline: none; transition: border-color 0.2s;
}
.form-input:focus { border-color: #4a90d9; }
.form-input::placeholder { color: rgba(255,255,255,0.3); }
.login-error { font-size: 12px; color: #ff4d4f; min-height: 16px; }
.login-btn {
background: #4a90d9; border: none; border-radius: 8px;
padding: 12px; font-size: 14px; font-weight: 600; color: #fff;
cursor: pointer; transition: all 0.2s; margin-top: 8px;
}
.login-btn:hover { background: #5ba0e9; transform: translateY(-1px); }
.login-btn:active { transform: translateY(0); }
.login-hint { font-size: 11px; color: rgba(255,255,255,0.3); text-align: center; }
/* Toast */
.toast {
position: fixed; top: 60px; left: 50%; transform: translateX(-50%) translateY(-20px);
background: rgba(0,0,0,0.85); color: #fff; padding: 12px 28px; border-radius: 8px;
font-size: 13px; z-index: 1000; opacity: 0; visibility: hidden;
transition: opacity 0.3s, transform 0.3s, visibility 0.3s;
white-space: nowrap; pointer-events: none; max-width: 90%;
text-align: center; line-height: 1.5;
}
.toast.show { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.toast--warn { background: rgba(250,173,22,0.92); }
.toast--error { background: rgba(255,77,79,0.92); }
</style>
</head>
<body>
<div class="device-frame">
<div class="top-bar">
<span class="top-bar__title">净菜配比秤 - 员工登录</span>
</div>
<div class="login-main">
<div class="login-panel">
<div class="login-panel__title">员工登录</div>
<div class="login-panel__subtitle">请选择登录方式</div>
<div class="login-tabs">
<div class="login-tab active" data-tab="face" onclick="switchTab('face')">人脸识别</div>
<div class="login-tab" data-tab="password" onclick="switchTab('password')">账号密码</div>
</div>
<!-- 人脸识别区 -->
<div class="face-section" id="faceSection">
<div class="face-frame" id="faceFrame" onclick="startFaceDetect()">
<div class="face-frame__icon" id="faceIcon">&#x1F464;</div>
<div class="face-frame__text" id="faceText">点击激活<br>人脸识别</div>
</div>
<div class="face-info">
<span class="face-info__status" id="faceStatus">识别成功后即可进入功能菜单</span>
<div class="face-info__name" id="faceName"></div>
<div class="face-info__detail" id="faceDetail"></div>
</div>
</div>
<!-- 账号密码表单 -->
<div class="password-section hidden" id="passwordSection">
<div class="form-group">
<label class="form-label">员工工号</label>
<input type="text" class="form-input" id="empId" placeholder="请输入员工工号">
</div>
<div class="form-group">
<label class="form-label">登录密码</label>
<input type="password" class="form-input" id="empPassword" placeholder="请输入登录密码">
</div>
<div class="login-error" id="loginError"></div>
<button class="login-btn" onclick="passwordLogin()">登 录</button>
<div class="login-hint">演示账号:30001 / 123456</div>
</div>
</div>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
var loginState = 'idle';
var currentUser = null;
var loginTab = 'face';
var mockUsers = [
{ name: '刘九', id: 'EMP-20260050', dept: '打包车间', role: '打包员', empId: '30001', password: '123456' },
{ name: '陈十', id: 'EMP-20260055', dept: '打包车间', role: '打包员', empId: '30002', password: '123456' },
{ name: '杨一', id: 'EMP-20260060', dept: '质检部', role: '质检员', empId: '30003', password: '123456' }
];
function switchTab(tab) {
loginTab = tab;
document.querySelectorAll('.login-tab').forEach(function(el) {
el.classList.toggle('active', el.dataset.tab === tab);
});
document.getElementById('faceSection').classList.toggle('hidden', tab !== 'face');
document.getElementById('passwordSection').classList.toggle('hidden', tab !== 'password');
document.getElementById('loginError').textContent = '';
}
function startFaceDetect() {
if (loginState !== 'idle') return;
loginState = 'detecting';
var frame = document.getElementById('faceFrame');
var icon = document.getElementById('faceIcon');
var text = document.getElementById('faceText');
frame.classList.add('detecting');
icon.innerHTML = '&#x1F50D;';
text.textContent = '识别中...';
showToast('正在识别人脸...', 1500);
setTimeout(function() {
var user = mockUsers[Math.floor(Math.random() * mockUsers.length)];
loginSuccess(user);
}, 2000);
}
function passwordLogin() {
var empId = document.getElementById('empId').value.trim();
var password = document.getElementById('empPassword').value.trim();
var errorEl = document.getElementById('loginError');
if (!empId || !password) { errorEl.textContent = '请输入工号和密码'; return; }
var user = null;
for (var i = 0; i < mockUsers.length; i++) {
var u = mockUsers[i];
if (u.empId === empId && u.password === password) {
user = u; break;
}
}
if (!user) { errorEl.textContent = '工号或密码错误,请重试'; return; }
loginSuccess(user);
}
function loginSuccess(user) {
currentUser = user;
loginState = 'success';
var frame = document.getElementById('faceFrame');
var icon = document.getElementById('faceIcon');
var text = document.getElementById('faceText');
frame.classList.remove('detecting');
frame.classList.add('success');
icon.innerHTML = '&#x2705;';
text.innerHTML = user.name;
document.getElementById('faceStatus').style.display = 'none';
var nameEl = document.getElementById('faceName');
nameEl.textContent = user.name + ' · ' + user.role;
nameEl.classList.add('show');
var detailEl = document.getElementById('faceDetail');
detailEl.textContent = user.dept + ' · ' + user.id;
detailEl.classList.add('show');
showToast('欢迎,' + user.name + ',正在进入功能菜单...', 1500);
setTimeout(function() {
window.location.href = '00-welcome.html?user=' + encodeURIComponent(user.name) + '&uid=' + user.id;
}, 1500);
}
function showToast(msg, duration, type) {
var el = document.getElementById('toast');
el.textContent = msg;
el.className = 'toast';
if (type === 'warn') el.classList.add('toast--warn');
if (type === 'error') el.classList.add('toast--error');
el.classList.add('show');
clearTimeout(el._timer);
el._timer = setTimeout(function() { el.classList.remove('show'); }, duration || 2000);
}
</script>
</body>
</html>
@@ -3,217 +3,209 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>净菜配比秤 - 欢迎 | 蚁熊智能餐养平台</title>
<title>净菜配比秤 - 功能菜单 | 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif; background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden; }
.top-bar { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.back-btn { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); text-decoration: none; transition: color 0.2s; }
.back-btn:hover { color: #fff; }
.top-bar__title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.welcome-main { display: flex; align-items: stretch; height: calc(100% - 44px); padding: 24px 32px; gap: 32px; }
.login-panel { flex: 0 0 420px; display: flex; flex-direction: column; gap: 20px; background: #1a2b4a; border-radius: 16px; padding: 24px; }
.login-panel__title { font-size: 16px; font-weight: 600; color: rgba(255,255,255,0.8); text-align: center; }
.login-tabs { display: flex; gap: 0; border-bottom: 1px solid rgba(255,255,255,0.1); }
.login-tab { flex: 1; padding: 10px; text-align: center; font-size: 13px; color: rgba(255,255,255,0.5); cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.2s; }
.login-tab:hover { color: rgba(255,255,255,0.8); }
.login-tab.active { color: #4a90d9; border-bottom-color: #4a90d9; }
.face-section { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 20px 0; }
.face-section.hidden { display: none; }
.face-frame { width: 180px; height: 180px; border-radius: 50%; border: 3px dashed rgba(74,144,217,0.4); display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 8px; position: relative; background: rgba(26,43,74,0.3); cursor: pointer; transition: all 0.3s; }
.face-frame:hover { border-color: #4a90d9; background: rgba(74,144,217,0.1); }
.face-frame__icon { font-size: 40px; color: rgba(255,255,255,0.3); }
.face-frame__text { font-size: 12px; color: rgba(255,255,255,0.4); text-align: center; }
@keyframes ringPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(74,144,217,0.3); } 50% { box-shadow: 0 0 0 12px rgba(74,144,217,0); } }
.face-frame.detecting { border-color: #4a90d9; border-style: solid; animation: ringPulse 1.5s infinite; }
.face-frame.success { border-color: #52c41a; border-style: solid; background: rgba(82,196,26,0.08); }
.face-info { text-align: center; min-height: 48px; background: rgba(0,0,0,0.15); border-radius: 10px; padding: 10px 16px; width: 100%; }
.face-info__status { font-size: 12px; color: rgba(255,255,255,0.35); display: block; }
.face-info__name { font-size: 15px; font-weight: 700; color: #52c41a; display: none; }
.face-info__name.show { display: block; }
.face-info__detail { font-size: 11px; color: rgba(255,255,255,0.5); margin-top: 4px; display: none; }
.face-info__detail.show { display: block; }
.password-section { display: flex; flex-direction: column; gap: 14px; padding: 16px 0; }
.password-section.hidden { display: none; }
.form-group { display: flex; flex-direction: column; gap: 6px; }
.form-label { font-size: 12px; color: rgba(255,255,255,0.6); }
.form-input { background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; padding: 12px 14px; font-size: 14px; color: #fff; outline: none; transition: border-color 0.2s; }
.form-input:focus { border-color: #4a90d9; }
.form-input::placeholder { color: rgba(255,255,255,0.3); }
.login-btn { background: #4a90d9; border: none; border-radius: 8px; padding: 12px; font-size: 14px; font-weight: 600; color: #fff; cursor: pointer; transition: all 0.2s; margin-top: 8px; }
.login-btn:hover { background: #5ba0e9; transform: translateY(-1px); }
.menu-panel { flex: 1; display: flex; flex-direction: column; justify-content: center; gap: 14px; }
.menu-panel__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.menu-panel__title { font-size: 14px; color: rgba(255,255,255,0.4); letter-spacing: 1px; }
.menu-panel__user { font-size: 12px; color: rgba(255,255,255,0.5); }
.menu-panel__user.show { display: block; }
.menu-panel__user.hidden { display: none; }
.menu-btn { display: flex; align-items: center; gap: 14px; padding: 16px 20px; border-radius: 12px; background: #1a2b4a; border: 1px solid rgba(255,255,255,0.06); cursor: pointer; transition: all 0.2s; text-decoration: none; color: #fff; }
.menu-btn:hover { transform: translateX(4px); border-color: rgba(74,144,217,0.3); box-shadow: 0 4px 16px rgba(0,0,0,0.2); }
body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #05080f; color: #fff;
min-height: 100vh; min-width: 1280px;
display: flex; align-items: center; justify-content: center;
padding: 20px;
}
/* PAD 设备外壳 */
.device-frame {
width: 1280px; height: 720px;
background: #0e1326;
border-radius: 24px;
box-shadow: 0 24px 64px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04);
overflow: hidden;
position: relative;
display: flex; flex-direction: column;
}
/* 顶部导航栏 */
.top-bar {
display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px;
}
.top-bar__title {
flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85);
}
.top-bar__user {
font-size: 12px; color: rgba(255,255,255,0.5);
}
.logout-btn {
font-size: 12px; color: rgba(255,255,255,0.5); cursor: pointer;
padding: 4px 12px; border-radius: 4px; transition: all 0.2s;
border: 1px solid rgba(255,255,255,0.15); background: none; margin-left: 10px;
}
.logout-btn:hover { color: #fff; border-color: rgba(255,255,255,0.3); }
/* 主内容:菜单按钮列居中 */
.menu-main {
display: flex; align-items: center; justify-content: center;
height: calc(100% - 44px); padding: 24px 32px;
}
.menu-panel {
width: 560px; display: flex; flex-direction: column; gap: 14px;
}
.menu-panel__header {
display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;
}
.menu-panel__title {
font-size: 14px; color: rgba(255,255,255,0.4); letter-spacing: 1px;
}
.menu-panel__user {
font-size: 12px; color: rgba(255,255,255,0.5);
}
.menu-btn {
display: flex; align-items: center; gap: 14px;
padding: 16px 20px; border-radius: 12px;
background: #1a2b4a; border: 1px solid rgba(255,255,255,0.06);
cursor: pointer; transition: all 0.2s; text-decoration: none; color: #fff;
position: relative;
}
.menu-btn:hover {
transform: translateX(4px); border-color: rgba(74,144,217,0.3);
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
}
.menu-btn:active { transform: translateX(2px) scale(0.99); }
.menu-btn.disabled { opacity: 0.4; cursor: not-allowed; transform: none !important; box-shadow: none !important; border-color: transparent !important; }
.menu-btn__icon { width: 44px; height: 44px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 22px; flex-shrink: 0; }
.menu-btn__icon {
width: 44px; height: 44px; border-radius: 10px;
display: flex; align-items: center; justify-content: center;
font-size: 22px; flex-shrink: 0;
}
.menu-btn__icon--blue { background: rgba(74,144,217,0.15); }
.menu-btn__icon--green { background: rgba(82,196,26,0.15); }
.menu-btn__icon--orange { background: rgba(250,173,22,0.15); }
.menu-btn__icon--purple { background: rgba(114,46,209,0.15); }
.menu-btn__icon--gray { background: rgba(255,255,255,0.08); }
.menu-btn__info { flex: 1; }
.menu-btn__title { font-size: 15px; font-weight: 600; margin-bottom: 2px; }
.menu-btn__desc { font-size: 12px; color: rgba(255,255,255,0.4); }
.menu-btn__arrow { font-size: 16px; color: rgba(255,255,255,0.25); flex-shrink: 0; }
.toast { position: fixed; top: 60px; left: 50%; transform: translateX(-50%) translateY(-20px); background: rgba(0,0,0,0.85); color: #fff; padding: 12px 28px; border-radius: 8px; font-size: 13px; z-index: 1000; opacity: 0; visibility: hidden; transition: opacity 0.3s, transform 0.3s, visibility 0.3s; white-space: nowrap; pointer-events: none; max-width: 90%; text-align: center; line-height: 1.5; }
/* 任务到达提示 */
.menu-btn--has-tasks {
border-color: rgba(74,144,217,0.5) !important;
animation: menuPulse 2s ease-in-out infinite;
}
@keyframes menuPulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(74,144,217,0.4); }
50% { box-shadow: 0 0 0 6px rgba(74,144,217,0); }
}
.menu-btn__badge {
position: absolute; top: -6px; right: -6px;
background: #ff4d4f; color: #fff;
font-size: 10px; font-weight: 700;
min-width: 18px; height: 18px; border-radius: 9px;
display: none; align-items: center; justify-content: center;
padding: 0 4px; line-height: 1;
}
/* Toast */
.toast {
position: fixed; top: 60px; left: 50%; transform: translateX(-50%) translateY(-20px);
background: rgba(0,0,0,0.85); color: #fff; padding: 12px 28px; border-radius: 8px;
font-size: 13px; z-index: 1000; opacity: 0; visibility: hidden;
transition: opacity 0.3s, transform 0.3s, visibility 0.3s;
white-space: nowrap; pointer-events: none; max-width: 90%;
text-align: center; line-height: 1.5;
}
.toast.show { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.toast--warn { background: rgba(250,173,22,0.92); }
.toast--error { background: rgba(255,77,79,0.92); }
</style>
</head>
<body>
<div class="top-bar">
<span class="top-bar__title">净菜配比秤</span>
</div>
<div class="welcome-main">
<div class="login-panel">
<div class="login-panel__title">员工登录</div>
<div class="login-tabs">
<div class="login-tab active" data-tab="face" onclick="switchTab('face')">人脸识别</div>
<div class="login-tab" data-tab="password" onclick="switchTab('password')">账号密码</div>
</div>
<div class="face-section" id="faceSection">
<div class="face-frame" id="faceFrame" onclick="startFaceDetect()">
<div class="face-frame__icon" id="faceIcon">&#x1F464;</div>
<div class="face-frame__text" id="faceText">点击激活<br>人脸识别</div>
<div class="device-frame">
<div class="top-bar">
<span class="top-bar__title">净菜配比秤 - 功能菜单</span>
<span class="top-bar__user" id="topUser">--</span>
<button class="logout-btn" onclick="handleLogout()">退出登录</button>
</div>
<div class="menu-main">
<div class="menu-panel">
<div class="menu-panel__header">
<div class="menu-panel__title">&#x1F4CC; 功能菜单</div>
<div class="menu-panel__user" id="menuUser"></div>
</div>
<div class="face-info">
<span class="face-info__status" id="faceStatus">识别成功后即可使用各项功能</span>
<div class="face-info__name" id="faceName"></div>
<div class="face-info__detail" id="faceDetail"></div>
</div>
</div>
<div class="password-section hidden" id="passwordSection">
<div class="form-group">
<label class="form-label">员工工号</label>
<input type="text" class="form-input" id="empId" placeholder="请输入员工工号">
</div>
<div class="form-group">
<label class="form-label">登录密码</label>
<input type="password" class="form-input" id="empPassword" placeholder="请输入登录密码">
</div>
<button class="login-btn" onclick="passwordLogin()">登录</button>
<a class="menu-btn" id="btnTasks" onclick="handleMenu('01-packing-tasks.html')">
<div class="menu-btn__icon menu-btn__icon--blue">&#x1F4CB;</div>
<div class="menu-btn__info">
<div class="menu-btn__title">称重任务</div>
<div class="menu-btn__desc">查看系统分配的称重任务清单</div>
</div>
<span class="menu-btn__badge" id="taskBadge">0</span>
<div class="menu-btn__arrow">&#x203A;</div>
</a>
</div>
</div>
<div class="menu-panel">
<div class="menu-panel__header">
<div class="menu-panel__title">&#x1F4CC; 功能菜单</div>
<div class="menu-panel__user hidden" id="menuUser"></div>
</div>
<a class="menu-btn disabled" id="btnTasks" onclick="handleMenu('01-packing-tasks.html')">
<div class="menu-btn__icon menu-btn__icon--blue">&#x1F4CB;</div>
<div class="menu-btn__info">
<div class="menu-btn__title">今日任务</div>
<div class="menu-btn__desc">查看系统分配的打包任务清单</div>
</div>
<div class="menu-btn__arrow">&#x203A;</div>
</a>
<a class="menu-btn disabled" id="btnPacking" onclick="handleMenu('02-weighing-packing.html')">
<div class="menu-btn__icon menu-btn__icon--green">&#x1F4E6;</div>
<div class="menu-btn__info">
<div class="menu-btn__title">称重打包</div>
<div class="menu-btn__desc">净菜识别、称重、按单打包</div>
</div>
<div class="menu-btn__arrow">&#x203A;</div>
</a>
<a class="menu-btn disabled" id="btnHistory" onclick="handleMenu('03-history.html')">
<div class="menu-btn__icon menu-btn__icon--orange">&#x1F4DD;</div>
<div class="menu-btn__info">
<div class="menu-btn__title">打包记录</div>
<div class="menu-btn__desc">历史打包记录查看与筛选</div>
</div>
<div class="menu-btn__arrow">&#x203A;</div>
</a>
<a class="menu-btn disabled" id="btnSettings" onclick="handleMenu('04-settings.html')">
<div class="menu-btn__icon menu-btn__icon--gray">&#x2699;&#xFE0F;</div>
<div class="menu-btn__info">
<div class="menu-btn__title">设备设置</div>
<div class="menu-btn__desc">设备参数配置与状态查看</div>
</div>
<div class="menu-btn__arrow">&#x203A;</div>
</a>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
var loginState = 'idle';
var currentUser = null;
var loginTab = 'face';
var mockUsers = [
{ name: '刘九', id: 'EMP-20260050', dept: '打包车间', role: '打包员', empId: '30001', password: '123456' },
{ name: '陈十', id: 'EMP-20260055', dept: '打包车间', role: '打包员', empId: '30002', password: '123456' },
{ name: '杨一', id: 'EMP-20260060', dept: '质检部', role: '质检员', empId: '30003', password: '123456' }
];
var menuButtons = ['btnTasks', 'btnPacking', 'btnHistory', 'btnSettings'];
function switchTab(tab) {
loginTab = tab;
document.querySelectorAll('.login-tab').forEach(function(el) { el.classList.toggle('active', el.dataset.tab === tab); });
document.getElementById('faceSection').classList.toggle('hidden', tab !== 'face');
document.getElementById('passwordSection').classList.toggle('hidden', tab !== 'password');
// 获取 URL 参数
function getUrlParam(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r !== null) return decodeURIComponent(r[2]);
return null;
}
function startFaceDetect() {
if (loginState !== 'idle') return;
loginState = 'detecting';
var frame = document.getElementById('faceFrame');
var icon = document.getElementById('faceIcon');
var text = document.getElementById('faceText');
frame.classList.add('detecting');
icon.innerHTML = '&#x1F50D;';
text.textContent = '识别中...';
showToast('正在识别人脸...', 1500);
setTimeout(function() {
var user = mockUsers[Math.floor(Math.random() * mockUsers.length)];
loginSuccess(user);
}, 2000);
}
// 页面加载时检查登录状态
(function() {
var userName = getUrlParam('user');
var userId = getUrlParam('uid');
function passwordLogin() {
var empId = document.getElementById('empId').value.trim();
var password = document.getElementById('empPassword').value.trim();
if (!empId || !password) { showToast('请输入工号和密码', 2000, 'warn'); return; }
var user = mockUsers.find(function(u) { return u.empId === empId && u.password === password; });
if (!user) { showToast('工号或密码错误', 2000, 'error'); return; }
loginSuccess(user);
}
if (!userName || !userId) {
// 未登录,跳转到登录页
window.location.href = '00-login.html';
return;
}
function loginSuccess(user) {
currentUser = user;
loginState = 'success';
var frame = document.getElementById('faceFrame');
var icon = document.getElementById('faceIcon');
var text = document.getElementById('faceText');
frame.classList.remove('detecting');
frame.classList.add('success');
icon.innerHTML = '&#x2705;';
text.innerHTML = user.name;
document.getElementById('faceStatus').style.display = 'none';
var nameEl = document.getElementById('faceName');
nameEl.textContent = user.name + ' · ' + user.role;
nameEl.classList.add('show');
var detailEl = document.getElementById('faceDetail');
detailEl.textContent = user.dept + ' · ' + user.id;
detailEl.classList.add('show');
var menuUser = document.getElementById('menuUser');
menuUser.textContent = '当前用户:' + user.name;
menuUser.classList.remove('hidden');
menuButtons.forEach(function(id) { document.getElementById(id).classList.remove('disabled'); });
showToast('欢迎,' + user.name, 2000);
currentUser = { name: userName, id: userId };
// 显示用户信息
document.getElementById('topUser').textContent = '操作员:' + userName;
document.getElementById('menuUser').textContent = '当前用户:' + userName;
// 启动任务轮询(每 30 秒检测新任务)
setTimeout(function() { checkNewTasks(5); }, 1500);
setInterval(function() { checkNewTasks(5); }, 30000);
})();
// 检测新任务到达
function checkNewTasks(count) {
var btn = document.getElementById('btnTasks');
var badge = document.getElementById('taskBadge');
btn.classList.add('menu-btn--has-tasks');
badge.textContent = count;
badge.style.display = 'flex';
showToast('⚡ 新任务已到达(' + count + ' 条待处理)', 2500);
}
// 菜单点击处理
function handleMenu(target) {
if (loginState !== 'success' || !currentUser) { showToast('请先登录', 2000, 'warn'); return; }
if (!currentUser) {
window.location.href = '00-login.html';
return;
}
window.location.href = target + '?user=' + encodeURIComponent(currentUser.name) + '&uid=' + currentUser.id;
}
// 退出登录
function handleLogout() {
window.location.href = '00-login.html';
}
// Toast 提示
function showToast(msg, duration, type) {
var el = document.getElementById('toast');
el.textContent = msg;
@@ -3,415 +3,157 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>净菜配比秤 - 今日任务 | 蚁熊智能餐养平台</title>
<title>净菜配比秤 - 称重任务 | 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif; background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden; }
body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #05080f; color: #fff;
min-height: 100vh; min-width: 1280px;
display: flex; align-items: center; justify-content: center;
padding: 20px;
}
/* PAD 设备外壳 */
.device-frame {
width: 1280px; height: 720px;
background: #0e1326;
border-radius: 24px;
box-shadow: 0 24px 64px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04);
overflow: hidden;
position: relative;
display: flex; flex-direction: column;
}
/* 顶部栏 */
.top-bar { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.back-btn { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); text-decoration: none; transition: color 0.2s; }
.back-btn:hover { color: #fff; }
.top-bar__title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.top-bar__user { font-size: 12px; color: rgba(255,255,255,0.5); }
.main-content { height: calc(100% - 44px); padding: 20px 28px; display: flex; flex-direction: column; gap: 16px; }
/* 状态标签卡(统计+筛选二合一) */
.tasks-tabs { display: flex; gap: 10px; }
.status-tab {
display: flex; align-items: center; gap: 8px;
padding: 10px 20px; border-radius: 8px;
background: #1a2b4a; border: 1px solid rgba(255,255,255,0.06);
cursor: pointer; transition: all 0.2s; user-select: none;
}
.status-tab:hover { border-color: rgba(255,255,255,0.15); }
.status-tab.active--pending { background: rgba(250,173,22,0.15); border-color: rgba(250,173,22,0.4); }
.status-tab.active--done { background: rgba(82,196,26,0.15); border-color: rgba(82,196,26,0.4); }
.status-tab__label { font-size: 13px; font-weight: 500; color: rgba(255,255,255,0.7); }
.status-tab__count { font-size: 20px; font-weight: 700; color: #fff; }
/* 主内容区 */
.main-content { height: calc(100% - 44px); padding: 16px 24px; display: flex; flex-direction: column; gap: 12px; }
.tasks-section { flex: 1; display: flex; flex-direction: column; gap: 12px; overflow: hidden; }
.task-list {
flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 10px;
padding-right: 4px; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.1) transparent;
/* Tab 切换栏 */
.tabs { display: flex; gap: 8px; }
.tab { padding: 8px 20px; border-radius: 6px; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; border: 1px solid rgba(255,255,255,0.08); background: transparent; color: rgba(255,255,255,0.6); }
.tab:hover { background: rgba(255,255,255,0.04); color: rgba(255,255,255,0.9); }
.tab.active { background: #4a90d9; border-color: #4a90d9; color: #fff; }
.tab__count { margin-left: 6px; font-size: 11px; opacity: 0.8; }
/* 任务列表容器 */
.task-list-container { flex: 1; overflow-y: auto; scrollbar-width: none; }
.task-list-container::-webkit-scrollbar { display: none; }
/* 任务卡片 */
.task-card { background: #1a2b4a; border-radius: 10px; padding: 0; margin-bottom: 8px; border: 1px solid rgba(255,255,255,0.06); transition: all 0.2s; overflow: hidden; }
.task-card:hover { border-color: rgba(74,144,217,0.3); }
.task-card__header { display: flex; align-items: center; padding: 10px 16px; gap: 14px; background: rgba(26,43,74,0.6); border-bottom: 1px solid rgba(255,255,255,0.06); }
.task-card__order-info { font-size: 13px; color: rgba(255,255,255,0.7); display: flex; align-items: center; gap: 4px; }
.task-card__order-info__label { color: rgba(255,255,255,0.4); }
.task-card__order-info__value { color: #4a90d9; font-weight: 600; }
.task-card__arrival { font-size: 12px; color: rgba(255,255,255,0.5); display: flex; align-items: center; gap: 4px; }
.task-card__arrival__time { color: #faad16; font-weight: 500; }
.task-card__status { margin-left: auto; }
.task-card__pack-btn { padding: 5px 14px; border-radius: 5px; font-size: 12px; font-weight: 500; border: none; cursor: pointer; transition: all 0.2s; background: #4a90d9; color: #fff; }
.task-card__pack-btn:hover { background: #5ba0e9; }
.task-card__pack-btn:disabled { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.4); cursor: not-allowed; }
/* 表格头(6列) */
.item-table__head {
display: grid; grid-template-columns: 1.6fr 0.8fr 0.7fr 0.9fr 0.9fr 70px; gap: 8px;
padding: 6px 16px; background: rgba(0,0,0,0.2); font-size: 11px; color: rgba(255,255,255,0.4); font-weight: 500;
}
.task-list::-webkit-scrollbar { width: 4px; }
.task-list::-webkit-scrollbar-track { background: transparent; }
.task-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }
.task-item {
display: flex; align-items: center; gap: 16px;
background: #1a2b4a; border-radius: 12px; padding: 16px 20px;
border: 1px solid rgba(255,255,255,0.06); cursor: pointer; transition: all 0.2s;
.item-table__body { display: flex; flex-direction: column; }
/* 餐品净菜包主行 */
.item-parent-row {
display: grid; grid-template-columns: 1.6fr 0.8fr 0.7fr 0.9fr 0.9fr 70px; gap: 8px;
padding: 10px 16px; border-top: 1px solid rgba(255,255,255,0.04); align-items: center;
background: rgba(74,144,217,0.06);
}
.task-item:hover { border-color: rgba(74,144,217,0.3); box-shadow: 0 4px 16px rgba(0,0,0,0.2); }
.task-icon { width: 52px; height: 52px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 26px; flex-shrink: 0; }
.task-icon--potato { background: rgba(250,173,22,0.15); }
.task-icon--tomato { background: rgba(255,77,79,0.15); }
.task-icon--cabbage { background: rgba(82,196,26,0.15); }
.task-icon--carrot { background: rgba(250,140,22,0.15); }
.task-icon--cucumber { background: rgba(20,184,166,0.15); }
.task-info { flex: 1; display: flex; flex-direction: column; gap: 4px; }
.task-name { font-size: 15px; font-weight: 600; color: #fff; }
.task-detail { font-size: 12px; color: rgba(255,255,255,0.5); display: flex; gap: 12px; }
.task-detail__item { display: flex; align-items: center; gap: 4px; }
.task-target { display: flex; align-items: center; gap: 6px; padding: 4px 10px; background: rgba(74,144,217,0.15); border-radius: 4px; font-size: 11px; color: #4a90d9; }
.task-progress { width: 140px; display: flex; flex-direction: column; gap: 4px; }
.progress-bar { height: 6px; background: rgba(255,255,255,0.1); border-radius: 3px; overflow: hidden; }
.progress-bar__fill { height: 100%; border-radius: 3px; }
.progress-bar__fill--blue { background: #4a90d9; }
.progress-bar__fill--green { background: #52c41a; }
.progress-text { font-size: 11px; color: rgba(255,255,255,0.5); text-align: right; }
.task-status { width: 80px; text-align: center; }
.status-badge { display: inline-block; padding: 4px 10px; border-radius: 4px; font-size: 11px; font-weight: 500; }
.parent-name { display: flex; align-items: center; gap: 8px; }
.parent-icon {
width: 32px; height: 32px; border-radius: 6px;
display: flex; align-items: center; justify-content: center;
font-size: 16px; background: rgba(114,46,209,0.15); flex-shrink: 0;
}
.parent-name__text { font-size: 13px; font-weight: 600; color: #fff; }
.pack-size-tag {
display: inline-block; font-size: 11px; padding: 2px 8px; border-radius: 4px;
background: rgba(74,144,217,0.15); color: #4a90d9; font-weight: 500;
}
.parent-weight { font-size: 12px; color: rgba(255,255,255,0.6); text-align: right; }
.parent-weight--done { color: #52c41a; font-weight: 600; }
/* 食材子行 */
.item-child-row {
display: grid; grid-template-columns: 1.6fr 0.8fr 0.7fr 0.9fr 0.9fr 70px; gap: 8px;
padding: 8px 16px 8px 48px; border-top: 1px solid rgba(255,255,255,0.03);
align-items: center; transition: background 0.2s;
}
.item-child-row:hover { background: rgba(74,144,217,0.08); }
.child-name { display: flex; align-items: center; gap: 8px; }
.child-icon {
width: 26px; height: 26px; border-radius: 5px;
display: flex; align-items: center; justify-content: center;
font-size: 13px; flex-shrink: 0;
}
.child-icon--potato { background: rgba(250,173,22,0.15); }
.child-icon--carrot { background: rgba(250,140,22,0.15); }
.child-icon--cucumber { background: rgba(20,184,166,0.15); }
.child-icon--cabbage { background: rgba(82,196,26,0.15); }
.child-icon--tomato { background: rgba(255,77,79,0.15); }
.child-icon--egg { background: rgba(250,173,22,0.15); }
.child-icon--onion { background: rgba(114,46,209,0.15); }
.child-icon--green-onion { background: rgba(82,196,26,0.15); }
.child-name__text { font-size: 12px; color: rgba(255,255,255,0.85); }
.child-weight { font-size: 12px; color: rgba(255,255,255,0.55); text-align: right; }
.child-weight--done { color: #52c41a; font-weight: 600; }
.child-pack-size { font-size: 12px; color: rgba(255,255,255,0.45); text-align: center; }
/* 状态徽章 */
.status-badge { font-size: 11px; padding: 3px 8px; border-radius: 4px; font-weight: 500; display: inline-block; }
.status-badge--pending { background: rgba(250,173,22,0.15); color: #faad16; }
.status-badge--done { background: rgba(82,196,26,0.15); color: #52c41a; }
.task-action { width: 80px; }
.action-btn { padding: 8px 16px; border-radius: 6px; font-size: 12px; font-weight: 500; background: #4a90d9; color: #fff; border: none; cursor: pointer; transition: all 0.2s; }
.action-btn:hover { background: #5ba0e9; }
.action-btn--done { background: rgba(82,196,26,0.15); color: #52c41a; cursor: default; }
.status-tag {
display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 10px;
}
.status-tag--done { background: rgba(82,196,26,0.15); color: #52c41a; }
.status-tag--wait { background: rgba(250,173,22,0.15); color: #faad16; }
/* 弹窗遮罩层 */
.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 900; display: none; align-items: center; justify-content: center; }
.modal-overlay.open { display: flex; }
.modal-box { width: 1220px; height: 660px; background: #0e1326; border-radius: 16px; display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 24px 64px rgba(0,0,0,0.7); }
.modal-header { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.modal-close { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); transition: color 0.2s; background: none; border: none; }
.modal-close:hover { color: #fff; }
.modal-title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.modal-body { flex: 1; display: flex; gap: 20px; padding: 16px 20px; overflow: hidden; }
.modal-scale-panel { flex: 0 0 540px; display: flex; flex-direction: column; gap: 12px; }
.modal-task { background: #1a2b4a; border-radius: 12px; padding: 12px 16px; border: 1px solid rgba(255,255,255,0.06); }
.modal-task__body { display: flex; align-items: center; gap: 12px; }
.modal-task__icon { width: 44px; height: 44px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 22px; }
.modal-task__info { flex: 1; }
.modal-task__name { font-size: 15px; font-weight: 600; color: #fff; margin-bottom: 2px; }
.modal-task__target { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; background: rgba(74,144,217,0.15); border-radius: 4px; font-size: 11px; color: #4a90d9; }
.modal-task__progress { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.progress-mini { width: 80px; height: 4px; background: rgba(255,255,255,0.1); border-radius: 2px; overflow: hidden; }
.progress-mini__fill { height: 100%; background: #4a90d9; border-radius: 2px; }
.progress-mini__text { font-size: 11px; color: rgba(255,255,255,0.5); }
.scale-display { flex: 1; background: #1a2b4a; border-radius: 14px; padding: 18px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; border: 2px solid rgba(255,255,255,0.06); }
.scale-display.weighing { border-color: #4a90d9; }
.scale-display.stable { border-color: #52c41a; }
.ai-result { display: flex; align-items: center; gap: 14px; padding: 14px 18px; background: rgba(0,0,0,0.2); border-radius: 10px; width: 100%; }
.ai-result__icon { width: 48px; height: 48px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 26px; background: rgba(74,144,217,0.15); }
.ai-result__info { flex: 1; }
.ai-result__label { font-size: 11px; color: rgba(255,255,255,0.4); margin-bottom: 2px; }
.ai-result__name { font-size: 18px; font-weight: 700; color: #fff; }
.ai-result__confidence { font-size: 12px; color: rgba(255,255,255,0.5); margin-top: 2px; }
.ai-result__status { font-size: 12px; padding: 4px 10px; border-radius: 4px; }
.ai-result__status--matching { background: rgba(82,196,26,0.15); color: #52c41a; }
.ai-result__status--mismatch { background: rgba(255,77,79,0.15); color: #ff4d4f; }
.ai-result__status--idle { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.4); }
.weight-display { text-align: center; }
.weight-display__value { font-size: 60px; font-weight: 700; color: #fff; line-height: 1; font-family: "Courier New", monospace; }
.weight-display__unit { font-size: 18px; color: rgba(255,255,255,0.5); margin-left: 4px; }
.weight-display__status { font-size: 13px; color: rgba(255,255,255,0.4); margin-top: 6px; }
.weight-display__status.weighing { color: #4a90d9; }
.weight-display__status.stable { color: #52c41a; }
.scale-actions { display: flex; gap: 12px; width: 100%; }
.scale-btn { flex: 1; padding: 12px; border-radius: 10px; font-size: 14px; font-weight: 600; border: none; cursor: pointer; transition: all 0.2s; }
.scale-btn--primary { background: #52c41a; color: #fff; }
.scale-btn--primary:hover { background: #62d42a; }
.scale-btn--primary:disabled { background: rgba(82,196,26,0.3); cursor: not-allowed; }
.scale-btn--secondary { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.8); }
.scale-btn--secondary:hover { background: rgba(255,255,255,0.12); }
.modal-records-panel { flex: 1; display: flex; flex-direction: column; gap: 12px; }
.modal-records { flex: 1; background: #1a2b4a; border-radius: 12px; padding: 14px 16px; display: flex; flex-direction: column; overflow: hidden; }
.modal-records__header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.modal-records__title { font-size: 14px; font-weight: 600; color: rgba(255,255,255,0.8); }
.modal-records__count { font-size: 12px; color: rgba(255,255,255,0.4); }
.modal-records__list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.1) transparent; }
.modal-records__list::-webkit-scrollbar { width: 4px; }
.modal-records__list::-webkit-scrollbar-track { background: transparent; }
.modal-records__list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }
.modal-record { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: rgba(0,0,0,0.15); border-radius: 8px; }
.modal-record__icon { width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; background: rgba(250,173,22,0.15); }
.modal-record__info { flex: 1; }
.modal-record__name { font-size: 12px; font-weight: 500; color: #fff; }
.modal-record__target { font-size: 11px; color: #4a90d9; }
.modal-record__time { font-size: 10px; color: rgba(255,255,255,0.3); }
.modal-record__weight { font-size: 13px; font-weight: 600; color: #52c41a; }
.modal-record__seal { font-size: 11px; padding: 2px 6px; background: rgba(74,144,217,0.15); border-radius: 3px; color: #4a90d9; }
.modal-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: rgba(255,255,255,0.3); }
.modal-empty__icon { font-size: 28px; margin-bottom: 8px; }
.modal-empty__text { font-size: 12px; }
.toast { position: fixed; top: 60px; left: 50%; transform: translateX(-50%) translateY(-20px); background: rgba(0,0,0,0.85); color: #fff; padding: 12px 28px; border-radius: 8px; font-size: 13px; z-index: 1100; opacity: 0; visibility: hidden; transition: opacity 0.3s, transform 0.3s, visibility 0.3s; white-space: nowrap; pointer-events: none; }
/* Toast 提示 */
.toast { position: fixed; top: 60px; left: 50%; transform: translateX(-50%) translateY(-20px); background: rgba(0,0,0,0.85); color: #fff; padding: 12px 28px; border-radius: 8px; font-size: 13px; z-index: 1000; opacity: 0; visibility: hidden; transition: opacity 0.3s, transform 0.3s, visibility 0.3s; white-space: nowrap; pointer-events: none; }
.toast.show { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.toast--error { background: rgba(255,77,79,0.92); }
.toast--success { background: rgba(82,196,26,0.92); }
.toast--warn { background: rgba(250,173,22,0.92); }
</style>
</head>
<body>
<div class="top-bar">
<div class="device-frame">
<div class="top-bar">
<a class="back-btn" id="backBtn" href="javascript:void(0)">&#x25C0; 返回</a>
<span class="top-bar__title">称重任务</span>
<span class="top-bar__user" id="topUser">--</span>
</div>
<a class="back-btn" id="backBtn" href="javascript:void(0)">&#x25C0; 返回</a> <span class="top-bar__title">今日打包任务</span>
<span class="top-bar__user" id="topUser">--</span>
</div>
<div class="main-content">
<!-- 状态标签卡(统计+筛选) -->
<div class="tasks-tabs">
<div class="status-tab active--pending" data-filter="pending" onclick="filterTasks('pending', this)">
<span class="status-tab__label">待处理</span>
<span class="status-tab__count" id="pendingCount">4</span>
<div class="main-content">
<!-- Tab 切换 -->
<div class="tabs">
<div class="tab active" data-filter="pending" onclick="switchTab('pending', this)">待称重 <span class="tab__count" id="pendingCount">0</span></div>
<div class="tab" data-filter="done" onclick="switchTab('done', this)">已完成 <span class="tab__count" id="doneCount">0</span></div>
</div>
<div class="status-tab" data-filter="done" onclick="filterTasks('done', this)">
<span class="status-tab__label">已完成</span>
<span class="status-tab__count" id="doneCount">6</span>
<!-- 任务列表 -->
<div class="task-list-container" id="taskListContainer">
<!-- 动态渲染 -->
</div>
</div>
<div class="tasks-section">
<div class="task-list" id="taskList">
<!-- 任务项1 -->
<div class="task-item" data-status="pending">
<div class="task-icon task-icon--potato">&#x1F954;</div>
<div class="task-info">
<div class="task-name">土豆丝</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 5.0斤</span>
<span class="task-detail__item">&#x23F0; 截止: 11:30</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 张三</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--blue" style="width: 60%"></div></div>
<div class="progress-text">已包 3.0 / 需 5.0 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--pending">待处理</span></div>
<div class="task-action"><button class="action-btn" onclick="openModal('potato')">去处理</button></div>
</div>
<!-- 任务项2 -->
<div class="task-item" data-status="pending">
<div class="task-icon task-icon--carrot">&#x1F955;</div>
<div class="task-info">
<div class="task-name">胡萝卜丝</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 2.8斤</span>
<span class="task-detail__item">&#x23F0; 截止: 12:00</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 李四</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--blue" style="width: 0%"></div></div>
<div class="progress-text">已包 0 / 需 2.8 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--pending">待处理</span></div>
<div class="task-action"><button class="action-btn" onclick="openModal('carrot')">去处理</button></div>
</div>
<!-- 任务项3 -->
<div class="task-item" data-status="done">
<div class="task-icon task-icon--cucumber">&#x1F952;</div>
<div class="task-info">
<div class="task-name">黄瓜片</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 4.2斤</span>
<span class="task-detail__item">&#x23F0; 截止: 11:00</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 王五</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--green" style="width: 100%"></div></div>
<div class="progress-text">已包 4.2 / 需 4.2 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="task-action"><button class="action-btn action-btn--done">完成</button></div>
</div>
<!-- 任务项4 -->
<div class="task-item" data-status="done">
<div class="task-icon task-icon--cabbage">&#x1F96C;</div>
<div class="task-info">
<div class="task-name">白菜段</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 8.5斤</span>
<span class="task-detail__item">&#x23F0; 截止: 10:30</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 赵六</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--green" style="width: 100%"></div></div>
<div class="progress-text">已包 8.5 / 需 8.5 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="task-action"><button class="action-btn action-btn--done">完成</button></div>
</div>
<!-- 任务项5 -->
<div class="task-item" data-status="pending">
<div class="task-icon task-icon--tomato">&#x1F345;</div>
<div class="task-info">
<div class="task-name">西红柿块</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 3.5斤</span>
<span class="task-detail__item">&#x23F0; 截止: 12:00</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 孙七</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--blue" style="width: 0%"></div></div>
<div class="progress-text">已包 0 / 需 3.5 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--pending">待处理</span></div>
<div class="task-action"><button class="action-btn" onclick="openModal('tomato')">去处理</button></div>
</div>
<!-- 任务项6 -->
<div class="task-item" data-status="pending">
<div class="task-icon task-icon--potato">&#x1F954;</div>
<div class="task-info">
<div class="task-name">土豆块(晚餐)</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 3.2斤</span>
<span class="task-detail__item">&#x23F0; 截止: 16:00</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 张三</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--blue" style="width: 30%"></div></div>
<div class="progress-text">已包 1.0 / 需 3.2 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--pending">待处理</span></div>
<div class="task-action"><button class="action-btn" onclick="openModal('potato_dinner')">去处理</button></div>
</div>
<!-- 任务项7 -->
<div class="task-item" data-status="done">
<div class="task-icon task-icon--carrot">&#x1F955;</div>
<div class="task-info">
<div class="task-name">胡萝卜丁</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 2.5斤</span>
<span class="task-detail__item">&#x23F0; 截止: 11:00</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 周八</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--green" style="width: 100%"></div></div>
<div class="progress-text">已包 2.5 / 需 2.5 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="task-action"><button class="action-btn action-btn--done">完成</button></div>
</div>
<!-- 任务项8 -->
<div class="task-item" data-status="done">
<div class="task-icon task-icon--cucumber">&#x1F952;</div>
<div class="task-info">
<div class="task-name">黄瓜条</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 3.0斤</span>
<span class="task-detail__item">&#x23F0; 截止: 11:30</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 刘九</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--green" style="width: 100%"></div></div>
<div class="progress-text">已包 3.0 / 需 3.0 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="task-action"><button class="action-btn action-btn--done">完成</button></div>
</div>
<!-- 任务项9 -->
<div class="task-item" data-status="done">
<div class="task-icon task-icon--potato">&#x1F954;</div>
<div class="task-info">
<div class="task-name">土豆条</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 2.6斤</span>
<span class="task-detail__item">&#x23F0; 截止: 11:00</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 陈十</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--green" style="width: 100%"></div></div>
<div class="progress-text">已包 2.6 / 需 2.6 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="task-action"><button class="action-btn action-btn--done">完成</button></div>
</div>
<!-- 任务项10 -->
<div class="task-item" data-status="done">
<div class="task-icon task-icon--tomato">&#x1F345;</div>
<div class="task-info">
<div class="task-name">小番茄</div>
<div class="task-detail">
<span class="task-detail__item">&#x1F4E6; 总量: 1.8斤</span>
<span class="task-detail__item">&#x23F0; 截止: 14:30</span>
</div>
</div>
<div class="task-target">&#x1F464; 分配给: 杨一</div>
<div class="task-progress">
<div class="progress-bar"><div class="progress-bar__fill progress-bar__fill--green" style="width: 100%"></div></div>
<div class="progress-text">已包 1.8 / 需 1.8 斤</div>
</div>
<div class="task-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="task-action"><button class="action-btn action-btn--done">完成</button></div>
</div>
</div>
</div>
</div>
<!-- 处理弹窗 -->
<div class="modal-overlay" id="modalOverlay">
<div class="modal-box">
<div class="modal-header">
<button class="modal-close" onclick="closeModal()">&#x25C0; 返回任务列表</button>
<span class="modal-title" id="modalTitle">称重打包</span>
<span style="width:80px"></span>
</div>
<div class="modal-body">
<div class="modal-scale-panel">
<div class="modal-task">
<div class="modal-task__body">
<div class="modal-task__icon task-icon--potato" id="modalTaskIcon">&#x1F954;</div>
<div class="modal-task__info">
<div class="modal-task__name" id="modalTaskName">土豆丝</div>
<span class="modal-task__target" id="modalTaskTarget">&#x1F464; 分配给: 张三</span>
</div>
</div>
<div class="modal-task__progress">
<div class="progress-mini">
<div class="progress-mini__fill" id="modalProgressFill" style="width:60%"></div>
</div>
<span class="progress-mini__text" id="modalProgressText">3.0/5.0斤</span>
</div>
</div>
<div class="scale-display" id="scaleDisplay">
<div class="ai-result">
<div class="ai-result__icon" id="aiIcon">&#x1F4C7;</div>
<div class="ai-result__info">
<div class="ai-result__label">AI 识别结果</div>
<div class="ai-result__name" id="aiName">等待放置食材...</div>
<div class="ai-result__confidence" id="aiConfidence"></div>
</div>
<span class="ai-result__status ai-result__status--idle" id="aiStatus">待机</span>
</div>
<div class="weight-display">
<span class="weight-display__value" id="weightValue">0.00</span>
<span class="weight-display__unit"></span>
<div class="weight-display__status" id="weightStatus">请将食材放置秤上</div>
</div>
<div class="scale-actions">
<button class="scale-btn scale-btn--secondary" onclick="resetScale()">清零</button>
<button class="scale-btn scale-btn--primary" id="packBtn" onclick="confirmPack()" disabled>打包+塑封</button>
</div>
</div>
</div>
<div class="modal-records-panel">
<div class="modal-records">
<div class="modal-records__header">
<span class="modal-records__title">打包记录</span>
<span class="modal-records__count" id="recordsCount">共 0 条</span>
</div>
<div class="modal-records__list" id="recordsList">
<div class="modal-empty" id="emptyState">
<div class="modal-empty__icon">&#x1F4ED;</div>
<div class="modal-empty__text">暂无打包记录</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="toast" id="toast"></div>
</div>
<div class="toast" id="toast"></div>
<script>
/* 获取 URL 参数 */
function getUrlParam(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
@@ -421,201 +163,233 @@
var userName = getUrlParam('user');
if (userName) document.getElementById('topUser').textContent = '操作员: ' + userName;
// 筛选任务
function filterTasks(status, tab) {
document.querySelectorAll('.status-tab').forEach(function(el) {
el.classList.remove('active--pending', 'active--done');
});
tab.classList.add(status === 'pending' ? 'active--pending' : 'active--done');
document.querySelectorAll('.task-item').forEach(function(item) {
item.style.display = (item.dataset.status === status) ? 'flex' : 'none';
});
}
/* 当前 Tab 筛选 */
var currentFilter = 'pending';
// ── 弹窗逻辑 ──
var modalKey = '';
var scaleState = 'idle';
var currentWeight = 0;
var currentAiResult = null;
var records = [];
var taskData = {
'potato': { name: '土豆丝', icon: '&#x1F954;', iconClass: 'task-icon--potato', need: 5.0, done: 3.0, target: '张三', deadline: '11:30' },
'carrot': { name: '胡萝卜丝', icon: '&#x1F955;', iconClass: 'task-icon--carrot', need: 2.8, done: 0, target: '李四', deadline: '12:00' },
'tomato': { name: '西红柿块', icon: '&#x1F345;', iconClass: 'task-icon--tomato', need: 3.5, done: 0, target: '孙七', deadline: '12:00' },
'potato_dinner': { name: '土豆块(晚餐)', icon: '&#x1F954;', iconClass: 'task-icon--potato', need: 3.2, done: 1.0, target: '张三', deadline: '16:00' }
};
var aiResults = {
'potato': { name: '土豆丝', icon: '&#x1F954;', confidence: 97 },
'tomato': { name: '西红柿块', icon: '&#x1F345;', confidence: 95 },
'carrot': { name: '胡萝卜丝', icon: '&#x1F955;', confidence: 96 },
'cucumber':{ name: '黄瓜片', icon: '&#x1F952;', confidence: 94 },
'cabbage': { name: '白菜段', icon: '&#x1F96C;', confidence: 93 }
};
function openModal(key) {
modalKey = key;
var task = taskData[key] || taskData['potato'];
var iconEl = document.getElementById('modalTaskIcon');
iconEl.innerHTML = task.icon;
iconEl.className = 'modal-task__icon ' + task.iconClass;
document.getElementById('modalTaskName').textContent = task.name;
document.getElementById('modalTaskTarget').innerHTML = '&#x1F464; 分配给: ' + task.target;
var pct = Math.round((task.done / task.need) * 100);
document.getElementById('modalProgressFill').style.width = pct + '%';
document.getElementById('modalProgressText').textContent = task.done.toFixed(1) + '/' + task.need.toFixed(1) + '斤';
document.getElementById('modalTitle').textContent = task.name + ' - 称重打包';
resetScale();
records = [];
updateRecordsDisplay();
document.getElementById('modalOverlay').classList.add('open');
setTimeout(function() { startDetecting(key.replace('_dinner','')); }, 1500);
}
function closeModal() {
document.getElementById('modalOverlay').classList.remove('open');
}
function startDetecting(ingredientKey) {
scaleState = 'detecting';
var scale = document.getElementById('scaleDisplay');
scale.classList.add('weighing');
scale.classList.remove('stable');
document.getElementById('weightStatus').textContent = '正在称重...';
document.getElementById('weightStatus').className = 'weight-display__status weighing';
var targetWeight = 2.05;
var anim = 0;
var timer = setInterval(function() {
anim += 0.13;
if (anim >= targetWeight) { anim = targetWeight; clearInterval(timer); onWeightStable(ingredientKey, targetWeight); }
document.getElementById('weightValue').textContent = anim.toFixed(2);
}, 100);
setTimeout(function() {
var result = aiResults[ingredientKey] || aiResults['potato'];
currentAiResult = result;
document.getElementById('aiIcon').innerHTML = result.icon;
document.getElementById('aiName').textContent = result.name;
document.getElementById('aiConfidence').textContent = '置信度: ' + result.confidence + '%';
var task = taskData[modalKey];
var isMatch = task.name === result.name || task.name.includes(result.name);
var statusEl = document.getElementById('aiStatus');
if (isMatch) {
statusEl.textContent = '匹配';
statusEl.className = 'ai-result__status ai-result__status--matching';
} else {
statusEl.textContent = '不匹配';
statusEl.className = 'ai-result__status ai-result__status--mismatch';
showToast('AI识别不匹配,请检查食材', 2500, 'error');
/* 模拟数据:每任务只有1个餐品净菜包 */
var mockData = [
{
orderNo: 'PO-20260616-001',
expectedArrival: '11:30',
taskNo: 'T-001',
mealPack: {
packName: '宫保鸡丁套餐',
packSize: 3,
notes: '少辣',
ingredients: [
{ name: '土豆', icon: '&#x1F954;', iconClass: 'child-icon--potato', weightPerPack: 500, weighed: 750 },
{ name: '胡萝卜丝', icon: '&#x1F955;', iconClass: 'child-icon--carrot', weightPerPack: 500, weighed: 1500 },
{ name: '黄瓜片', icon: '&#x1F952;', iconClass: 'child-icon--cucumber', weightPerPack: 500, weighed: 0 },
{ name: '大葱丝', icon: '&#x1F9C5;', iconClass: 'child-icon--green-onion', weightPerPack: 500, weighed: 0 },
{ name: '紫洋葱丝', icon: '&#x1F9C5;', iconClass: 'child-icon--onion', weightPerPack: 500, weighed: 0 }
]
}
},
{
orderNo: 'PO-20260616-002',
expectedArrival: '12:00',
taskNo: 'T-002',
mealPack: {
packName: '鱼香肉丝套餐',
packSize: 2,
notes: '',
ingredients: [
{ name: '胡萝卜丝', icon: '&#x1F955;', iconClass: 'child-icon--carrot', weightPerPack: 750, weighed: 0 },
{ name: '白菜丝', icon: '&#x1F96C;', iconClass: 'child-icon--cabbage', weightPerPack: 1000, weighed: 0 },
{ name: '土豆丝', icon: '&#x1F954;', iconClass: 'child-icon--potato', weightPerPack: 500, weighed: 0 }
]
}
},
{
orderNo: 'PO-20260616-003',
expectedArrival: '14:30',
taskNo: 'T-003',
mealPack: {
packName: '番茄炒蛋套餐',
packSize: 4,
notes: '',
ingredients: [
{ name: '西红柿块', icon: '&#x1F345;', iconClass: 'child-icon--tomato', weightPerPack: 750, weighed: 3000 },
{ name: '鸡蛋', icon: '&#x1F95A;', iconClass: 'child-icon--egg', weightPerPack: 500, weighed: 2000 }
]
}
},
{
orderNo: 'PO-20260616-004',
expectedArrival: '16:00',
taskNo: 'T-004',
mealPack: {
packName: '凉拌黄瓜套餐',
packSize: 5,
notes: '多放蒜',
ingredients: [
{ name: '黄瓜条', icon: '&#x1F952;', iconClass: 'child-icon--cucumber', weightPerPack: 750, weighed: 3750 },
{ name: '小番茄', icon: '&#x1F345;', iconClass: 'child-icon--tomato', weightPerPack: 250, weighed: 1250 }
]
}
}, 800);
}
function onWeightStable(key, weight) {
scaleState = 'stable';
currentWeight = weight;
var scale = document.getElementById('scaleDisplay');
scale.classList.remove('weighing');
scale.classList.add('stable');
document.getElementById('weightStatus').textContent = '重量已稳定';
document.getElementById('weightStatus').className = 'weight-display__status stable';
document.getElementById('packBtn').disabled = false;
}
function confirmPack() {
if (scaleState !== 'stable' || !currentAiResult) return;
var task = taskData[modalKey];
task.done += currentWeight;
records.unshift({
name: currentAiResult.name,
icon: currentAiResult.icon,
target: task.target,
weight: currentWeight,
time: new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
});
updateRecordsDisplay();
var pct = Math.round((task.done / task.need) * 100);
document.getElementById('modalProgressFill').style.width = pct + '%';
document.getElementById('modalProgressText').textContent = task.done.toFixed(1) + '/' + task.need.toFixed(1) + '斤';
showToast('打包成功: ' + currentAiResult.name + ' ' + currentWeight.toFixed(2) + '斤 → ' + task.target, 2500, 'success');
resetScale();
}
function updateRecordsDisplay() {
var list = document.getElementById('recordsList');
var empty = document.getElementById('emptyState');
if (records.length === 0) {
empty.style.display = 'flex';
document.getElementById('recordsCount').textContent = '共 0 条';
return;
}
empty.style.display = 'none';
document.getElementById('recordsCount').textContent = '共 ' + records.length + ' 条';
var existing = list.querySelectorAll('.modal-record');
existing.forEach(function(el) { el.remove(); });
records.forEach(function(r) {
var item = document.createElement('div');
item.className = 'modal-record';
item.innerHTML = '<div class="modal-record__icon">' + r.icon + '</div>' +
'<div class="modal-record__info"><div class="modal-record__name">' + r.name + '</div>' +
'<div class="modal-record__target">→ ' + r.target + '</div>' +
'<div class="modal-record__time">' + r.time + '</div></div>' +
'<div class="modal-record__weight">+' + r.weight.toFixed(2) + '斤</div>' +
'<div class="modal-record__seal">&#x1F512; 已塑封</div>';
list.appendChild(item);
];
/* 获取餐品净菜包状态 */
function getPackStatus(pack) {
var allDone = pack.ingredients.every(function(ing) {
return ing.weighed >= ing.weightPerPack * pack.packSize;
});
if (allDone) return { text: '已完成', cls: 'status-badge--done' };
return { text: '待称重', cls: 'status-badge--pending' };
}
/* 判断餐品净菜包是否有待称重项 */
function hasPendingPack(pack) {
return pack.ingredients.some(function(ing) {
return ing.weighed < ing.weightPerPack * pack.packSize;
});
}
function resetScale() {
scaleState = 'idle';
currentWeight = 0;
currentAiResult = null;
var scale = document.getElementById('scaleDisplay');
scale.classList.remove('weighing', 'stable');
document.getElementById('weightValue').textContent = '0.00';
document.getElementById('weightStatus').textContent = '请将食材放置秤上';
document.getElementById('weightStatus').className = 'weight-display__status';
document.getElementById('aiIcon').innerHTML = '&#x1F4C7;';
document.getElementById('aiName').textContent = '等待放置食材...';
document.getElementById('aiConfidence').textContent = '';
document.getElementById('aiStatus').textContent = '待机';
document.getElementById('aiStatus').className = 'ai-result__status ai-result__status--idle';
document.getElementById('packBtn').disabled = true;
setTimeout(function() {
if (scaleState === 'idle' && document.getElementById('modalOverlay').classList.contains('open')) {
var keys = ['potato', 'carrot', 'cucumber'];
startDetecting(keys[Math.floor(Math.random() * keys.length)]);
}
}, 3000);
/* 渲染任务列表 */
function renderTaskList() {
var container = document.getElementById('taskListContainer');
container.innerHTML = '';
var pendingCount = 0;
var doneCount = 0;
mockData.forEach(function(order) {
var pack = order.mealPack;
var hasPending = hasPendingPack(pack);
if (hasPending) pendingCount++; else doneCount++;
var taskCard = document.createElement('div');
taskCard.className = 'task-card';
/* 合并行:订单号 + 任务号 + 期望到货 + 状态 + 去称重 */
var header = document.createElement('div');
header.className = 'task-card__header';
header.innerHTML =
'<span class="task-card__order-info"><span class="task-card__order-info__label">订单号:</span><span class="task-card__order-info__value">' + order.orderNo + '</span></span>' +
'<span class="task-card__order-info"><span class="task-card__order-info__label">任务号:</span><span class="task-card__order-info__value">' + order.taskNo + '</span></span>' +
'<span class="task-card__arrival">&#x23F0; 期望到货:<span class="task-card__arrival__time">' + order.expectedArrival + '</span></span>' +
'<span class="task-card__status"><span class="status-badge ' + (hasPending ? 'status-badge--pending' : 'status-badge--done') + '">' + (hasPending ? '待称重' : '已完成') + '</span></span>' +
(hasPending
? '<button class="task-card__pack-btn" onclick="handleWeigh(\'' + order.taskNo + '\',\'' + order.orderNo + '\',\'' + pack.packName + '\')">去称重</button>'
: '<button class="task-card__pack-btn" disabled>全部完成</button>');
taskCard.appendChild(header);
/* 表格头(6列) */
var tableHead = document.createElement('div');
tableHead.className = 'item-table__head';
tableHead.innerHTML = '<div>名称</div><div style="text-align:center">每份用量</div><div style="text-align:center">打包规格</div><div style="text-align:right">所需总重</div><div style="text-align:right">已称</div><div style="text-align:center">状态</div>';
taskCard.appendChild(tableHead);
/* 表格体 */
var tableBody = document.createElement('div');
tableBody.className = 'item-table__body';
var packStatus = getPackStatus(pack);
var totalNeed = 0;
var totalWeighed = 0;
pack.ingredients.forEach(function(ing) {
totalNeed += ing.weightPerPack * pack.packSize;
totalWeighed += ing.weighed;
});
totalNeed = Math.round(totalNeed * 100) / 100;
totalWeighed = Math.round(totalWeighed * 100) / 100;
var weightClass = totalWeighed >= totalNeed ? 'parent-weight--done' : '';
/* 主行:餐品净菜包 */
var parentRow = document.createElement('div');
parentRow.className = 'item-parent-row';
parentRow.innerHTML =
'<div class="parent-name"><div class="parent-icon">&#x1F4E6;</div><span class="parent-name__text">' + pack.packName + '</span></div>' +
'<div style="text-align:center;font-size:12px;color:rgba(255,255,255,0.5)">—</div>' +
'<div style="text-align:center"><span class="pack-size-tag">' + pack.packSize + ' 份</span></div>' +
'<div class="parent-weight ' + weightClass + '">需 ' + totalNeed + ' 克</div>' +
'<div class="parent-weight ' + weightClass + '">已 ' + totalWeighed + ' 克</div>' +
'<div style="text-align:center"><span class="status-tag ' + (packStatus.text === '已完成' ? 'status-tag--done' : 'status-tag--wait') + '">' + packStatus.text + '</span></div>';
tableBody.appendChild(parentRow);
/* 子行:每种食材 */
pack.ingredients.forEach(function(ing) {
var need = Math.round(ing.weightPerPack * pack.packSize * 100) / 100;
var isDone = ing.weighed >= need;
var childWeightClass = isDone ? 'child-weight--done' : '';
var childRow = document.createElement('div');
childRow.className = 'item-child-row';
childRow.innerHTML =
'<div class="child-name"><div class="child-icon ' + ing.iconClass + '">' + ing.icon + '</div><span class="child-name__text">' + ing.name + '</span></div>' +
'<div class="child-weight" style="text-align:center">' + ing.weightPerPack + ' 克</div>' +
'<div class="child-pack-size">×</div>' +
'<div class="child-weight" style="text-align:right">' + need + ' 克</div>' +
'<div class="child-weight ' + childWeightClass + '" style="text-align:right">' + ing.weighed + ' 克</div>' +
'<div style="text-align:center"><span class="status-tag ' + (isDone ? 'status-tag--done' : 'status-tag--wait') + '">' + (isDone ? '已完成' : '待称重') + '</span></div>';
tableBody.appendChild(childRow);
});
taskCard.appendChild(tableBody);
container.appendChild(taskCard);
});
/* 更新 Tab 计数 */
document.getElementById('pendingCount').textContent = pendingCount;
document.getElementById('doneCount').textContent = doneCount;
}
/* 切换 Tab */
function switchTab(filter, el) {
currentFilter = filter;
document.querySelectorAll('.tab').forEach(function(tab) { tab.classList.remove('active'); });
el.classList.add('active');
document.querySelectorAll('.task-card').forEach(function(card) {
var packBtn = card.querySelector('.task-card__pack-btn');
var hasPending = packBtn && !packBtn.disabled;
if (filter === 'pending') {
card.style.display = hasPending ? 'block' : 'none';
} else {
card.style.display = hasPending ? 'none' : 'block';
}
});
}
/* 去称重 */
function handleWeigh(taskNo, orderNo, packName) {
var target = '02-weighing-packing.html?task=' + taskNo + '&pack=' + encodeURIComponent(packName) + '&order=' + encodeURIComponent(orderNo);
if (userName) target += '&user=' + encodeURIComponent(userName);
window.location.href = target;
}
/* Toast 提示 */
function showToast(msg, duration, type) {
var el = document.getElementById('toast');
el.textContent = msg;
el.className = 'toast';
if (type === 'error') el.classList.add('toast--error');
if (type === 'success') el.classList.add('toast--success');
if (type === 'warn') el.classList.add('toast--warn');
el.classList.add('show');
clearTimeout(el._timer);
el._timer = setTimeout(function() { el.classList.remove('show'); }, duration || 2000);
}
/* 初始化 */
renderTaskList();
/* 初始过滤:待称重 Tab 默认不显示已完成订单 */
switchTab('pending', document.querySelector('.tab[data-filter="pending"]'));
/* 返回按钮 */
(function() {
var backBtn = document.getElementById('backBtn');
if (backBtn) {
var params = new URLSearchParams(window.location.search);
var referer = params.get('referer');
if (referer) {
backBtn.href = referer;
} else if (document.referrer && document.referrer.indexOf(window.location.hostname) !== -1) {
backBtn.href = document.referrer;
} else {
backBtn.href = '00-welcome.html';
}
}
})();
</script>
/* 返回按钮:从哪来回哪去 */
(function() {
var backBtn = document.getElementById('backBtn');
if (backBtn) {
var params = new URLSearchParams(window.location.search);
var referer = params.get('referer');
if (referer) {
backBtn.href = referer;
} else if (document.referrer && document.referrer.indexOf(window.location.hostname) !== -1) {
backBtn.href = document.referrer;
} else {
backBtn.href = 'index.html';
}
}
})();
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -1,258 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>净菜配比秤 - 打包记录 | 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif; background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden; }
.top-bar { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.back-btn { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); text-decoration: none; transition: color 0.2s; }
.back-btn:hover { color: #fff; }
.top-bar__title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.top-bar__user { font-size: 12px; color: rgba(255,255,255,0.5); }
.main-content { height: calc(100% - 44px); padding: 20px 28px; display: flex; flex-direction: column; gap: 16px; }
.filter-bar { display: flex; align-items: center; gap: 12px; }
.filter-group { display: flex; align-items: center; gap: 8px; }
.filter-label { font-size: 12px; color: rgba(255,255,255,0.5); }
.filter-select { background: #1a2b4a; border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; padding: 8px 12px; font-size: 12px; color: #fff; outline: none; cursor: pointer; }
.filter-input { background: #1a2b4a; border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; padding: 8px 12px; font-size: 12px; color: #fff; outline: none; width: 140px; }
.filter-btn { padding: 8px 16px; border-radius: 6px; font-size: 12px; background: #4a90d9; color: #fff; border: none; cursor: pointer; }
.filter-btn:hover { background: #5ba0e9; }
.filter-btn--secondary { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.8); }
.filter-btn--secondary:hover { background: rgba(255,255,255,0.12); }
.stats-row { display: flex; gap: 14px; }
.stat-card { flex: 1; background: #1a2b4a; border-radius: 12px; padding: 16px 20px; border: 1px solid rgba(255,255,255,0.06); }
.stat-card__label { font-size: 12px; color: rgba(255,255,255,0.5); margin-bottom: 6px; }
.stat-card__value { font-size: 24px; font-weight: 700; color: #fff; }
.stat-card__value--green { color: #52c41a; }
.stat-card__value--blue { color: #4a90d9; }
.records-section { flex: 1; background: #1a2b4a; border-radius: 12px; padding: 16px 20px; display: flex; flex-direction: column; overflow: hidden; }
.records-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.records-title { font-size: 14px; font-weight: 600; color: rgba(255,255,255,0.8); }
.records-count { font-size: 12px; color: rgba(255,255,255,0.4); }
.table-header { display: flex; align-items: center; padding: 10px 16px; background: rgba(0,0,0,0.2); border-radius: 8px; margin-bottom: 8px; }
.table-header__cell { font-size: 12px; color: rgba(255,255,255,0.5); font-weight: 500; }
.col-batch { flex: 0 0 130px; }
.col-name { flex: 0 0 100px; }
.col-target { flex: 0 0 80px; }
.col-weight { flex: 0 0 80px; }
.col-time { flex: 0 0 80px; }
.col-seal { flex: 0 0 80px; }
.col-operator { flex: 0 0 80px; }
.col-status { flex: 0 0 80px; }
.col-action { flex: 1; text-align: right; }
.records-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.1) transparent; }
.records-list::-webkit-scrollbar { width: 4px; }
.records-list::-webkit-scrollbar-track { background: transparent; }
.records-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }
.record-row { display: flex; align-items: center; padding: 12px 16px; background: rgba(0,0,0,0.15); border-radius: 8px; transition: background 0.2s; }
.record-row:hover { background: rgba(74,144,217,0.08); }
.record-row__cell { font-size: 13px; color: rgba(255,255,255,0.8); }
.record-row__cell--icon { font-size: 18px; }
.seal-badge { display: inline-block; padding: 2px 6px; border-radius: 3px; font-size: 11px; background: rgba(74,144,217,0.15); color: #4a90d9; }
.status-badge { display: inline-block; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; }
.status-badge--done { background: rgba(82,196,26,0.15); color: #52c41a; }
.action-link { font-size: 12px; color: #4a90d9; cursor: pointer; text-decoration: none; }
.action-link:hover { text-decoration: underline; }
.pagination { display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding-top: 14px; border-top: 1px solid rgba(255,255,255,0.06); }
.pagination__info { font-size: 12px; color: rgba(255,255,255,0.4); margin-right: auto; }
.pagination__btn { width: 32px; height: 32px; border-radius: 6px; display: flex; align-items: center; justify-content: center; background: rgba(255,255,255,0.06); color: rgba(255,255,255,0.6); font-size: 12px; border: none; cursor: pointer; transition: all 0.2s; }
.pagination__btn:hover { background: rgba(255,255,255,0.1); color: #fff; }
.pagination__btn.active { background: #4a90d9; color: #fff; }
.pagination__btn:disabled { opacity: 0.4; cursor: not-allowed; }
</style>
</head>
<body>
<div class="top-bar">
<a class="back-btn" id="backBtn" href="javascript:void(0)">&#x25C0; 返回</a> <span class="top-bar__title">打包记录</span>
<span class="top-bar__user" id="topUser">--</span>
</div>
<div class="main-content">
<div class="filter-bar">
<div class="filter-group"><span class="filter-label">日期</span><input type="date" class="filter-input" value="2026-06-13"></div>
<div class="filter-group">
<span class="filter-label">食材</span>
<select class="filter-select"><option value="">全部</option><option value="potato">土豆丝</option><option value="carrot">胡萝卜丝</option><option value="cucumber">黄瓜片</option><option value="cabbage">白菜段</option><option value="tomato">西红柿块</option></select>
</div>
<div class="filter-group">
<span class="filter-label">目标员工</span>
<select class="filter-select"><option value="">全部</option><option>张三</option><option>李四</option><option>王五</option><option>赵六</option><option>孙七</option></select>
</div>
<button class="filter-btn">查询</button>
<button class="filter-btn filter-btn--secondary">重置</button>
</div>
<div class="stats-row">
<div class="stat-card"><div class="stat-card__label">今日打包批次</div><div class="stat-card__value stat-card__value--blue">15</div></div>
<div class="stat-card"><div class="stat-card__label">打包总量</div><div class="stat-card__value">48.6斤</div></div>
<div class="stat-card"><div class="stat-card__label">分配员工数</div><div class="stat-card__value">8</div></div>
<div class="stat-card"><div class="stat-card__label">塑封完成率</div><div class="stat-card__value stat-card__value--green">100%</div></div>
</div>
<div class="records-section">
<div class="records-header">
<span class="records-title">打包记录列表</span>
<span class="records-count">共 15 条记录</span>
</div>
<div class="table-header">
<div class="table-header__cell col-batch">批次号</div>
<div class="table-header__cell col-name">食材</div>
<div class="table-header__cell col-target">目标员工</div>
<div class="table-header__cell col-weight">重量</div>
<div class="table-header__cell col-time">打包时间</div>
<div class="table-header__cell col-seal">塑封</div>
<div class="table-header__cell col-operator">操作员</div>
<div class="table-header__cell col-status">状态</div>
<div class="table-header__cell col-action">操作</div>
</div>
<div class="records-list">
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-001</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F954;</span> 土豆丝</div>
<div class="record-row__cell col-target">张三</div>
<div class="record-row__cell col-weight">5.2斤</div>
<div class="record-row__cell col-time">09:45</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-002</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F955;</span> 胡萝卜丝</div>
<div class="record-row__cell col-target">李四</div>
<div class="record-row__cell col-weight">2.8斤</div>
<div class="record-row__cell col-time">09:58</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-003</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F952;</span> 黄瓜片</div>
<div class="record-row__cell col-target">王五</div>
<div class="record-row__cell col-weight">4.2斤</div>
<div class="record-row__cell col-time">10:12</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-004</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F96C;</span> 白菜段</div>
<div class="record-row__cell col-target">赵六</div>
<div class="record-row__cell col-weight">8.5斤</div>
<div class="record-row__cell col-time">10:28</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-005</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F345;</span> 西红柿块</div>
<div class="record-row__cell col-target">孙七</div>
<div class="record-row__cell col-weight">3.5斤</div>
<div class="record-row__cell col-time">10:42</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-006</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F954;</span> 土豆丝</div>
<div class="record-row__cell col-target">周八</div>
<div class="record-row__cell col-weight">3.0斤</div>
<div class="record-row__cell col-time">10:55</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-007</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F954;</span> 土豆块</div>
<div class="record-row__cell col-target">张三</div>
<div class="record-row__cell col-weight">3.2斤</div>
<div class="record-row__cell col-time">14:30</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-008</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F955;</span> 胡萝卜丁</div>
<div class="record-row__cell col-target">陈十</div>
<div class="record-row__cell col-weight">2.5斤</div>
<div class="record-row__cell col-time">14:45</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-009</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F954;</span> 土豆条</div>
<div class="record-row__cell col-target">刘九</div>
<div class="record-row__cell col-weight">2.6斤</div>
<div class="record-row__cell col-time">14:58</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
<div class="record-row">
<div class="record-row__cell col-batch">#P20260613-010</div>
<div class="record-row__cell col-name"><span class="record-row__cell--icon">&#x1F345;</span> 小番茄</div>
<div class="record-row__cell col-target">杨一</div>
<div class="record-row__cell col-weight">1.8斤</div>
<div class="record-row__cell col-time">15:10</div>
<div class="record-row__cell col-seal"><span class="seal-badge">&#x1F512; 已塑封</span></div>
<div class="record-row__cell col-operator">刘九</div>
<div class="record-row__cell col-status"><span class="status-badge status-badge--done">已完成</span></div>
<div class="record-row__cell col-action"><a class="action-link">查看详情</a></div>
</div>
</div>
<div class="pagination">
<span class="pagination__info">共 15 条,第 1/2 页</span>
<button class="pagination__btn" disabled>&#x2039;</button>
<button class="pagination__btn active">1</button>
<button class="pagination__btn">2</button>
<button class="pagination__btn">&#x203A;</button>
</div>
</div>
</div>
<script>
function getUrlParam(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r !== null) return decodeURIComponent(r[2]);
return null;
}
var userName = getUrlParam('user');
if (userName) document.getElementById('topUser').textContent = '操作员: ' + userName;
</script>
/* 返回按钮:从哪来回哪去 */
(function() {
var backBtn = document.getElementById('backBtn');
if (backBtn) {
var params = new URLSearchParams(window.location.search);
var referer = params.get('referer');
if (referer) {
backBtn.href = referer;
} else if (document.referrer && document.referrer.indexOf(window.location.hostname) !== -1) {
backBtn.href = document.referrer;
} else {
backBtn.href = 'index.html';
}
}
})();
</body>
</html>
@@ -1,189 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1280, initial-scale=1.0">
<title>净菜配比秤 - 设备设置 | 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif; background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden; }
.top-bar { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.back-btn { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); text-decoration: none; transition: color 0.2s; }
.back-btn:hover { color: #fff; }
.top-bar__title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.top-bar__user { font-size: 12px; color: rgba(255,255,255,0.5); }
.main-content { height: calc(100% - 44px); padding: 24px 32px; display: flex; gap: 24px; }
.settings-menu { flex: 0 0 200px; background: #1a2b4a; border-radius: 12px; padding: 12px; display: flex; flex-direction: column; gap: 4px; }
.menu-item { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-radius: 8px; cursor: pointer; font-size: 13px; color: rgba(255,255,255,0.6); transition: all 0.2s; }
.menu-item:hover { background: rgba(255,255,255,0.06); color: #fff; }
.menu-item.active { background: rgba(74,144,217,0.15); color: #4a90d9; }
.menu-item__icon { font-size: 16px; }
.settings-content { flex: 1; background: #1a2b4a; border-radius: 12px; padding: 24px 28px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.1) transparent; }
.settings-content::-webkit-scrollbar { width: 4px; }
.settings-content::-webkit-scrollbar-track { background: transparent; }
.settings-content::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }
.settings-section { margin-bottom: 28px; }
.settings-section:last-child { margin-bottom: 0; }
.section-title { font-size: 14px; font-weight: 600; color: rgba(255,255,255,0.8); padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.08); margin-bottom: 16px; }
.setting-item { display: flex; align-items: center; padding: 14px 0; border-bottom: 1px solid rgba(255,255,255,0.04); }
.setting-item:last-child { border-bottom: none; }
.setting-item__info { flex: 1; }
.setting-item__label { font-size: 13px; color: rgba(255,255,255,0.8); margin-bottom: 2px; }
.setting-item__desc { font-size: 11px; color: rgba(255,255,255,0.4); }
.setting-item__control { flex: 0 0 auto; }
.setting-input { background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; padding: 8px 12px; font-size: 13px; color: #fff; outline: none; width: 120px; text-align: right; }
.setting-input:focus { border-color: #4a90d9; }
.setting-select { background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; padding: 8px 12px; font-size: 13px; color: #fff; outline: none; cursor: pointer; }
.toggle { width: 44px; height: 24px; border-radius: 12px; background: rgba(255,255,255,0.15); position: relative; cursor: pointer; transition: background 0.2s; }
.toggle.on { background: #4a90d9; }
.toggle__knob { width: 20px; height: 20px; border-radius: 50%; background: #fff; position: absolute; top: 2px; left: 2px; transition: left 0.2s; }
.toggle.on .toggle__knob { left: 22px; }
.device-info { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.info-item { background: rgba(0,0,0,0.15); border-radius: 10px; padding: 14px 16px; }
.info-item__label { font-size: 11px; color: rgba(255,255,255,0.4); margin-bottom: 4px; }
.info-item__value { font-size: 14px; color: #fff; font-weight: 500; }
.info-item__value--online { color: #52c41a; }
.save-btn { display: block; width: 100%; padding: 14px; border-radius: 10px; background: #4a90d9; color: #fff; font-size: 14px; font-weight: 600; border: none; cursor: pointer; transition: all 0.2s; margin-top: 24px; }
.save-btn:hover { background: #5ba0e9; }
.toast { position: fixed; top: 60px; left: 50%; transform: translateX(-50%) translateY(-20px); background: rgba(0,0,0,0.85); color: #fff; padding: 12px 28px; border-radius: 8px; font-size: 13px; z-index: 1000; opacity: 0; visibility: hidden; transition: opacity 0.3s, transform 0.3s, visibility 0.3s; white-space: nowrap; pointer-events: none; }
.toast.show { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.toast--success { background: rgba(82,196,26,0.92); }
</style>
</head>
<body>
<div class="top-bar">
<a class="back-btn" id="backBtn" href="javascript:void(0)">&#x25C0; 返回</a> <span class="top-bar__title">设备设置</span>
<span class="top-bar__user" id="topUser">--</span>
</div>
<div class="main-content">
<div class="settings-menu">
<div class="menu-item active" onclick="switchSection('packing', this)"><span class="menu-item__icon">&#x1F4E6;</span><span>打包参数</span></div>
<div class="menu-item" onclick="switchSection('ai', this)"><span class="menu-item__icon">&#x1F916;</span><span>AI识别</span></div>
<div class="menu-item" onclick="switchSection('scale', this)"><span class="menu-item__icon">&#x2696;&#xFE0F;</span><span>秤参数</span></div>
<div class="menu-item" onclick="switchSection('device', this)"><span class="menu-item__icon">&#x1F4BB;</span><span>设备信息</span></div>
<div class="menu-item" onclick="switchSection('about', this)"><span class="menu-item__icon">&#x2139;&#xFE0F;</span><span>关于</span></div>
</div>
<div class="settings-content">
<div class="settings-section" id="section-packing">
<div class="section-title">打包参数配置</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">自动塑封</div><div class="setting-item__desc">打包完成后自动塑封</div></div>
<div class="setting-item__control"><div class="toggle on" onclick="toggleSwitch(this)"><div class="toggle__knob"></div></div></div>
</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">标签打印</div><div class="setting-item__desc">打包完成后自动打印标签</div></div>
<div class="setting-item__control"><div class="toggle on" onclick="toggleSwitch(this)"><div class="toggle__knob"></div></div></div>
</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">重量误差范围</div><div class="setting-item__desc">打包重量与目标重量的允许误差</div></div>
<div class="setting-item__control"><input type="number" class="setting-input" value="0.2" min="0.1" max="1" step="0.1"> <span style="color: rgba(255,255,255,0.5); font-size: 12px; margin-left: 4px"></span></div>
</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">打包完成提示音</div><div class="setting-item__desc">打包完成时播放提示音</div></div>
<div class="setting-item__control"><div class="toggle on" onclick="toggleSwitch(this)"><div class="toggle__knob"></div></div></div>
</div>
</div>
<div class="settings-section" id="section-ai" style="display:none">
<div class="section-title">AI 识别设置</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">识别置信度阈值</div><div class="setting-item__desc">低于此值将提示人工确认</div></div>
<div class="setting-item__control"><input type="number" class="setting-input" value="85" min="60" max="99"> <span style="color: rgba(255,255,255,0.5); font-size: 12px; margin-left: 4px">%</span></div>
</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">不匹配自动告警</div><div class="setting-item__desc">AI识别与任务不匹配时弹窗提示</div></div>
<div class="setting-item__control"><div class="toggle on" onclick="toggleSwitch(this)"><div class="toggle__knob"></div></div></div>
</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">连续识别模式</div><div class="setting-item__desc">放置食材后自动触发识别</div></div>
<div class="setting-item__control"><div class="toggle on" onclick="toggleSwitch(this)"><div class="toggle__knob"></div></div></div>
</div>
</div>
<div class="settings-section" id="section-scale" style="display:none">
<div class="section-title">秤参数配置</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">重量单位</div><div class="setting-item__desc">称重显示单位</div></div>
<div class="setting-item__control"><select class="setting-select"><option selected></option><option></option><option>千克</option></select></div>
</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">小数位数</div><div class="setting-item__desc">重量显示精度</div></div>
<div class="setting-item__control"><select class="setting-select"><option>1位</option><option selected>2位</option></select></div>
</div>
<div class="setting-item">
<div class="setting-item__info"><div class="setting-item__label">稳定判定时间</div><div class="setting-item__desc">重量稳定后自动锁定的等待时间</div></div>
<div class="setting-item__control"><input type="number" class="setting-input" value="3" min="1" max="10"> <span style="color: rgba(255,255,255,0.5); font-size: 12px; margin-left: 4px"></span></div>
</div>
</div>
<div class="settings-section" id="section-device" style="display:none">
<div class="section-title">设备信息</div>
<div class="device-info">
<div class="info-item"><div class="info-item__label">设备名称</div><div class="info-item__value">净菜配比秤-01</div></div>
<div class="info-item"><div class="info-item__label">设备编号</div><div class="info-item__value">JC PB-2026-001</div></div>
<div class="info-item"><div class="info-item__label">固件版本</div><div class="info-item__value">v2.3.1</div></div>
<div class="info-item"><div class="info-item__label">AI模型版本</div><div class="info-item__value">pack-ai-v2.5</div></div>
<div class="info-item"><div class="info-item__label">设备状态</div><div class="info-item__value info-item__value--online">在线</div></div>
<div class="info-item"><div class="info-item__label">网络状态</div><div class="info-item__value info-item__value--online">已连接</div></div>
<div class="info-item"><div class="info-item__label">上次校准</div><div class="info-item__value">2026-06-10</div></div>
<div class="info-item"><div class="info-item__label">累计使用</div><div class="info-item__value">128天</div></div>
</div>
</div>
<div class="settings-section" id="section-about" style="display:none">
<div class="section-title">关于</div>
<div class="device-info">
<div class="info-item"><div class="info-item__label">系统名称</div><div class="info-item__value">蚁熊智能餐养平台</div></div>
<div class="info-item"><div class="info-item__label">系统版本</div><div class="info-item__value">v5.8.0</div></div>
<div class="info-item"><div class="info-item__label">设备类型</div><div class="info-item__value">净菜配比秤</div></div>
<div class="info-item"><div class="info-item__label">制造商</div><div class="info-item__value">蚁熊智能科技有限公司</div></div>
</div>
</div>
<button class="save-btn" onclick="saveSettings()">保存设置</button>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
function getUrlParam(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r !== null) return decodeURIComponent(r[2]);
return null;
}
var userName = getUrlParam('user');
if (userName) document.getElementById('topUser').textContent = '操作员: ' + userName;
function switchSection(section, el) {
document.querySelectorAll('.menu-item').forEach(function(item) { item.classList.remove('active'); });
el.classList.add('active');
document.querySelectorAll('.settings-section').forEach(function(sec) { sec.style.display = 'none'; });
document.getElementById('section-' + section).style.display = 'block';
}
function toggleSwitch(el) { el.classList.toggle('on'); }
function saveSettings() { showToast('设置已保存', 2000, 'success'); }
function showToast(msg, duration, type) {
var el = document.getElementById('toast');
el.textContent = msg;
el.className = 'toast';
if (type === 'success') el.classList.add('toast--success');
el.classList.add('show');
clearTimeout(el._timer);
el._timer = setTimeout(function() { el.classList.remove('show'); }, duration || 2000);
}
</script>
/* 返回按钮:从哪来回哪去 */
(function() {
var backBtn = document.getElementById('backBtn');
if (backBtn) {
var params = new URLSearchParams(window.location.search);
var referer = params.get('referer');
if (referer) {
backBtn.href = referer;
} else if (document.referrer && document.referrer.indexOf(window.location.hostname) !== -1) {
backBtn.href = document.referrer;
} else {
backBtn.href = 'index.html';
}
}
})();
</body>
</html>
@@ -1,5 +1,43 @@
# 净菜配比秤 CHANGELOG
## [1.2.0] - 2026-06-16
### 新增
- 称重打包页食材识别区底部新增「当前秤重」实时显示
- 净菜清清单表格新增「操作」列,支持单独清空食材重量
- 称重打包页新增「下一步」按钮,点击后弹出包装信息窗口
- 包装信息弹窗包含:包装方式(必填)、保质期至(必填/三列下拉)、存放温度、终端设备(下拉)
### 调整
- 表头列名优化:「每份用量」→「每份用量(克)」、「打包规格」→「打包规格(份)」
- 称重打包页删除「保存并完成」「保存并继续」按钮,统一改为「下一步」弹窗流程
- 全局去除「今日」字样:标题、导航、卡片描述统一精简
- 保质期至由浏览器原生日期控件改为自定义三列下拉(年/月/日)
### 修复
- 任务列表页「待称重」Tab 初始化时未过滤已完成订单
---
## [1.1.0] - 2026-06-16
### 重构
- 登录页与菜单页分离:新增 `00-login.html` 独立登录页,`00-welcome.html` 改为纯功能菜单页
- 任务列表页重构为三层结构:订单号(第一层)→ 任务号(第二层)→ 净菜项(第三层)
- 新增 Tab 切换:「净菜包打包」/「餐品组合打包」两种模式
- 称重打包页支持两种模式:净菜包模式(AI识别称重)和餐品组合模式(自动匹配子项)
- 所有页面统一 PAD 居中外壳(`.device-frame` 1280×720
### 删除
- `03-history.html` — 打包记录页(冗余功能移除)
- `04-settings.html` — 设备设置页(冗余功能移除)
### 调整
- 导航页 `index.html` 同步更新页面列表和描述
- 任务列表按期望到货时间升序排列
---
## [1.0.0] - 2026-06-13
### 新增
@@ -0,0 +1,232 @@
# 净菜配比秤 — 产品需求文档
> 原型目录:`other-module/canteen-device/ratio-scale/`
> 创建时间:2026-06-13 | 最后更新:2026-06-16
---
## 一、模块概述
净菜配比秤是食堂净菜打包流程中的核心硬件终端,承担打包任务管理、净菜识别称重、餐品组合打包三大职能。设备配备触摸屏、称重传感器、食材识别摄像头和人脸识别摄像头,支持「净菜包打包」和「餐品组合打包」两种业务模式。
### 业务流程
```
用户登录 → 查看打包任务(按期望到货时间排序)→ 净材识别录入 → 完成任务
```
**任务结构(三层)**
- 第一层:打包任务(关联后台系统外采内供订单号)
- 第二层:净菜/餐品(净菜包 或 餐品净菜包)
- 第三层:餐品净菜包的净菜明细(仅餐品净菜包类型)
**打包流程**
1. 先按照任务把所有净菜包打包完成
2. 随后查询任务单里的餐品净菜包,系统自动匹配已打包净菜进行组合打包
### 页面导航结构
```
00 登录页(人脸识别 / 账号密码登录)
└── 登录成功后进入
00 功能菜单页
├── 今日任务 → 01-packing-tasks
│ ├── Tab: 净菜包打包
│ └── Tab: 餐品组合打包
└── 称重打包 → 02-weighing-packing
├── 模式 A: 净菜包打包(AI 识别 + 称重 + 塑封)
── 模式 B: 餐品组合打包(自动匹配子项)
```
#### 页面间跳转关系
| 来源页 | 触发操作 | 目标页 | 携带参数 |
|--------|----------|--------|----------|
| 根导航(index.html) | 进入净菜配比秤 | 00 登录页 | — |
| 00 登录页 | 登录成功 | 00 功能菜单页 | `?user=姓名&uid=工号` |
| 00 功能菜单页 | 点击「今日任务」 | 01 任务列表页 | `?user=...&uid=...` |
| 00 功能菜单页 | 点击「称重打包」 | 02 称重打包页 | `?user=...&uid=...` |
| 00 功能菜单页 | 点击「退出登录」 | 00 登录页 | — |
| 01 任务列表页 | 点击「去打包」 | 02 称重打包页 | `?task=任务号&item=序号&type=veg/meal&user=...` |
| 01 任务列表页 | 顶栏 ← 返回 | 00 功能菜单页 | — |
| 02 称重打包页 | 顶栏 ← 返回 | 01 任务列表页 | — |
### 核心状态流转
```mermaid
flowchart TD
A[登录页] -->|人脸识别/账号登录| B[功能菜单页]
B -->|今日任务| C[任务列表页]
B -->|称重打包| D[称重打包页]
B -->|退出登录| A
C -->|Tab: 净菜包打包| C1[净菜包任务列表]
C -->|Tab: 餐品组合打包| C2[餐品组合任务列表]
C1 -->|点击去打包| D1[净菜包模式]
C2 -->|点击去打包| D2[餐品组合模式]
D1 --> E[放置食材]
E --> F[AI 识别品类]
F --> G[称重稳定]
G --> H[打包 + 塑封]
H -->|继续打包| E
H -->|返回| C
D2 --> I[放置食材]
I --> J[AI 识别净菜]
J --> K{匹配子项?}
K -->|匹配成功| L[标记子项完成]
K -->|未匹配| M[提示不匹配]
L --> N{所有子项完成?}
N -->|是| O[组合打包]
N -->|否| I
O -->|返回| C
```
---
## 二、页面清单
| 编号 | 文件名 | 页面名称 | 类型 | 原型链接 |
|------|--------|----------|------|----------|
| 00 | `00-login.html` | 登录页 | 功能页 | [打开原型](../00-login.html) |
| 00 | `00-welcome.html` | 功能菜单页 | 功能页 | [打开原型](../00-welcome.html) |
| 01 | `01-packing-tasks.html` | 今日打包任务 | 功能页 | [打开原型](../01-packing-tasks.html) |
| 02 | `02-weighing-packing.html` | 称重打包 | 功能页 | [打开原型](../02-weighing-packing.html) |
---
## 三、页面详细说明
### 3.1 登录页 `00-login.html`
**页面定位**:员工身份验证入口
**功能说明**
- 支持人脸识别和账号密码两种登录方式
- 登录成功后自动跳转到功能菜单页
- 显示登录员工姓名、部门、工号信息
**交互说明**
- 点击人脸识别区 → 启动识别动画 → 2 秒后模拟识别成功
- 切换登录方式 Tab → 切换显示人脸/密码表单
- 账号密码登录 → 验证工号和密码 → 成功则跳转
**演示账号**
| 工号 | 密码 | 姓名 | 角色 |
|------|------|------|------|
| 30001 | 123456 | 刘九 | 打包员 |
| 30002 | 123456 | 陈十 | 打包员 |
| 30003 | 123456 | 杨一 | 质检员 |
---
### 3.2 功能菜单页 `00-welcome.html`
**页面定位**:登录后功能导航入口
**功能说明**
- 显示当前登录用户信息
- 提供「今日任务」和「称重打包」两个功能入口
- 支持退出登录返回登录页
- 新任务到达时显示脉冲动画和角标提示
**交互说明**
- 页面加载时检查登录状态,未登录自动跳转登录页
- 每 30 秒轮询检测新任务到达
- 点击功能按钮携带用户参数跳转到对应页面
---
### 3.3 今日打包任务 `01-packing-tasks.html`
**页面定位**:打包任务列表管理
**功能说明**
- Tab 切换:「净菜包打包」/「餐品组合打包」
- 三层结构展示:订单号 → 任务号 → 净菜项
- 按期望到货时间升序排列(最紧急的在最上面)
- 显示每种净菜的打包进度和状态
**数据结构**
```
订单 #PO-20260616-001(期望到货 11:30
└── 任务 #T-001
├── 🥔 土豆丝(净菜包)需 2.5 斤 已包 1.0 斤 [进行中]
├── 🥕 胡萝卜丝(净菜包)需 1.8 斤 已包 1.8 斤 [已完成]
└── 📦 宫保鸡丁套餐(餐品净菜包)需 3 份 已包 0 份
├── 土豆丝 1.5 斤
├── 胡萝卜丝 0.8 斤
└── 黄瓜片 0.5 斤
```
**交互说明**
- 点击净菜包「去打包」→ 跳转称重打包页(净菜包模式)
- 点击餐品净菜包「去打包」→ 跳转称重打包页(餐品组合模式)
- 已完成项显示「完成」按钮,不可再次操作
---
### 3.4 称重打包 `02-weighing-packing.html`
**页面定位**:净菜识别、称重、打包操作
**功能说明**
**净菜包模式**
- AI 识别放置的食材品类
- 实时称重显示
- 重量稳定后点击「打包 + 塑封」
- 记录本次打包历史
**餐品组合模式**
- 显示餐品净菜包所需的子项清单
- 秤上放置食材时系统自动识别并匹配对应子项
- 匹配成功的子项显示绿色勾选
- 所有子项匹配完成后可点击「组合打包」
**交互说明**
- 从任务列表进入时带入任务参数(任务号、净菜序号、类型)
- 称重过程动画:数字递增 → 稳定 → 可操作
- AI 识别结果与任务比对:匹配显示绿色,不匹配显示红色警告
- 打包成功后记录自动添加到右侧打包记录列表
---
## 四、设计规范
### 4.1 视觉尺寸
- 设备分辨率:1280 × 720(PAD 横屏)
- 顶部导航栏高度:44px
- 内容区高度:calc(100% - 44px)
### 4.2 设计令牌
| 用途 | 颜色值 |
|------|--------|
| 背景色 | `#0e1326` |
| 卡片背景 | `#1a2b4a` |
| 主色(按钮/高亮) | `#4a90d9` |
| 成功色 | `#52c41a` |
| 警告色 | `#faad16` |
| 错误色 | `#ff4d4f` |
| 餐品净菜包色 | `#722ed1` |
### 4.3 PAD 外壳规范
所有页面统一采用 `.device-frame` 容器:
- 圆角:24px
- 阴影:`0 24px 64px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)`
- 内部禁止可见滚动条(`scrollbar-width: none` + `::-webkit-scrollbar { display: none }`
---
## 五、待办事项
- [ ] 对接真实硬件称重传感器 API
- [ ] 对接真实 AI 食材识别 API
- [ ] 对接后台系统任务同步接口
- [ ] 打包记录本地存储与同步
- [ ] 打印标签功能(如有硬件支持)
@@ -6,7 +6,23 @@
<title>净菜配比秤 - 蚁熊智能餐养平台</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif; background: #0e1326; color: #fff; width: 1280px; height: 720px; overflow: hidden; }
body {
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
background: #05080f; color: #fff;
min-height: 100vh; min-width: 1280px;
display: flex; align-items: center; justify-content: center;
padding: 20px;
}
/* PAD 设备外壳 */
.device-frame {
width: 1280px; height: 720px;
background: #0e1326;
border-radius: 24px;
box-shadow: 0 24px 64px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04);
overflow: hidden;
position: relative;
display: flex; flex-direction: column;
}
.top-bar { display: flex; align-items: center; height: 44px; background: #1a2b4a; padding: 0 20px; }
.top-bar__title { flex: 1; text-align: center; font-size: 15px; font-weight: 600; color: rgba(255,255,255,0.85); }
.back-link { font-size: 14px; cursor: pointer; color: rgba(255,255,255,0.7); text-decoration: none; }
@@ -22,51 +38,37 @@
</style>
</head>
<body>
<div class="top-bar">
<a class="back-link" href="../index.html">&#x25C0; 返回</a>
<span class="top-bar__title">净菜配比秤</span>
</div>
<div class="main-content">
<a class="card" href="00-welcome.html">
<span class="card__icon">&#x1F44B;</span>
<div class="card__info">
<div class="card__title">欢迎页面</div>
<div class="card__desc">人脸识别/账号密码登录、功能菜单入口</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
<a class="card" href="01-packing-tasks.html">
<span class="card__icon">&#x1F4CB;</span>
<div class="card__info">
<div class="card__title">今日打包任务</div>
<div class="card__desc">系统分配的净菜打包任务列表</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
<a class="card" href="02-weighing-packing.html">
<span class="card__icon">&#x1F4E6;</span>
<div class="card__info">
<div class="card__title">称重打包</div>
<div class="card__desc">净菜AI识别、称重、按员工拆单打包塑封</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
<a class="card" href="03-history.html">
<span class="card__icon">&#x1F4DD;</span>
<div class="card__info">
<div class="card__title">打包记录</div>
<div class="card__desc">历史打包记录查看与筛选</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
<a class="card" href="04-settings.html">
<span class="card__icon">&#x2699;&#xFE0F;</span>
<div class="card__info">
<div class="card__title">设备设置</div>
<div class="card__desc">打包参数、AI识别、秤参数配置</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
<div class="device-frame">
<div class="top-bar">
<a class="back-link" href="../index.html">&#x25C0; 返回</a>
<span class="top-bar__title">净菜配比秤</span>
</div>
<div class="main-content">
<a class="card" href="00-login.html">
<span class="card__icon">&#x1F511;</span>
<div class="card__info">
<div class="card__title">员工登录</div>
<div class="card__desc">人脸识别/账号密码登录入口</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
<a class="card" href="00-welcome.html">
<span class="card__icon">&#x1F4CB;</span>
<div class="card__info">
<div class="card__title">功能菜单</div>
<div class="card__desc">称重任务入口</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
<a class="card" href="01-packing-tasks.html">
<span class="card__icon">&#x1F4E6;</span>
<div class="card__info">
<div class="card__title">称重任务</div>
<div class="card__desc">三层任务结构:订单→任务→净菜项,点击去称重直接进入称重页</div>
</div>
<span class="card__arrow">&#x203A;</span>
</a>
</div>
</div>
</body>
</html>