Files
2026-09-08 14:05:30 +08:00

52 lines
20 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>柏悦物流 PDA · 仓储作业平台</title>
<link rel="icon" href="data:,">
<style>
:root { --primary:#1769e0; --primary-deep:#123b72; --primary-soft:#eaf2ff; --bg:#f3f6fa; --card:#fff; --text:#172b4d; --text-secondary:#6b788c; --border:#d9e4f2; --danger:#dc2626; --success:#0d9488; --shadow:0 4px 14px rgba(18,59,114,.08); }
* { box-sizing:border-box; margin:0; padding:0; }
body { min-height:100vh; max-width:480px; margin:0 auto; overflow-x:hidden; background:var(--bg); color:var(--text); font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"PingFang SC","Noto Sans SC",sans-serif; }
button,input { font:inherit; }
.hidden { display:none !important; }
.toast { position:fixed; top:70px; left:50%; z-index:400; padding:10px 20px; border-radius:8px; transform:translateX(-50%) translateY(-20px); opacity:0; color:#fff; background:#1f2937; transition:all .3s; pointer-events:none; }
.toast.show { transform:translateX(-50%) translateY(0); opacity:1; }
.toast.success { background:var(--success); }
.login-page { display:flex; flex-direction:column; justify-content:center; min-height:100vh; padding:32px 24px; background:linear-gradient(160deg,#1769e0 0%,#0d47a1 100%); }
.login-logo { margin:0 -22px; padding:54px 22px 42px; text-align:left; }.login-brand-line { display:flex; align-items:center; gap:14px; }.login-logo-icon { display:flex; align-items:center; justify-content:center; width:62px; height:62px; border-radius:16px; background:rgba(255,255,255,.15); }.login-logo-icon svg { width:31px; height:31px; }.login-logo-title { color:#fff; font-size:21px; font-weight:700; }.login-logo-sub { margin-top:5px; color:rgba(255,255,255,.72); font-size:12px; }.login-status { display:flex; align-items:center; gap:6px; margin-top:25px; color:rgba(255,255,255,.8); font-size:11px; }.login-status::before { width:7px; height:7px; border-radius:50%; background:#48d5bc; box-shadow:0 0 0 4px rgba(72,213,188,.12); content:""; }
.login-card { padding:22px; border-radius:12px; background:#fff; box-shadow:0 8px 32px rgba(0,0,0,.18); }.login-card-title { color:#172033; font-size:18px; font-weight:750; }.login-card-desc { margin:5px 0 20px; color:#78869a; font-size:12px; }.login-field { margin-bottom:15px; }.login-field-label { margin-bottom:7px; color:#445168; font-size:13px; font-weight:600; }.login-input-wrap { position:relative; }.login-input-icon { position:absolute; top:50%; left:13px; display:flex; color:#8290a4; transform:translateY(-50%); }.login-input-icon svg,.password-toggle svg { width:18px; height:18px; }.login-field-input { width:100%; height:46px; padding:0 42px; border:1px solid #d7e0eb; border-radius:8px; outline:0; color:#243b53; background:#f9fbfd; font-size:14px; }.login-field-input:focus { border-color:var(--primary); background:#fff; box-shadow:0 0 0 3px rgba(23,105,224,.08); }.password-toggle { position:absolute; top:50%; right:10px; display:flex; align-items:center; justify-content:center; width:28px; height:28px; border:0; color:#8290a4; background:transparent; transform:translateY(-50%); cursor:pointer; }.login-error { display:none; margin-bottom:12px; padding:9px 11px; border-radius:7px; color:var(--danger); background:#fff1f1; font-size:13px; }.login-error.show { display:block; }.login-btn { width:100%; height:48px; margin-top:5px; border:0; border-radius:8px; color:#fff; background:#1769e0; box-shadow:0 6px 14px rgba(23,105,224,.2); font-size:16px; font-weight:600; cursor:pointer; }.login-demo-tip { margin-top:13px; padding:10px 12px; border-radius:7px; color:#6b788c; background:#f2f5f9; font-size:11px; text-align:center; }.login-accounts { margin-top:16px; padding-top:14px; border-top:1px solid #e3e8ef; }.login-accounts-title { margin-bottom:9px; color:#78869a; font-size:11px; text-align:left; }.login-quick-btns { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; }.login-quick-btn { padding:10px; border:1px solid #dbe4ee; border-radius:8px; color:#334e68; background:#fff; text-align:left; cursor:pointer; }.login-quick-btn-name,.login-quick-btn-role { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }.login-quick-btn-name { font-size:13px; font-weight:650; }.login-quick-btn-role { margin-top:3px; color:#78869a; font-size:11px; }
.workbench-page { --wb-brand:#155eef; --wb-brand-deep:#123b72; --wb-brand-surface:#f4f8ff; --wb-brand-border:#a7c4f4; --wb-surface:#fff; --wb-border:#d9e4f2; --wb-text:#172b4d; --wb-text-secondary:#627388; --wb-danger:#e5484d; }.app-shell { min-height:100vh; padding-bottom:28px; background:var(--bg); }.app-topbar { padding:15px 16px 24px; border-radius:0 0 16px 16px; color:#fff; background:linear-gradient(140deg,var(--wb-brand-deep),var(--wb-brand)); box-shadow:0 8px 20px rgba(18,59,114,.18); }.app-topbar-row,.welcome-card,.section-heading { display:flex; align-items:center; justify-content:space-between; }.app-topbar-row { gap:12px; }.app-brand { display:flex; align-items:center; gap:10px; }.app-brand-mark { display:flex; align-items:center; justify-content:center; width:36px; height:36px; border-radius:9px; background:rgba(255,255,255,.18); font-size:18px; }.app-brand-title { font-size:17px; font-weight:700; }.app-brand-sub { margin-top:2px; font-size:11px; opacity:.72; }.profile-entry { display:flex; align-items:center; justify-content:center; width:40px; height:40px; border:1px solid rgba(255,255,255,.25); border-radius:10px; color:#fff; background:rgba(255,255,255,.16); font-size:17px; font-weight:700; cursor:pointer; }.welcome-card { align-items:flex-end; gap:12px; margin-top:18px; }.welcome-kicker { font-size:12px; opacity:.75; }.welcome-name { margin-top:3px; font-size:21px; font-weight:700; }.welcome-meta { margin-top:5px; font-size:12px; opacity:.82; }.shift-badge { flex:none; padding:7px 10px; border-radius:10px; background:rgba(255,255,255,.15); font-size:11px; }.shift-badge::before { display:inline-block; width:7px; height:7px; margin-right:5px; border-radius:50%; background:#24b886; content:""; }.workbench-content { padding:14px 12px 22px; }.today-panel { display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-bottom:14px; }.today-stat { padding:12px 8px; border:1px solid var(--wb-border); border-radius:12px; background:var(--wb-surface); box-shadow:var(--shadow); text-align:center; }.today-stat-num { color:var(--wb-brand-deep); font-size:20px; font-weight:750; line-height:1.1; }.today-stat-label { margin-top:5px; color:var(--wb-text-secondary); font-size:11px; }.section-heading { margin:18px 2px 8px; }.section-heading-title { display:flex; align-items:center; gap:8px; color:var(--wb-text); font-size:15px; font-weight:700; }.section-heading-title::before { width:4px; height:16px; border-radius:3px; background:var(--wb-brand); content:""; }.module-card { padding:4px 6px; border:1px solid var(--wb-border); border-radius:10px; background:var(--wb-surface); box-shadow:var(--shadow); }.module-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }.module-item { position:relative; min-height:78px; margin:3px; padding:7px 3px; border:1px solid var(--wb-brand-border); border-radius:9px; color:#174ea6; background:var(--wb-brand-surface); box-shadow:0 4px 10px rgba(18,59,114,.1); cursor:pointer; }.module-icon { display:flex; align-items:center; justify-content:center; width:40px; height:40px; margin:0 auto 7px; border-radius:10px; color:#fff; background:var(--wb-brand); box-shadow:0 5px 12px rgba(18,59,114,.16); font-size:20px; }.module-name { font-size:12px; line-height:1.3; white-space:nowrap; }.module-badge { position:absolute; top:7px; right:5px; min-width:17px; height:17px; padding:0 4px; border-radius:9px; color:#fff; background:var(--wb-danger); font-size:9px; line-height:17px; }
.page-back { width:38px; height:38px; border:0; border-radius:11px; color:#fff; background:rgba(255,255,255,.14); font-size:20px; cursor:pointer; }.profile-cover { height:132px; padding:16px; color:#fff; background:linear-gradient(140deg,#123b72,#1769e0); }.profile-card { position:relative; margin:-49px 12px 12px; padding:18px; border-radius:12px; background:#fff; box-shadow:0 10px 28px rgba(20,49,82,.13); text-align:center; }.profile-avatar { display:flex; align-items:center; justify-content:center; width:68px; height:68px; margin:-50px auto 10px; border:4px solid #fff; border-radius:16px; color:#155eef; background:#eaf2ff; font-size:25px; font-weight:800; }.profile-name { color:#172b4d; font-size:19px; font-weight:700; }.profile-role { margin-top:4px; color:#728196; font-size:12px; }.profile-stats { display:grid; grid-template-columns:repeat(3,1fr); margin-top:16px; padding-top:14px; border-top:1px solid #edf1f5; }.profile-stat strong { display:block; color:#163b68; font-size:17px; }.profile-stat span { color:#8a97a8; font-size:10px; }.profile-list { margin:12px; padding:2px 14px; border:1px solid #e3e8ef; border-radius:10px; background:#fff; }.profile-list-item { display:flex; align-items:center; gap:12px; padding:15px 0; border-bottom:1px solid #edf1f5; color:#334e68; font-size:13px; }.profile-list-item:last-child { border-bottom:0; }.profile-list-icon { display:flex; align-items:center; justify-content:center; width:32px; height:32px; border-radius:8px; background:#eef4ff; }.profile-list-value { margin-left:auto; color:#829ab1; font-size:12px; }.profile-logout { width:calc(100% - 24px); margin:4px 12px 24px; padding:13px; border:1px solid #fecaca; border-radius:9px; color:#dc2626; background:#fff; font-size:14px; font-weight:600; cursor:pointer; }
@media (max-width:360px) { .login-page { padding-right:16px; padding-left:16px; } .login-logo { margin-right:-16px; margin-left:-16px; padding-right:18px; padding-left:18px; } .module-name { font-size:11px; } }
</style>
<link rel="stylesheet" href="domestic-overseas-theme.css">
</head>
<body>
<div class="toast" id="toast"></div>
<div class="login-page" id="loginPage"><div class="login-logo"><div class="login-brand-line"><div class="login-logo-icon"><svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 3v18"/></svg></div><div><div class="login-logo-title">柏悦物流 PDA</div><div class="login-logo-sub">仓储移动作业平台</div></div></div><div class="login-status">作业系统运行正常</div></div><div class="login-card"><div class="login-card-title">账号登录</div><div class="login-card-desc">请输入作业人员账号进入工作台</div><div class="login-field"><div class="login-field-label">用户名</div><div class="login-input-wrap"><span class="login-input-icon">♙</span><input class="login-field-input" id="loginUser" placeholder="请输入用户名" autocomplete="username"></div></div><div class="login-field"><div class="login-field-label">密码</div><div class="login-input-wrap"><span class="login-input-icon">▣</span><input class="login-field-input" id="loginPass" type="password" placeholder="请输入密码" autocomplete="current-password"><button class="password-toggle" id="passwordToggle" type="button" onclick="togglePassword()" aria-label="显示密码">◉</button></div></div><div class="login-error" id="loginError">用户名或密码错误</div><button class="login-btn" type="button" onclick="handleLogin()">登录</button><div class="login-demo-tip">演示账号:W001 密码:123456</div><div class="login-accounts"><div class="login-accounts-title">快捷登录</div><div class="login-quick-btns"><button class="login-quick-btn" type="button" onclick="quickLogin('W001','张伟','拣货员')"><span class="login-quick-btn-name">W001 · 张伟</span><span class="login-quick-btn-role">拣货员 · 洛杉矶仓</span></button><button class="login-quick-btn" type="button" onclick="quickLogin('W002','李敏','拣货组长')"><span class="login-quick-btn-name">W002 · 李敏</span><span class="login-quick-btn-role">拣货组长 · 洛杉矶仓</span></button></div></div></div></div>
<div class="workbench-page hidden" id="workbenchPage"><div class="app-shell"><div class="app-topbar"><div class="app-topbar-row"><div class="app-brand"><div class="app-brand-mark">▦</div><div><div class="app-brand-title">柏悦物流 PDA</div><div class="app-brand-sub">仓储移动作业平台</div></div></div><button class="profile-entry" id="workbenchAvatar" type="button" onclick="openProfile()" aria-label="个人中心">张</button></div><div class="welcome-card"><div><div class="welcome-kicker">欢迎回来</div><div class="welcome-name" id="workbenchUserName">张伟</div><div class="welcome-meta"><span id="workbenchRole">拣货员</span> · <span id="workbenchWarehouse">洛杉矶仓</span></div></div><div class="shift-badge">白班 · 作业中</div></div></div><div class="workbench-content"><div class="today-panel"><div class="today-stat"><div class="today-stat-num">3</div><div class="today-stat-label">待拣集装箱</div></div><div class="today-stat"><div class="today-stat-num">0</div><div class="today-stat-label">今日已完成</div></div><div class="today-stat"><div class="today-stat-num">17</div><div class="today-stat-label">待拣货品</div></div></div><div class="section-heading"><div class="section-heading-title">入库</div></div><div class="module-card"><div class="module-grid"><button class="module-item active-menu" id="receiveEntry" type="button" onclick="navigateToModule('入库/收货登记.html')"><span class="module-badge">3</span><span class="module-icon">📥</span><span class="module-name">收货登记</span></button><button class="module-item active-menu" id="palletEntry" type="button" onclick="navigateToModule('入库/入库打托.html')"><span class="module-badge">2</span><span class="module-icon">🪵</span><span class="module-name">入库打托</span></button></div></div><div class="section-heading"><div class="section-heading-title">出库</div></div><div class="module-card"><div class="module-grid" id="outboundModuleGrid"><button class="module-item active-menu" id="consolidationEntry" type="button" onclick="navigateToModule('出库/合托.html')"><span class="module-badge">1</span><span class="module-icon">🪵</span><span class="module-name">合托</span></button><button class="module-item active-menu" id="pickingEntry" type="button" onclick="navigateToModule('出库/集装箱拣货.html')"><span class="module-badge">3</span><span class="module-icon">📦</span><span class="module-name">集装箱拣货</span></button></div></div></div></div></div>
<div class="profile-page hidden" id="profilePage"><div class="app-shell"><div class="profile-cover"><button class="page-back" type="button" onclick="backFromProfile()">‹</button></div><div class="profile-card"><div class="profile-avatar" id="profileAvatar">张</div><div class="profile-name" id="profileName">张伟</div><div class="profile-role" id="profileRole">W001 · 拣货员</div><div class="profile-stats"><div class="profile-stat"><strong>28</strong><span>今日完成</span></div><div class="profile-stat"><strong>156</strong><span>本月任务</span></div><div class="profile-stat"><strong>99.2%</strong><span>作业准确率</span></div></div></div><div class="profile-list"><div class="profile-list-item"><span class="profile-list-icon">🏢</span><span>当前仓库</span><span class="profile-list-value" id="profileWarehouse">洛杉矶仓</span></div><div class="profile-list-item"><span class="profile-list-icon">🕘</span><span>当前班次</span><span class="profile-list-value">白班 08:00–17:00</span></div><div class="profile-list-item"><span class="profile-list-icon">🔐</span><span>修改密码</span><span class="profile-list-value">›</span></div><div class="profile-list-item"><span class="profile-list-icon">⚙️</span><span>系统设置</span><span class="profile-list-value">›</span></div><div class="profile-list-item"><span class="profile-list-icon">ℹ️</span><span>版本信息</span><span class="profile-list-value">V1.0.0</span></div></div><button class="profile-logout" type="button" onclick="handleLogout()">退出登录</button></div></div>
<script>
const DEMO_ACCOUNTS = { W001:{pass:'123456',name:'张伟',role:'拣货员',warehouse:'洛杉矶仓'}, W002:{pass:'123456',name:'李敏',role:'拣货组长',warehouse:'洛杉矶仓'}, W003:{pass:'123456',name:'王磊',role:'仓管员',warehouse:'新泽西仓'} };
const USER_STORAGE_KEY = 'pdaCurrentUser'; let currentUser = null; let toastTimer = null;
function buildUserParams(user) { return new URLSearchParams({ id:user.id, name:user.name, role:user.role, warehouse:user.warehouse }); }
function persistCurrentUser() { localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(currentUser)); }
function navigateToModule(relativePath) { persistCurrentUser(); location.href = `${relativePath}?${buildUserParams(currentUser)}`; }
function togglePassword() { const input = document.getElementById('loginPass'); const showing = input.type === 'text'; input.type = showing ? 'password' : 'text'; document.getElementById('passwordToggle').setAttribute('aria-label', showing ? '显示密码' : '隐藏密码'); }
function showToast(message,type='') { const toast=document.getElementById('toast'); toast.textContent=message; toast.className=`toast show ${type}`; clearTimeout(toastTimer); toastTimer=setTimeout(()=>{toast.className='toast';},2200); }
function showLoginError(message) { const error=document.getElementById('loginError'); error.textContent=message; error.classList.add('show'); } function hideLoginError() { document.getElementById('loginError').classList.remove('show'); }
function updateUserUI() { const initial=currentUser.name.slice(0,1); document.getElementById('workbenchAvatar').textContent=initial; document.getElementById('workbenchUserName').textContent=currentUser.name; document.getElementById('workbenchRole').textContent=currentUser.role; document.getElementById('workbenchWarehouse').textContent=currentUser.warehouse; document.getElementById('profileAvatar').textContent=initial; document.getElementById('profileName').textContent=currentUser.name; document.getElementById('profileRole').textContent=`${currentUser.id} · ${currentUser.role}`; document.getElementById('profileWarehouse').textContent=currentUser.warehouse; }
function showAppPage(pageId) { ['workbenchPage','profilePage'].forEach(id=>document.getElementById(id).classList.toggle('hidden',id!==pageId)); window.scrollTo(0,0); }
function enterMainPage(showWelcome=true) { document.getElementById('loginPage').classList.add('hidden'); showAppPage('workbenchPage'); updateUserUI(); hideLoginError(); if(showWelcome) showToast(`${currentUser.name},欢迎登录`,'success'); }
function handleLogin() { const userId=document.getElementById('loginUser').value.trim(); const password=document.getElementById('loginPass').value; if(!userId) return showLoginError('请输入用户名'); if(!password) return showLoginError('请输入密码'); const account=DEMO_ACCOUNTS[userId]; if(!account||account.pass!==password) return showLoginError('用户名或密码错误'); currentUser={id:userId,name:account.name,role:account.role,warehouse:account.warehouse}; persistCurrentUser(); enterMainPage(); }
function quickLogin(id,name,role) { currentUser={id,name,role,warehouse:DEMO_ACCOUNTS[id].warehouse}; persistCurrentUser(); enterMainPage(); } function openProfile() { showAppPage('profilePage'); } function backFromProfile() { showAppPage('workbenchPage'); }
function handleLogout() { currentUser=null; localStorage.removeItem(USER_STORAGE_KEY); document.getElementById('loginPage').classList.remove('hidden'); document.getElementById('workbenchPage').classList.add('hidden'); document.getElementById('profilePage').classList.add('hidden'); document.getElementById('loginUser').value=''; document.getElementById('loginPass').value=''; hideLoginError(); }
function readUserFromParams(params) { if(!params.get('id')) return null; return Object.fromEntries(['id','name','role','warehouse'].map(key=>[key,params.get(key)||''])); } function readStoredUser() { try { const stored=JSON.parse(localStorage.getItem(USER_STORAGE_KEY)); return stored?.id ? stored : null; } catch { return null; } }
function restoreWorkbenchSession() { const params=new URLSearchParams(location.search); if(params.get('resume')!=='1') return; currentUser=readUserFromParams(params)||readStoredUser(); if(!currentUser) return; persistCurrentUser(); enterMainPage(false); }
document.getElementById('loginPass').addEventListener('keydown',event=>{if(event.key==='Enter')handleLogin();}); document.getElementById('loginUser').addEventListener('keydown',event=>{if(event.key==='Enter')document.getElementById('loginPass').focus();}); restoreWorkbenchSession();
</script>
</body>
</html>