This commit is contained in:
2026-08-12 19:11:56 +08:00
parent a485844b47
commit 81656344cd
390 changed files with 52423 additions and 50395 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 881 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because it is too large Load Diff
-266
View File
@@ -1,266 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="loginSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1494412574643-ff11b0a5c1c3?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="login_welcome">欢迎回来</h2>
<p class="text-white/80" data-i18n="login_desc">登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat1">全程可视化追踪</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat2">智能订单管理</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat3">在线询价下单</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="login_title">账号登录</h2>
<p class="text-gray-600 mb-8"><span data-i18n="login_no_account">还没有账号?</span><button onclick="window.location.href='register.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="login_register_link">立即注册</button></p>
<form id="loginForm" class="space-y-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_account_label">邮箱 / 手机号</label>
<div class="relative">
<i class="fas fa-user absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="text" name="account" required placeholder="请输入邮箱或手机号" data-i18n-ph="login_account_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请输入密码" data-i18n-ph="login_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<label class="flex items-center text-gray-600 cursor-pointer">
<input type="checkbox" class="w-4 h-4 text-brand-blue rounded border-gray-300 mr-2">
<span data-i18n="login_remember">记住我</span>
</label>
<button type="button" onclick="showToast(t('toast_resetpw'))" class="text-brand-blue hover:underline" data-i18n="login_forgot">忘记密码?</button>
</div>
<button type="submit" class="btn-primary w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="login_submit">
登 录
</button>
</form>
<div class="mt-8">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-200"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500" data-i18n="login_other">其他方式登录</span>
</div>
</div>
<div class="flex justify-center space-x-4 mt-6">
<button onclick="showToast('微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-green-600 hover:bg-green-50 transition-colors">
<i class="fab fa-weixin text-xl"></i>
</button>
<button onclick="showToast('企业微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-blue-600 hover:bg-blue-50 transition-colors">
<i class="fas fa-briefcase text-xl"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
login_welcome: "欢迎回来",
login_desc: "登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。",
login_feat1: "全程可视化追踪",
login_feat2: "智能订单管理",
login_feat3: "在线询价下单",
login_title: "账号登录",
login_no_account: "还没有账号?",
login_register_link: "立即注册",
login_account_label: "邮箱 / 手机号",
login_account_ph: "请输入邮箱或手机号",
login_password_label: "密码",
login_password_ph: "请输入密码",
login_remember: "记住我",
login_forgot: "忘记密码?",
login_submit: "登 录",
login_other: "其他方式登录",
toast_resetpw: "请联系客服重置密码",
toast_login_ok: "登录成功!欢迎回来"
},
en: {
login_welcome: "Welcome Back",
login_desc: "Log in to track shipments in real time and manage orders and inventory.",
login_feat1: "Full Visual Tracking",
login_feat2: "Smart Order Management",
login_feat3: "Online Quoting & Ordering",
login_title: "Account Login",
login_no_account: "No account yet?",
login_register_link: "Sign Up Now",
login_account_label: "Email / Phone",
login_account_ph: "Enter email or phone",
login_password_label: "Password",
login_password_ph: "Enter password",
login_remember: "Remember Me",
login_forgot: "Forgot Password?",
login_submit: "Log In",
login_other: "Other Sign-In Methods",
toast_resetpw: "Please contact support to reset your password",
toast_login_ok: "Login successful! Welcome back"
},
es: {
login_welcome: "Bienvenido de nuevo",
login_desc: "Inicia sesión para rastrear envíos en tiempo real y gestionar pedidos e inventario.",
login_feat1: "Seguimiento visual completo",
login_feat2: "Gestión inteligente de pedidos",
login_feat3: "Cotización y pedidos en línea",
login_title: "Inicio de sesión",
login_no_account: "¿Aún no tienes cuenta?",
login_register_link: "Regístrate ahora",
login_account_label: "Correo / Teléfono",
login_account_ph: "Ingresa correo o teléfono",
login_password_label: "Contraseña",
login_password_ph: "Ingresa tu contraseña",
login_remember: "Recuérdame",
login_forgot: "¿Olvidó su contraseña?",
login_submit: "Iniciar sesión",
login_other: "Otros métodos de acceso",
toast_resetpw: "Contacta a soporte para restablecer tu contraseña",
toast_login_ok: "¡Inicio de sesión exitoso! Bienvenido"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_login_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 KiB

-286
View File
@@ -1,286 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="registerSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1566576912321-d58ddd7a6088?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流仓储" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="reg_welcome">加入我们</h2>
<p class="text-white/80" data-i18n="reg_welcome_desc">注册虫虫出海企业账号,开启高效跨境物流体验。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat1">新用户专属优惠</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat2">免费获取物流方案</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat3">专属客户经理对接</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="reg_title">企业注册</h2>
<p class="text-gray-600 mb-8"><span data-i18n="reg_have_account">已有账号?</span><button onclick="window.location.href='login.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="reg_login_link">立即登录</button></p>
<form id="registerForm" class="space-y-5">
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_company_label">企业名称</label>
<input type="text" name="company" required placeholder="请输入企业名称" data-i18n-ph="reg_company_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_contact_label">联系人</label>
<input type="text" name="contact" required placeholder="请输入联系人姓名" data-i18n-ph="reg_contact_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_email_label">邮箱</label>
<div class="relative">
<i class="fas fa-envelope absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="email" name="email" required placeholder="请输入企业邮箱" data-i18n-ph="reg_email_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_phone_label">手机号</label>
<div class="relative">
<i class="fas fa-phone absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="tel" name="phone" required placeholder="请输入手机号" data-i18n-ph="reg_phone_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请设置8-20位密码" data-i18n-ph="reg_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-start">
<input type="checkbox" id="agree" required class="w-4 h-4 text-brand-blue rounded border-gray-300 mt-1 mr-2">
<label for="agree" class="text-sm text-gray-600">
<span data-i18n="reg_agree">我已阅读并同意</span> <button type="button" onclick="showToast(t('toast_tos'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_tos">《用户服务协议》</button> 和 <button type="button" onclick="showToast(t('toast_pp'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_pp">《隐私政策》</button>
</label>
</div>
<button type="submit" class="btn-accent w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="reg_submit">
立即注册
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
reg_welcome: "加入我们",
reg_welcome_desc: "注册虫虫出海企业账号,开启高效跨境物流体验。",
reg_feat1: "新用户专属优惠",
reg_feat2: "免费获取物流方案",
reg_feat3: "专属客户经理对接",
reg_title: "企业注册",
reg_have_account: "已有账号?",
reg_login_link: "立即登录",
reg_company_label: "企业名称",
reg_company_ph: "请输入企业名称",
reg_contact_label: "联系人",
reg_contact_ph: "请输入联系人姓名",
reg_email_label: "邮箱",
reg_email_ph: "请输入企业邮箱",
reg_phone_label: "手机号",
reg_phone_ph: "请输入手机号",
reg_password_label: "密码",
reg_password_ph: "请设置8-20位密码",
reg_agree: "我已阅读并同意",
reg_agree_tos: "《用户服务协议》",
reg_agree_pp: "《隐私政策》",
reg_submit: "立即注册",
toast_tos: "服务协议即将上线",
toast_pp: "隐私政策即将上线",
toast_register_ok: "注册成功!欢迎加入虫虫出海"
},
en: {
reg_welcome: "Join Us",
reg_welcome_desc: "Register a Chongchong Global business account and start efficient cross-border logistics.",
reg_feat1: "Exclusive New-User Offers",
reg_feat2: "Free Logistics Plan",
reg_feat3: "Dedicated Account Manager",
reg_title: "Business Registration",
reg_have_account: "Already have an account?",
reg_login_link: "Log In Now",
reg_company_label: "Company Name",
reg_company_ph: "Enter company name",
reg_contact_label: "Contact Person",
reg_contact_ph: "Enter contact name",
reg_email_label: "Email",
reg_email_ph: "Enter company email",
reg_phone_label: "Phone Number",
reg_phone_ph: "Enter phone number",
reg_password_label: "Password",
reg_password_ph: "Set an 8-20 char password",
reg_agree: "I have read and agree to the",
reg_agree_tos: "User Agreement",
reg_agree_pp: "Privacy Policy",
reg_submit: "Sign Up Now",
toast_tos: "Terms of Service coming soon",
toast_pp: "Privacy Policy coming soon",
toast_register_ok: "Registration successful! Welcome to Chongchong Global"
},
es: {
reg_welcome: "Únete a nosotros",
reg_welcome_desc: "Regístrate en la cuenta empresarial de Chongchong Global y comienza una logística eficiente.",
reg_feat1: "Ofertas exclusivas para nuevos usuarios",
reg_feat2: "Plan logístico gratuito",
reg_feat3: "Gerente de cuenta dedicado",
reg_title: "Registro empresarial",
reg_have_account: "¿Ya tienes una cuenta?",
reg_login_link: "Inicia sesión ahora",
reg_company_label: "Nombre de empresa",
reg_company_ph: "Ingresa el nombre de empresa",
reg_contact_label: "Persona de contacto",
reg_contact_ph: "Ingresa el nombre de contacto",
reg_email_label: "Correo electrónico",
reg_email_ph: "Ingresa el correo empresarial",
reg_phone_label: "Número de teléfono",
reg_phone_ph: "Ingresa tu número de teléfono",
reg_password_label: "Contraseña",
reg_password_ph: "Define una contraseña de 8-20 caracteres",
reg_agree: "He leído y acepto los",
reg_agree_tos: "Términos de servicio",
reg_agree_pp: "Política de privacidad",
reg_submit: "Regístrate ahora",
toast_tos: "Términos de servicio próximamente",
toast_pp: "Política de privacidad próximamente",
toast_register_ok: "¡Registro exitoso! Bienvenido a Chongchong Global"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('registerForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_register_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because it is too large Load Diff
-266
View File
@@ -1,266 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="loginSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1494412574643-ff11b0a5c1c3?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="login_welcome">欢迎回来</h2>
<p class="text-white/80" data-i18n="login_desc">登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat1">全程可视化追踪</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat2">智能订单管理</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat3">在线询价下单</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="login_title">账号登录</h2>
<p class="text-gray-600 mb-8"><span data-i18n="login_no_account">还没有账号?</span><button onclick="window.location.href='register.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="login_register_link">立即注册</button></p>
<form id="loginForm" class="space-y-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_account_label">邮箱 / 手机号</label>
<div class="relative">
<i class="fas fa-user absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="text" name="account" required placeholder="请输入邮箱或手机号" data-i18n-ph="login_account_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请输入密码" data-i18n-ph="login_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<label class="flex items-center text-gray-600 cursor-pointer">
<input type="checkbox" class="w-4 h-4 text-brand-blue rounded border-gray-300 mr-2">
<span data-i18n="login_remember">记住我</span>
</label>
<button type="button" onclick="showToast(t('toast_resetpw'))" class="text-brand-blue hover:underline" data-i18n="login_forgot">忘记密码?</button>
</div>
<button type="submit" class="btn-primary w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="login_submit">
登 录
</button>
</form>
<div class="mt-8">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-200"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500" data-i18n="login_other">其他方式登录</span>
</div>
</div>
<div class="flex justify-center space-x-4 mt-6">
<button onclick="showToast('微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-green-600 hover:bg-green-50 transition-colors">
<i class="fab fa-weixin text-xl"></i>
</button>
<button onclick="showToast('企业微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-blue-600 hover:bg-blue-50 transition-colors">
<i class="fas fa-briefcase text-xl"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
login_welcome: "欢迎回来",
login_desc: "登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。",
login_feat1: "全程可视化追踪",
login_feat2: "智能订单管理",
login_feat3: "在线询价下单",
login_title: "账号登录",
login_no_account: "还没有账号?",
login_register_link: "立即注册",
login_account_label: "邮箱 / 手机号",
login_account_ph: "请输入邮箱或手机号",
login_password_label: "密码",
login_password_ph: "请输入密码",
login_remember: "记住我",
login_forgot: "忘记密码?",
login_submit: "登 录",
login_other: "其他方式登录",
toast_resetpw: "请联系客服重置密码",
toast_login_ok: "登录成功!欢迎回来"
},
en: {
login_welcome: "Welcome Back",
login_desc: "Log in to track shipments in real time and manage orders and inventory.",
login_feat1: "Full Visual Tracking",
login_feat2: "Smart Order Management",
login_feat3: "Online Quoting & Ordering",
login_title: "Account Login",
login_no_account: "No account yet?",
login_register_link: "Sign Up Now",
login_account_label: "Email / Phone",
login_account_ph: "Enter email or phone",
login_password_label: "Password",
login_password_ph: "Enter password",
login_remember: "Remember Me",
login_forgot: "Forgot Password?",
login_submit: "Log In",
login_other: "Other Sign-In Methods",
toast_resetpw: "Please contact support to reset your password",
toast_login_ok: "Login successful! Welcome back"
},
es: {
login_welcome: "Bienvenido de nuevo",
login_desc: "Inicia sesión para rastrear envíos en tiempo real y gestionar pedidos e inventario.",
login_feat1: "Seguimiento visual completo",
login_feat2: "Gestión inteligente de pedidos",
login_feat3: "Cotización y pedidos en línea",
login_title: "Inicio de sesión",
login_no_account: "¿Aún no tienes cuenta?",
login_register_link: "Regístrate ahora",
login_account_label: "Correo / Teléfono",
login_account_ph: "Ingresa correo o teléfono",
login_password_label: "Contraseña",
login_password_ph: "Ingresa tu contraseña",
login_remember: "Recuérdame",
login_forgot: "¿Olvidó su contraseña?",
login_submit: "Iniciar sesión",
login_other: "Otros métodos de acceso",
toast_resetpw: "Contacta a soporte para restablecer tu contraseña",
toast_login_ok: "¡Inicio de sesión exitoso! Bienvenido"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_login_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
-286
View File
@@ -1,286 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="registerSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1566576912321-d58ddd7a6088?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流仓储" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="reg_welcome">加入我们</h2>
<p class="text-white/80" data-i18n="reg_welcome_desc">注册虫虫出海企业账号,开启高效跨境物流体验。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat1">新用户专属优惠</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat2">免费获取物流方案</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat3">专属客户经理对接</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="reg_title">企业注册</h2>
<p class="text-gray-600 mb-8"><span data-i18n="reg_have_account">已有账号?</span><button onclick="window.location.href='login.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="reg_login_link">立即登录</button></p>
<form id="registerForm" class="space-y-5">
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_company_label">企业名称</label>
<input type="text" name="company" required placeholder="请输入企业名称" data-i18n-ph="reg_company_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_contact_label">联系人</label>
<input type="text" name="contact" required placeholder="请输入联系人姓名" data-i18n-ph="reg_contact_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_email_label">邮箱</label>
<div class="relative">
<i class="fas fa-envelope absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="email" name="email" required placeholder="请输入企业邮箱" data-i18n-ph="reg_email_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_phone_label">手机号</label>
<div class="relative">
<i class="fas fa-phone absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="tel" name="phone" required placeholder="请输入手机号" data-i18n-ph="reg_phone_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请设置8-20位密码" data-i18n-ph="reg_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-start">
<input type="checkbox" id="agree" required class="w-4 h-4 text-brand-blue rounded border-gray-300 mt-1 mr-2">
<label for="agree" class="text-sm text-gray-600">
<span data-i18n="reg_agree">我已阅读并同意</span> <button type="button" onclick="showToast(t('toast_tos'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_tos">《用户服务协议》</button> 和 <button type="button" onclick="showToast(t('toast_pp'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_pp">《隐私政策》</button>
</label>
</div>
<button type="submit" class="btn-accent w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="reg_submit">
立即注册
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
reg_welcome: "加入我们",
reg_welcome_desc: "注册虫虫出海企业账号,开启高效跨境物流体验。",
reg_feat1: "新用户专属优惠",
reg_feat2: "免费获取物流方案",
reg_feat3: "专属客户经理对接",
reg_title: "企业注册",
reg_have_account: "已有账号?",
reg_login_link: "立即登录",
reg_company_label: "企业名称",
reg_company_ph: "请输入企业名称",
reg_contact_label: "联系人",
reg_contact_ph: "请输入联系人姓名",
reg_email_label: "邮箱",
reg_email_ph: "请输入企业邮箱",
reg_phone_label: "手机号",
reg_phone_ph: "请输入手机号",
reg_password_label: "密码",
reg_password_ph: "请设置8-20位密码",
reg_agree: "我已阅读并同意",
reg_agree_tos: "《用户服务协议》",
reg_agree_pp: "《隐私政策》",
reg_submit: "立即注册",
toast_tos: "服务协议即将上线",
toast_pp: "隐私政策即将上线",
toast_register_ok: "注册成功!欢迎加入虫虫出海"
},
en: {
reg_welcome: "Join Us",
reg_welcome_desc: "Register a Chongchong Global business account and start efficient cross-border logistics.",
reg_feat1: "Exclusive New-User Offers",
reg_feat2: "Free Logistics Plan",
reg_feat3: "Dedicated Account Manager",
reg_title: "Business Registration",
reg_have_account: "Already have an account?",
reg_login_link: "Log In Now",
reg_company_label: "Company Name",
reg_company_ph: "Enter company name",
reg_contact_label: "Contact Person",
reg_contact_ph: "Enter contact name",
reg_email_label: "Email",
reg_email_ph: "Enter company email",
reg_phone_label: "Phone Number",
reg_phone_ph: "Enter phone number",
reg_password_label: "Password",
reg_password_ph: "Set an 8-20 char password",
reg_agree: "I have read and agree to the",
reg_agree_tos: "User Agreement",
reg_agree_pp: "Privacy Policy",
reg_submit: "Sign Up Now",
toast_tos: "Terms of Service coming soon",
toast_pp: "Privacy Policy coming soon",
toast_register_ok: "Registration successful! Welcome to Chongchong Global"
},
es: {
reg_welcome: "Únete a nosotros",
reg_welcome_desc: "Regístrate en la cuenta empresarial de Chongchong Global y comienza una logística eficiente.",
reg_feat1: "Ofertas exclusivas para nuevos usuarios",
reg_feat2: "Plan logístico gratuito",
reg_feat3: "Gerente de cuenta dedicado",
reg_title: "Registro empresarial",
reg_have_account: "¿Ya tienes una cuenta?",
reg_login_link: "Inicia sesión ahora",
reg_company_label: "Nombre de empresa",
reg_company_ph: "Ingresa el nombre de empresa",
reg_contact_label: "Persona de contacto",
reg_contact_ph: "Ingresa el nombre de contacto",
reg_email_label: "Correo electrónico",
reg_email_ph: "Ingresa el correo empresarial",
reg_phone_label: "Número de teléfono",
reg_phone_ph: "Ingresa tu número de teléfono",
reg_password_label: "Contraseña",
reg_password_ph: "Define una contraseña de 8-20 caracteres",
reg_agree: "He leído y acepto los",
reg_agree_tos: "Términos de servicio",
reg_agree_pp: "Política de privacidad",
reg_submit: "Regístrate ahora",
toast_tos: "Términos de servicio próximamente",
toast_pp: "Política de privacidad próximamente",
toast_register_ok: "¡Registro exitoso! Bienvenido a Chongchong Global"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('registerForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_register_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
-880
View File
@@ -1,880 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>虫虫出海 CHONGCHONG GLOBAL | 工作台</title>
<meta name="description" content="虫虫出海工作台 - 海外采购、美国本土卡车、美本土散板一键下单。">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
brand: {
green: '#4CAF50',
greenDark: '#2E7D32',
blue: '#1565C0',
blueDark: '#0D47A1',
teal: '#00838F',
light: '#F0F7FF',
dark: '#0A1628'
}
},
fontFamily: {
sans: ['Inter', 'Noto Sans SC', 'system-ui', 'sans-serif'],
},
boxShadow: {
'card': '0 10px 40px rgba(10, 22, 40, 0.08)',
'card-hover': '0 20px 60px rgba(10, 22, 40, 0.15)',
}
}
}
}
</script>
<style>
* { scroll-behavior: smooth; }
body { font-family: 'Inter', 'Noto Sans SC', system-ui, sans-serif; overflow-x: hidden; }
.btn-primary {
background: linear-gradient(135deg, #1565C0 0%, #0D47A1 100%);
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(21, 101, 192, 0.35);
}
.btn-accent {
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
transition: all 0.3s ease;
}
.btn-accent:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(76, 175, 80, 0.35);
}
/* 语言切换器 */
.lang-switcher {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.15);
border-radius: 9999px;
padding: 3px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.lang-switcher button {
font-size: 12px;
font-weight: 600;
padding: 4px 9px;
border-radius: 9999px;
color: rgba(255, 255, 255, 0.9);
transition: all 0.25s ease;
white-space: nowrap;
}
.lang-switcher button:hover { color: #fff; }
.lang-switcher button.active { background: #fff; color: #0D47A1; }
/* ===== 下单工作台 Dashboard ===== */
.dash-view { animation: fadeIn 0.4s ease; }
.dash-card {
background: #fff;
border: 1px solid #eef2f7;
border-radius: 1.25rem;
box-shadow: 0 10px 40px rgba(10, 22, 40, 0.06);
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.dash-card:hover {
transform: translateY(-8px);
box-shadow: 0 24px 60px rgba(10, 22, 40, 0.14);
border-color: rgba(21, 101, 192, 0.35);
}
.dash-form-input {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
outline: none;
font-size: 0.95rem;
color: #1f2937;
background: #fff;
transition: all 0.2s ease;
}
.dash-form-input:focus {
border-color: #1565C0;
box-shadow: 0 0 0 3px rgba(21, 101, 192, 0.12);
}
.dash-form-input::placeholder { color: #9ca3af; }
.dash-label {
display: block;
font-size: 0.875rem;
font-weight: 600;
color: #374151;
margin-bottom: 0.5rem;
}
.dash-label .req { color: #ef4444; margin-left: 2px; }
.seg-input { position: absolute; opacity: 0; width: 0; height: 0; }
.seg-pill {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 0.65rem 1.1rem;
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
cursor: pointer;
font-size: 0.9rem;
color: #475569;
background: #fff;
transition: all 0.2s ease;
user-select: none;
}
.seg-pill:hover { border-color: #cbd5e1; }
.seg-input:checked + .seg-pill {
border-color: #1565C0;
background: #EAF2FD;
color: #0D47A1;
font-weight: 600;
box-shadow: 0 0 0 1px #1565C0 inset;
}
.dash-topbar {
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(12px);
border-bottom: 1px solid #eef2f7;
}
.dash-sidebar {
width: 250px;
flex-shrink: 0;
background: linear-gradient(180deg, #0B1F3A 0%, #0C2A52 100%);
color: #fff;
display: flex;
flex-direction: column;
position: sticky;
top: 0;
height: 100vh;
padding: 1.25rem 0.875rem;
}
.dash-sidebar-brand {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.4rem 0.55rem 1.1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
margin-bottom: 0.6rem;
}
.dash-sidebar-brand .brand-name { font-weight: 700; font-size: 1rem; line-height: 1.15; }
.dash-sidebar-brand .brand-sub { font-size: 0.62rem; letter-spacing: 0.12em; color: rgba(255, 255, 255, 0.45); }
.dash-nav { flex: 1; display: flex; flex-direction: column; gap: 0.25rem; overflow-y: auto; }
.dash-nav-group {
font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.1em;
color: rgba(255, 255, 255, 0.4); padding: 0.85rem 0.75rem 0.35rem;
}
.dash-nav-link {
display: flex; align-items: center; gap: 0.75rem;
padding: 0.72rem 0.85rem; border-radius: 0.75rem;
color: rgba(255, 255, 255, 0.72); font-size: 0.92rem; font-weight: 500;
cursor: pointer; transition: all 0.2s ease; white-space: nowrap;
}
.dash-nav-link i { width: 1.2rem; text-align: center; font-size: 0.95rem; }
.dash-nav-link:hover { background: rgba(255, 255, 255, 0.07); color: #fff; }
.dash-nav-link.active {
background: linear-gradient(90deg, #1565C0, #1E88E5); color: #fff;
box-shadow: 0 6px 18px rgba(21, 101, 192, 0.4);
}
.dash-sidebar-bottom {
border-top: 1px solid rgba(255, 255, 255, 0.08); padding-top: 0.9rem;
display: flex; flex-direction: column; gap: 0.7rem; align-items: stretch;
}
.dash-sidebar-bottom #langSwitcherDash { align-self: center; }
.dash-logout-btn {
display: flex; align-items: center; justify-content: center; gap: 0.5rem;
padding: 0.6rem; border-radius: 0.7rem; font-size: 0.88rem; font-weight: 600;
background: rgba(239, 68, 68, 0.12); color: #fca5a5; cursor: pointer;
transition: all 0.2s ease;
}
.dash-logout-btn:hover { background: rgba(239, 68, 68, 0.22); color: #fff; }
.dash-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.dash-menu-btn {
display: none; width: 2.25rem; height: 2.25rem; border-radius: 0.6rem;
align-items: center; justify-content: center; color: #374151;
background: #f1f5f9; cursor: pointer;
}
.dash-overlay { display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45); z-index: 45; }
.dash-overlay.show { display: block; }
.dash-right {
width: 320px; flex-shrink: 0;
background: #fff; border-left: 1px solid #eef2f7;
padding: 1.5rem 1.25rem;
position: sticky; top: 4rem; align-self: flex-start;
max-height: calc(100vh - 4rem); overflow-y: auto;
}
.dash-right-card {
background: #f8fafc; border: 1px solid #eef2f7;
border-radius: 1rem; padding: 1.25rem; margin-bottom: 1rem;
}
.dash-right-card h4 {
font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em;
text-transform: uppercase; color: #94a3b8; margin-bottom: 0.9rem;
}
.dash-account { display: flex; align-items: center; gap: 0.75rem; }
.dash-account-avatar {
width: 2.75rem; height: 2.75rem; border-radius: 0.8rem;
background: linear-gradient(135deg, #1565C0, #00BFA5); color: #fff;
display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 1.1rem; flex-shrink: 0;
}
.dash-account-name { font-weight: 700; color: #0f172a; font-size: 0.95rem; line-height: 1.2; }
.dash-account-type { font-size: 0.75rem; color: #64748b; margin-top: 0.15rem; }
.dash-stat-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px dashed #e2e8f0; }
.dash-stat-row:last-child { border-bottom: none; }
.dash-stat-label { font-size: 0.85rem; color: #475569; }
.dash-stat-value { font-weight: 700; color: #0f172a; font-size: 0.95rem; }
.dash-help-item {
display: flex; align-items: center; gap: 0.6rem;
padding: 0.6rem 0.55rem; border-radius: 0.6rem; cursor: pointer;
color: #334155; font-size: 0.9rem; transition: background 0.2s, color 0.2s;
}
.dash-help-item:hover { background: #eef2f7; color: #1565C0; }
.dash-help-item i { color: #00BFA5; width: 1.15rem; text-align: center; }
.dash-tip {
background: linear-gradient(135deg, rgba(0,191,165,0.08), rgba(21,101,192,0.08));
border: 1px solid rgba(0,191,165,0.22); border-radius: 1rem;
padding: 1.1rem; font-size: 0.82rem; color: #475569; line-height: 1.65;
}
.dash-tip i { color: #00BFA5; margin-right: 0.4rem; }
.dash-empty { padding: 1rem; color: #94a3b8; }
.dash-empty i { font-size: 3rem; color: #cbd5e1; margin-bottom: 1rem; display: block; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.dash-sidebar {
position: fixed; left: 0; top: 0; z-index: 50;
transform: translateX(-100%); transition: transform 0.3s ease;
}
.dash-sidebar.open { transform: translateX(0); }
.dash-menu-btn { display: inline-flex; }
}
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<!-- 工作台 -->
<section id="workbench" class="min-h-screen flex">
<aside id="dashSidebar" class="dash-sidebar">
<div class="dash-sidebar-brand">
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
</div>
<nav class="dash-nav">
<div class="dash-nav-group" data-i18n="dash_menu_main">主导航</div>
<a class="dash-nav-link active" data-view="Home" onclick="goDash('Home')"><i class="fas fa-th-large"></i><span data-i18n="dash_menu_overview">业务总览</span></a>
<a class="dash-nav-link" data-view="Procurement" onclick="goDash('Procurement')"><i class="fas fa-shopping-cart"></i><span data-i18n="dash_menu_proc">海外采购</span></a>
<a class="dash-nav-link" data-view="Truck" onclick="goDash('Truck')"><i class="fas fa-truck-moving"></i><span data-i18n="dash_menu_truck">美国本土卡</span></a>
<a class="dash-nav-link" data-view="Pallet" onclick="goDash('Pallet')"><i class="fas fa-pallet"></i><span data-i18n="dash_menu_pallet">美本土散板</span></a>
<a class="dash-nav-link" data-view="Orders" onclick="goDash('Orders')"><i class="fas fa-clipboard-list"></i><span data-i18n="dash_menu_orders">我的订单</span></a>
</nav>
<div class="dash-sidebar-bottom">
<div id="langSwitcherDash" class="lang-switcher flex">
<button data-lang="zh">中</button>
<button data-lang="en">EN</button>
<button data-lang="es">🇲🇽</button>
</div>
<button class="dash-logout-btn" onclick="doLogout()"><i class="fas fa-sign-out-alt"></i><span data-i18n="dash_logout">退出登录</span></button>
</div>
</aside>
<!-- 移动端遮罩 -->
<div id="dashOverlay" class="dash-overlay" onclick="toggleDashSidebar()"></div>
<!-- 主内容区 -->
<div class="dash-main">
<header class="dash-topbar sticky top-0 z-30">
<div class="px-4 sm:px-6 lg:px-10 h-16 flex items-center justify-between">
<div class="flex items-center space-x-3">
<button class="dash-menu-btn" onclick="toggleDashSidebar()"><i class="fas fa-bars"></i></button>
<span class="text-sm text-gray-500" id="dashUserName">Hi, 客户</span>
</div>
<a href="index.html" class="text-sm text-gray-600 hover:text-brand-blue font-medium px-3 py-2 transition-colors" data-i18n="dash_home_link">返回官网</a>
</div>
</header>
<div class="flex flex-1 min-h-0">
<main class="flex-1 px-4 sm:px-6 lg:px-10 py-8 min-w-0">
<!-- 视图1:选择业务 -->
<div id="dashViewHome" class="dash-view">
<div class="mb-10">
<h1 class="text-3xl lg:text-4xl font-bold text-gray-900 mb-3" data-i18n="dash_welcome_title">选择业务,快速下单</h1>
<p class="text-gray-500" data-i18n="dash_welcome_sub">请选择您需要的业务类型,填写信息后提交订单,我们将尽快为您安排。</p>
</div>
<div class="grid md:grid-cols-3 gap-6">
<div class="dash-card p-8" onclick="openOrder('Procurement')">
<div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-brand-blue to-brand-blueDark flex items-center justify-center text-white text-2xl mb-6">
<i class="fas fa-shopping-cart"></i>
</div>
<h3 class="text-xl font-bold text-gray-900 mb-2" data-i18n="dash_biz1_title">海外采购</h3>
<p class="text-sm text-gray-500 mb-6" data-i18n="dash_biz1_desc">采购代购 · 验货质检 · 集中发货</p>
<span class="inline-flex items-center text-brand-blue font-semibold text-sm">
<span data-i18n="dash_start">开始下单</span><i class="fas fa-arrow-right ml-2"></i>
</span>
</div>
<div class="dash-card p-8" onclick="openOrder('Truck')">
<div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-brand-green to-brand-greenDark flex items-center justify-center text-white text-2xl mb-6">
<i class="fas fa-truck-moving"></i>
</div>
<h3 class="text-xl font-bold text-gray-900 mb-2" data-i18n="dash_biz2_title">美国本土卡</h3>
<p class="text-sm text-gray-500 mb-6" data-i18n="dash_biz2_desc">港口提柜 · 仓到仓 · 仓到门</p>
<span class="inline-flex items-center text-brand-green font-semibold text-sm">
<span data-i18n="dash_start">开始下单</span><i class="fas fa-arrow-right ml-2"></i>
</span>
</div>
<div class="dash-card p-8" onclick="openOrder('Pallet')">
<div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-brand-teal to-cyan-700 flex items-center justify-center text-white text-2xl mb-6">
<i class="fas fa-pallet"></i>
</div>
<h3 class="text-xl font-bold text-gray-900 mb-2" data-i18n="dash_biz3_title">美本土散板</h3>
<p class="text-sm text-gray-500 mb-6" data-i18n="dash_biz3_desc">拼车拼载 · 打盘服务 · 灵活派送</p>
<span class="inline-flex items-center text-brand-teal font-semibold text-sm">
<span data-i18n="dash_start">开始下单</span><i class="fas fa-arrow-right ml-2"></i>
</span>
</div>
</div>
</div>
<!-- 视图2:海外采购下单 -->
<div id="dashViewProcurement" class="dash-view hidden">
<button onclick="showDashView('Home')" class="inline-flex items-center text-gray-500 hover:text-brand-blue mb-6 text-sm font-medium">
<i class="fas fa-arrow-left mr-2"></i><span data-i18n="dash_back">返回业务选择</span>
</button>
<div class="bg-white rounded-2xl shadow-card border border-gray-100 p-8 lg:p-10 max-w-3xl">
<div class="flex items-center space-x-3 mb-8">
<div class="w-11 h-11 rounded-xl bg-gradient-to-br from-brand-blue to-brand-blueDark flex items-center justify-center text-white text-lg"><i class="fas fa-shopping-cart"></i></div>
<h2 class="text-2xl font-bold text-gray-900" data-i18n="po_title">海外采购下单</h2>
</div>
<form class="order-form space-y-5" data-order="海外采购 Overseas Sourcing">
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="po_category">采购品类</span><span class="req">*</span></label>
<input type="text" name="采购品类" required class="dash-form-input" placeholder="如:家居用品 / 3C 配件" data-i18n-ph="po_category_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="po_spec">商品规格</span></label>
<input type="text" name="商品规格" class="dash-form-input" placeholder="如:尺寸 / 颜色 / 材质" data-i18n-ph="po_spec_ph">
</div>
</div>
<div>
<label class="dash-label"><span data-i18n="po_link">商品链接</span></label>
<input type="url" name="商品链接" class="dash-form-input" placeholder="https://" data-i18n-ph="po_link_ph">
</div>
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="po_qty">采购数量</span><span class="req">*</span></label>
<input type="number" min="1" name="采购数量" required class="dash-form-input" placeholder="0" data-i18n-ph="po_qty_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="po_price">预算单价</span></label>
<div class="relative">
<span class="absolute left-4 top-1/2 -translate-y-1/2 text-gray-400 font-medium">$</span>
<input type="number" min="0" step="0.01" name="预算单价($)" class="dash-form-input pl-8" placeholder="0.00" data-i18n-ph="po_price_ph">
</div>
</div>
</div>
<div>
<label class="dash-label"><span data-i18n="po_address">收货地址</span></label>
<input type="text" name="收货地址" class="dash-form-input" placeholder="请输入收货地址" data-i18n-ph="po_address_ph">
</div>
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="po_contact">联系人</span><span class="req">*</span></label>
<input type="text" name="联系人" required class="dash-form-input" placeholder="请输入联系人姓名" data-i18n-ph="po_contact_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="po_phone">联系电话</span><span class="req">*</span></label>
<input type="tel" name="联系电话" required class="dash-form-input" placeholder="请输入联系电话" data-i18n-ph="po_phone_ph">
</div>
</div>
<div>
<label class="dash-label"><span data-i18n="po_note">订单备注</span></label>
<textarea name="订单备注" rows="3" class="dash-form-input" placeholder="其他需求说明(选填)" data-i18n-ph="po_note_ph"></textarea>
</div>
<div class="flex gap-3 pt-2">
<button type="submit" class="btn-primary text-white font-bold px-8 py-3.5 rounded-xl" data-i18n="dash_submit">提交订单</button>
<button type="button" onclick="showDashView('Home')" class="px-8 py-3.5 rounded-xl border border-gray-200 text-gray-600 font-medium hover:bg-gray-50" data-i18n="dash_cancel">取消</button>
</div>
</form>
</div>
</div>
<!-- 视图3:美国本土卡下单 -->
<div id="dashViewTruck" class="dash-view hidden">
<button onclick="showDashView('Home')" class="inline-flex items-center text-gray-500 hover:text-brand-blue mb-6 text-sm font-medium">
<i class="fas fa-arrow-left mr-2"></i><span data-i18n="dash_back">返回业务选择</span>
</button>
<div class="bg-white rounded-2xl shadow-card border border-gray-100 p-8 lg:p-10 max-w-3xl">
<div class="flex items-center space-x-3 mb-8">
<div class="w-11 h-11 rounded-xl bg-gradient-to-br from-brand-green to-brand-greenDark flex items-center justify-center text-white text-lg"><i class="fas fa-truck-moving"></i></div>
<h2 class="text-2xl font-bold text-gray-900" data-i18n="tk_title">美国本土卡下单</h2>
</div>
<form class="order-form space-y-6" data-order="美国本土卡 US Trucking">
<div>
<label class="dash-label"><span data-i18n="ts_type">运输类型</span><span class="req">*</span></label>
<div class="flex flex-wrap gap-3">
<label><input type="radio" name="运输类型" value="港口提柜" class="seg-input" checked><span class="seg-pill"><i class="fas fa-anchor text-xs"></i><span data-i18n="ts_type_port">港口提柜</span></span></label>
<label><input type="radio" name="运输类型" value="仓到仓" class="seg-input"><span class="seg-pill"><i class="fas fa-warehouse text-xs"></i><span data-i18n="ts_type_w2w">仓到仓</span></span></label>
<label><input type="radio" name="运输类型" value="仓到门" class="seg-input"><span class="seg-pill"><i class="fas fa-door-open text-xs"></i><span data-i18n="ts_type_w2d">仓到门</span></span></label>
</div>
</div>
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="ts_origin">起运地</span><span class="req">*</span></label>
<input type="text" name="起运地" required class="dash-form-input" placeholder="城市 / 港口 / 州" data-i18n-ph="ts_origin_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="ts_dest">目的地</span><span class="req">*</span></label>
<input type="text" name="目的地" required class="dash-form-input" placeholder="城市 / 州" data-i18n-ph="ts_dest_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="ts_origin_addr">详细地址</span></label>
<input type="text" name="起运详细地址" class="dash-form-input" placeholder="街道 / 门牌" data-i18n-ph="ts_origin_addr_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="ts_dest_addr">详细地址 / 码头 / 仓号</span></label>
<input type="text" name="目的详细地址/码头/仓号" class="dash-form-input" placeholder="地址 / 码头 / 仓号" data-i18n-ph="ts_dest_addr_ph">
</div>
</div>
<div>
<label class="dash-label mb-3"><i class="fas fa-box mr-1 text-gray-400"></i><span data-i18n="cargo_title">货物信息</span></label>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_container">柜型</label>
<select name="柜型" class="dash-form-input">
<option value="20GP">20GP</option>
<option value="40GP">40GP</option>
<option value="40HQ">40HQ</option>
<option value="45HQ">45HQ</option>
<option value="LTL" data-i18n="cargo_container_ltl">散货/托盘</option>
</select>
</div>
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_pieces">件数</label>
<input type="number" min="0" name="件数" class="dash-form-input" placeholder="0">
</div>
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_weight">总重量 (kg)</label>
<input type="number" min="0" step="0.01" name="总重量(kg)" class="dash-form-input" placeholder="0">
</div>
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_volume">总体积 (m³)</label>
<input type="number" min="0" step="0.01" name="总体积(m³)" class="dash-form-input" placeholder="0">
</div>
</div>
</div>
<div>
<label class="dash-label mb-3"><i class="fas fa-user mr-1 text-gray-400"></i><span data-i18n="contact_title">联系人信息</span></label>
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="contact_name">联系人</span><span class="req">*</span></label>
<input type="text" name="联系人" required class="dash-form-input" placeholder="请输入联系人姓名" data-i18n-ph="contact_name_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="contact_phone">联系方式</span><span class="req">*</span></label>
<input type="text" name="联系方式" required class="dash-form-input" placeholder="电话 / 微信 / 邮箱" data-i18n-ph="contact_phone_ph">
</div>
</div>
</div>
<div class="max-w-xs">
<label class="dash-label"><span data-i18n="req_time">要求时间</span></label>
<input type="date" name="要求时间" class="dash-form-input">
</div>
<div class="flex gap-3 pt-2">
<button type="submit" class="btn-primary text-white font-bold px-8 py-3.5 rounded-xl" data-i18n="dash_submit">提交订单</button>
<button type="button" onclick="showDashView('Home')" class="px-8 py-3.5 rounded-xl border border-gray-200 text-gray-600 font-medium hover:bg-gray-50" data-i18n="dash_cancel">取消</button>
</div>
</form>
</div>
</div>
<!-- 视图4:美本土散板下单 -->
<div id="dashViewPallet" class="dash-view hidden">
<button onclick="showDashView('Home')" class="inline-flex items-center text-gray-500 hover:text-brand-blue mb-6 text-sm font-medium">
<i class="fas fa-arrow-left mr-2"></i><span data-i18n="dash_back">返回业务选择</span>
</button>
<div class="bg-white rounded-2xl shadow-card border border-gray-100 p-8 lg:p-10 max-w-3xl">
<div class="flex items-center space-x-3 mb-8">
<div class="w-11 h-11 rounded-xl bg-gradient-to-br from-brand-teal to-cyan-700 flex items-center justify-center text-white text-lg"><i class="fas fa-pallet"></i></div>
<h2 class="text-2xl font-bold text-gray-900" data-i18n="pl_title">美本土散板下单</h2>
</div>
<form class="order-form space-y-6" data-order="美本土散板 US Pallet">
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="pl_method">运输方式</span><span class="req">*</span></label>
<div class="flex flex-wrap gap-3">
<label><input type="radio" name="运输方式" value="拼车" class="seg-input" checked><span class="seg-pill"><span data-i18n="pl_method_pool">拼车</span></span></label>
<label><input type="radio" name="运输方式" value="优先拼载" class="seg-input"><span class="seg-pill"><span data-i18n="pl_method_priority">优先拼载</span></span></label>
</div>
</div>
<div>
<label class="dash-label"><span data-i18n="pl_palletize">是否打盘</span><span class="req">*</span></label>
<div class="flex flex-wrap gap-3">
<label><input type="radio" name="是否打盘" value="是" class="seg-input" checked><span class="seg-pill"><span data-i18n="pl_yes">是</span></span></label>
<label><input type="radio" name="是否打盘" value="否" class="seg-input"><span class="seg-pill"><span data-i18n="pl_no">否</span></span></label>
</div>
</div>
</div>
<div>
<label class="dash-label"><span data-i18n="ts_type">运输类型</span><span class="req">*</span></label>
<div class="flex flex-wrap gap-3">
<label><input type="radio" name="运输类型" value="港口提柜" class="seg-input" checked><span class="seg-pill"><i class="fas fa-anchor text-xs"></i><span data-i18n="ts_type_port">港口提柜</span></span></label>
<label><input type="radio" name="运输类型" value="仓到仓" class="seg-input"><span class="seg-pill"><i class="fas fa-warehouse text-xs"></i><span data-i18n="ts_type_w2w">仓到仓</span></span></label>
<label><input type="radio" name="运输类型" value="仓到门" class="seg-input"><span class="seg-pill"><i class="fas fa-door-open text-xs"></i><span data-i18n="ts_type_w2d">仓到门</span></span></label>
</div>
</div>
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="ts_origin">起运地</span><span class="req">*</span></label>
<input type="text" name="起运地" required class="dash-form-input" placeholder="城市 / 港口 / 州" data-i18n-ph="ts_origin_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="ts_dest">目的地</span><span class="req">*</span></label>
<input type="text" name="目的地" required class="dash-form-input" placeholder="城市 / 州" data-i18n-ph="ts_dest_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="ts_origin_addr">详细地址</span></label>
<input type="text" name="起运详细地址" class="dash-form-input" placeholder="街道 / 门牌" data-i18n-ph="ts_origin_addr_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="ts_dest_addr">详细地址 / 码头 / 仓号</span></label>
<input type="text" name="目的详细地址/码头/仓号" class="dash-form-input" placeholder="地址 / 码头 / 仓号" data-i18n-ph="ts_dest_addr_ph">
</div>
</div>
<div>
<label class="dash-label mb-3"><i class="fas fa-box mr-1 text-gray-400"></i><span data-i18n="cargo_title">货物信息</span></label>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_container">柜型</label>
<select name="柜型" class="dash-form-input">
<option value="20GP">20GP</option>
<option value="40GP">40GP</option>
<option value="40HQ">40HQ</option>
<option value="45HQ">45HQ</option>
<option value="LTL" data-i18n="cargo_container_ltl">散货/托盘</option>
</select>
</div>
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_pieces">件数</label>
<input type="number" min="0" name="件数" class="dash-form-input" placeholder="0">
</div>
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_weight">总重量 (kg)</label>
<input type="number" min="0" step="0.01" name="总重量(kg)" class="dash-form-input" placeholder="0">
</div>
<div>
<label class="text-xs text-gray-500 mb-1 block" data-i18n="cargo_volume">总体积 (m³)</label>
<input type="number" min="0" step="0.01" name="总体积(m³)" class="dash-form-input" placeholder="0">
</div>
</div>
</div>
<div>
<label class="dash-label mb-3"><i class="fas fa-user mr-1 text-gray-400"></i><span data-i18n="contact_title">联系人信息</span></label>
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="dash-label"><span data-i18n="contact_name">联系人</span><span class="req">*</span></label>
<input type="text" name="联系人" required class="dash-form-input" placeholder="请输入联系人姓名" data-i18n-ph="contact_name_ph">
</div>
<div>
<label class="dash-label"><span data-i18n="contact_phone">联系方式</span><span class="req">*</span></label>
<input type="text" name="联系方式" required class="dash-form-input" placeholder="电话 / 微信 / 邮箱" data-i18n-ph="contact_phone_ph">
</div>
</div>
</div>
<div class="max-w-xs">
<label class="dash-label"><span data-i18n="req_time">要求时间</span></label>
<input type="date" name="要求时间" class="dash-form-input">
</div>
<div class="flex gap-3 pt-2">
<button type="submit" class="btn-primary text-white font-bold px-8 py-3.5 rounded-xl" data-i18n="dash_submit">提交订单</button>
<button type="button" onclick="showDashView('Home')" class="px-8 py-3.5 rounded-xl border border-gray-200 text-gray-600 font-medium hover:bg-gray-50" data-i18n="dash_cancel">取消</button>
</div>
</form>
</div>
</div>
<!-- 视图5:我的订单 -->
<div id="dashViewOrders" class="dash-view hidden">
<div class="bg-white rounded-2xl shadow-card border border-gray-100 p-10 lg:p-14 max-w-3xl mx-auto text-center">
<div class="dash-empty">
<i class="fas fa-clipboard-list"></i>
<h3 class="text-xl font-bold text-gray-700 mb-2" data-i18n="dash_orders_empty">暂无订单</h3>
<p class="text-gray-500" data-i18n="dash_orders_hint">您还没有提交任何订单,前往「业务总览」选择服务开始下单吧。</p>
</div>
</div>
</div>
</main>
<!-- 右侧信息栏 -->
<aside id="dashRight" class="dash-right hidden lg:flex flex-col">
<div class="dash-right-card">
<h4 data-i18n="right_account_title">账户信息</h4>
<div class="dash-account">
<div class="dash-account-avatar" id="dashAvatar">C</div>
<div>
<div class="dash-account-name" id="dashAccountName">虫虫出海客户</div>
<div class="dash-account-type" data-i18n="right_account_type">企业客户</div>
</div>
</div>
</div>
<div class="dash-right-card">
<h4 data-i18n="right_stat_title">数据概览</h4>
<div class="dash-stat-row">
<span class="dash-stat-label" data-i18n="right_stat_progress">进行中订单</span>
<span class="dash-stat-value" id="statProgress">0</span>
</div>
<div class="dash-stat-row">
<span class="dash-stat-label" data-i18n="right_stat_done">已完成订单</span>
<span class="dash-stat-value" id="statDone">0</span>
</div>
<div class="dash-stat-row">
<span class="dash-stat-label" data-i18n="right_stat_month">本月下单</span>
<span class="dash-stat-value" id="statMonth">0</span>
</div>
</div>
<div class="dash-right-card">
<h4 data-i18n="right_help_title">帮助与支持</h4>
<div class="dash-help-item" onclick="showToast(t('right_help_cs_toast'))">
<i class="fas fa-headset"></i><span data-i18n="right_help_cs">联系客服</span>
</div>
<div class="dash-help-item" onclick="showToast(t('right_help_faq_toast'))">
<i class="fas fa-question-circle"></i><span data-i18n="right_help_faq">常见问题</span>
</div>
<div class="dash-help-item" onclick="showToast(t('right_help_quote_toast'))">
<i class="fas fa-tag"></i><span data-i18n="right_help_quote">获取报价</span>
</div>
</div>
<div class="dash-tip">
<i class="fas fa-lightbulb"></i>
<span data-i18n="right_tip_text">提交订单后,专属客户经理将在 24 小时内与您联系,确认运输方案与报价。</span>
</div>
</aside>
</div>
</div>
</section>
<script>
// 工作台交互
function setDashUser(name) {
const display = name ? name : '虫虫出海客户';
const topEl = document.getElementById('dashUserName');
if (topEl) topEl.textContent = 'Hi, ' + display;
const accEl = document.getElementById('dashAccountName');
if (accEl) accEl.textContent = display;
const avEl = document.getElementById('dashAvatar');
if (avEl) avEl.textContent = display.charAt(0).toUpperCase();
}
function showDashView(name) {
document.querySelectorAll('#workbench .dash-view').forEach(v => v.classList.add('hidden'));
const view = document.getElementById('dashView' + name);
if (view) view.classList.remove('hidden');
document.querySelectorAll('.dash-nav-link').forEach(l => l.classList.toggle('active', l.getAttribute('data-view') === name));
window.scrollTo(0, 0);
}
function goDash(name) {
showDashView(name);
closeDashSidebar();
}
function toggleDashSidebar() {
const sb = document.getElementById('dashSidebar');
const ov = document.getElementById('dashOverlay');
if (!sb) return;
const open = sb.classList.toggle('open');
if (ov) ov.classList.toggle('show', open);
document.body.style.overflow = open ? 'hidden' : '';
}
function closeDashSidebar() {
const sb = document.getElementById('dashSidebar');
const ov = document.getElementById('dashOverlay');
if (sb) sb.classList.remove('open');
if (ov) ov.classList.remove('show');
document.body.style.overflow = '';
}
function openOrder(type) {
showDashView(type);
}
function doLogout() {
showToast(t('toast_logout'));
setTimeout(() => { window.location.href = 'index.html'; }, 700);
}
// 订单表单提交
document.querySelectorAll('.order-form').forEach(form => {
form.addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_order_ok'));
this.reset();
setTimeout(() => showDashView('Home'), 1200);
});
});
// ====== 多语言 i18n ======
const i18n = {
zh: {
dash_home_link: "返回官网", dash_logout: "退出登录",
dash_welcome_title: "选择业务,快速下单", dash_welcome_sub: "请选择您需要的业务类型,填写信息后提交订单,我们将尽快为您安排。",
dash_biz1_title: "海外采购", dash_biz1_desc: "采购代购 · 验货质检 · 集中发货",
dash_biz2_title: "美国本土卡", dash_biz2_desc: "港口提柜 · 仓到仓 · 仓到门",
dash_biz3_title: "美本土散板", dash_biz3_desc: "拼车拼载 · 打盘服务 · 灵活派送",
dash_start: "开始下单", dash_back: "返回业务选择", dash_submit: "提交订单", dash_cancel: "取消",
po_title: "海外采购下单", po_category: "采购品类", po_category_ph: "如:家居用品 / 3C 配件",
po_spec: "商品规格", po_spec_ph: "如:尺寸 / 颜色 / 材质", po_link: "商品链接", po_link_ph: "https://",
po_qty: "采购数量", po_qty_ph: "0", po_price: "预算单价", po_price_ph: "0.00",
po_address: "收货地址", po_address_ph: "请输入收货地址", po_contact: "联系人", po_contact_ph: "请输入联系人姓名",
po_phone: "联系电话", po_phone_ph: "请输入联系电话", po_note: "订单备注", po_note_ph: "其他需求说明(选填)",
tk_title: "美国本土卡下单",
pl_title: "美本土散板下单", pl_method: "运输方式", pl_method_pool: "拼车", pl_method_priority: "优先拼载",
pl_palletize: "是否打盘", pl_yes: "是", pl_no: "否",
ts_type: "运输类型", ts_type_port: "港口提柜", ts_type_w2w: "仓到仓", ts_type_w2d: "仓到门",
ts_origin: "起运地", ts_origin_ph: "城市 / 港口 / 州", ts_dest: "目的地", ts_dest_ph: "城市 / 州",
ts_origin_addr: "详细地址", ts_origin_addr_ph: "街道 / 门牌",
ts_dest_addr: "详细地址 / 码头 / 仓号", ts_dest_addr_ph: "地址 / 码头 / 仓号",
cargo_title: "货物信息", cargo_container: "柜型", cargo_container_ltl: "散货/托盘",
cargo_pieces: "件数", cargo_weight: "总重量 (kg)", cargo_volume: "总体积 (m³)",
req_time: "要求时间", contact_title: "联系人信息", contact_name: "联系人", contact_name_ph: "请输入联系人姓名", contact_phone: "联系方式", contact_phone_ph: "电话 / 微信 / 邮箱",
toast_logout: "已退出登录", toast_order_ok: "订单提交成功,我们将尽快为您安排!",
dash_menu_main: "主导航", dash_menu_overview: "业务总览", dash_menu_proc: "海外采购",
dash_menu_truck: "美国本土卡", dash_menu_pallet: "美本土散板", dash_menu_orders: "我的订单",
dash_orders_empty: "暂无订单", dash_orders_hint: "您还没有提交任何订单,前往「业务总览」选择服务开始下单吧。",
right_account_title: "账户信息", right_account_type: "企业客户",
right_stat_title: "数据概览", right_stat_progress: "进行中订单", right_stat_done: "已完成订单", right_stat_month: "本月下单",
right_help_title: "帮助与支持", right_help_cs: "联系客服", right_help_faq: "常见问题", right_help_quote: "获取报价",
right_help_cs_toast: "客服热线:400-XXX-XXXX(工作日 9:00-18:00)", right_help_faq_toast: "常见问题页面即将上线", right_help_quote_toast: "报价顾问将尽快与您联系",
right_tip_text: "提交订单后,专属客户经理将在 24 小时内与您联系,确认运输方案与报价。"
},
en: {
dash_home_link: "Back to Site", dash_logout: "Log Out",
dash_welcome_title: "Choose a Service to Order", dash_welcome_sub: "Select the service you need, fill in the details, and submit your order — we will arrange it for you shortly.",
dash_biz1_title: "Overseas Sourcing", dash_biz1_desc: "Sourcing · Inspection · Consolidation",
dash_biz2_title: "US Domestic Trucking", dash_biz2_desc: "Port Pickup · Warehouse-to-Warehouse · Door-to-Door",
dash_biz3_title: "US Pallet Break", dash_biz3_desc: "Pooled Freight · Palletizing · Flexible Delivery",
dash_start: "Start Order", dash_back: "Back to Services", dash_submit: "Submit Order", dash_cancel: "Cancel",
po_title: "Overseas Sourcing Order", po_category: "Product Category", po_category_ph: "e.g. Home goods / 3C accessories",
po_spec: "Specs", po_spec_ph: "e.g. Size / Color / Material", po_link: "Product Link", po_link_ph: "https://",
po_qty: "Quantity", po_qty_ph: "0", po_price: "Budget Unit Price", po_price_ph: "0.00",
po_address: "Shipping Address", po_address_ph: "Enter shipping address", po_contact: "Contact Name", po_contact_ph: "Enter contact name",
po_phone: "Contact Phone", po_phone_ph: "Enter phone number", po_note: "Order Notes", po_note_ph: "Other requirements (optional)",
tk_title: "US Domestic Trucking Order",
pl_title: "US Pallet Break Order", pl_method: "Transport Mode", pl_method_pool: "Pooled", pl_method_priority: "Priority Load",
pl_palletize: "Palletize?", pl_yes: "Yes", pl_no: "No",
ts_type: "Transport Type", ts_type_port: "Port Pickup", ts_type_w2w: "Warehouse to Warehouse", ts_type_w2d: "Warehouse to Door",
ts_origin: "Origin", ts_origin_ph: "City / Port / State", ts_dest: "Destination", ts_dest_ph: "City / State",
ts_origin_addr: "Detailed Address", ts_origin_addr_ph: "Street / Number",
ts_dest_addr: "Detailed Address / Pier / WH No.", ts_dest_addr_ph: "Address / Pier / WH No.",
cargo_title: "Cargo Info", cargo_container: "Container Type", cargo_container_ltl: "Loose / Pallet",
cargo_pieces: "Pieces", cargo_weight: "Total Weight (kg)", cargo_volume: "Total Volume (m³)",
req_time: "Required By", contact_title: "Contact Info", contact_name: "Contact Person", contact_name_ph: "Enter contact name", contact_phone: "Contact Number", contact_phone_ph: "Phone / WeChat / Email",
toast_logout: "Logged out", toast_order_ok: "Order submitted! We will arrange it for you shortly.",
dash_menu_main: "Main Menu", dash_menu_overview: "Overview", dash_menu_proc: "Overseas Sourcing",
dash_menu_truck: "US Trucking", dash_menu_pallet: "US Pallet Break", dash_menu_orders: "My Orders",
dash_orders_empty: "No Orders Yet", dash_orders_hint: "You haven't placed any orders yet. Go to Overview to start a new order.",
right_account_title: "Account", right_account_type: "Enterprise Client",
right_stat_title: "Overview", right_stat_progress: "In Progress", right_stat_done: "Completed", right_stat_month: "This Month",
right_help_title: "Help & Support", right_help_cs: "Contact Support", right_help_faq: "FAQ", right_help_quote: "Get a Quote",
right_help_cs_toast: "Support Hotline: 400-XXX-XXXX (Mon-Fri 9:00-18:00)", right_help_faq_toast: "FAQ page coming soon", right_help_quote_toast: "A quoting specialist will contact you shortly",
right_tip_text: "After you submit an order, your dedicated account manager will contact you within 24 hours to confirm the shipping plan and quote."
},
es: {
dash_home_link: "Volver al sitio", dash_logout: "Cerrar sesión",
dash_welcome_title: "Elige un servicio para pedir", dash_welcome_sub: "Selecciona el servicio que necesitas, completa los datos y envía tu pedido; lo gestionaremos lo antes posible.",
dash_biz1_title: "Abastecimiento en el extranjero", dash_biz1_desc: "Compras · Inspección · Consolidación",
dash_biz2_title: "Transporte por camión en EE.UU.", dash_biz2_desc: "Recogida en puerto · Almacén a almacén · Puerta a puerta",
dash_biz3_title: "Despaletizado en EE.UU.", dash_biz3_desc: "Carga compartida · Paletizado · Entrega flexible",
dash_start: "Iniciar pedido", dash_back: "Volver a servicios", dash_submit: "Enviar pedido", dash_cancel: "Cancelar",
po_title: "Pedido de abastecimiento", po_category: "Categoría del producto", po_category_ph: "ej. Artículos para el hogar / Accesorios 3C",
po_spec: "Especificaciones", po_spec_ph: "ej. Tamaño / Color / Material", po_link: "Enlace del producto", po_link_ph: "https://",
po_qty: "Cantidad", po_qty_ph: "0", po_price: "Precio unitario estimado", po_price_ph: "0.00",
po_address: "Dirección de envío", po_address_ph: "Ingresa la dirección de envío", po_contact: "Persona de contacto", po_contact_ph: "Ingresa el nombre de contacto",
po_phone: "Teléfono de contacto", po_phone_ph: "Ingresa el teléfono", po_note: "Notas del pedido", po_note_ph: "Otros requisitos (opcional)",
tk_title: "Pedido de camión en EE.UU.",
pl_title: "Pedido de despaletizado en EE.UU.", pl_method: "Modo de transporte", pl_method_pool: "Carga compartida", pl_method_priority: "Carga prioritaria",
pl_palletize: "¿Paletizar?", pl_yes: "Sí", pl_no: "No",
ts_type: "Tipo de transporte", ts_type_port: "Recogida en puerto", ts_type_w2w: "Almacén a almacén", ts_type_w2d: "Almacén a puerta",
ts_origin: "Origen", ts_origin_ph: "Ciudad / Puerto / Estado", ts_dest: "Destino", ts_dest_ph: "Ciudad / Estado",
ts_origin_addr: "Dirección detallada", ts_origin_addr_ph: "Calle / Número",
ts_dest_addr: "Dirección / Muelle / N.º almacén", ts_dest_addr_ph: "Dirección / Muelle / N.º almacén",
cargo_title: "Información de carga", cargo_container: "Tipo de contenedor", cargo_container_ltl: "Carga suelta / Paleta",
cargo_pieces: "Piezas", cargo_weight: "Peso total (kg)", cargo_volume: "Volumen total (m³)",
req_time: "Fecha requerida", contact_title: "Información de contacto", contact_name: "Persona de contacto", contact_name_ph: "Ingrese el nombre de contacto", contact_phone: "Número de contacto", contact_phone_ph: "Teléfono / WeChat / Email",
toast_logout: "Sesión cerrada", toast_order_ok: "¡Pedido enviado! Lo gestionaremos lo antes posible.",
dash_menu_main: "Menú principal", dash_menu_overview: "Resumen", dash_menu_proc: "Abastecimiento",
dash_menu_truck: "Camión en EE.UU.", dash_menu_pallet: "Despaletizado", dash_menu_orders: "Mis pedidos",
dash_orders_empty: "Sin pedidos", dash_orders_hint: "Aún no has enviado pedidos. Ve a Resumen para empezar uno nuevo.",
right_account_title: "Cuenta", right_account_type: "Cliente Empresarial",
right_stat_title: "Resumen", right_stat_progress: "En Progreso", right_stat_done: "Completados", right_stat_month: "Este Mes",
right_help_title: "Ayuda y Soporte", right_help_cs: "Contactar Soporte", right_help_faq: "Preguntas Frecuentes", right_help_quote: "Obtener Cotización",
right_help_cs_toast: "Línea de soporte: 400-XXX-XXXX (Lun-Vie 9:00-18:00)", right_help_faq_toast: "Página de preguntas próximamente", right_help_quote_toast: "Un especialista en cotización te contactará pronto",
right_tip_text: "Después de enviar el pedido, tu ejecutivo de cuenta te contactará en 24 horas para confirmar el plan de envío y la cotización."
}
};
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
if (i18n[lang][k] !== undefined) el.textContent = i18n[lang][k];
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('#langSwitcherDash [data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcherDash [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
// Toast 提示
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
// 初始化
document.addEventListener('DOMContentLoaded', function() {
applyLanguage(currentLang);
// 尝试从官网登录带入用户名(URL ?user=xxx)
const params = new URLSearchParams(window.location.search);
const userParam = params.get('user');
if (userParam) {
setDashUser(decodeURIComponent(userParam));
}
});
applyLanguage(currentLang);
</script>
</body>
</html>
+207 -6
View File
@@ -178,6 +178,61 @@
.nav-link.route-active { color:#69c46f!important;position:relative; }
.nav-link.route-active::after { content:'';position:absolute;left:50%;bottom:-9px;width:18px;height:2px;background:#4CAF50;border-radius:99px;transform:translateX(-50%); }
.nav-scrolled .nav-link.route-active { color:#1565C0!important; }
/* 五大业务独立详情页 */
.business-detail-page { padding-top:96px;background:#f5f7fb;min-height:100vh; }
.bd-hero { position:relative;min-height:420px;display:flex;align-items:center;overflow:hidden;background:#07182b var(--bd-bg) center/cover no-repeat; }
.bd-hero::before { content:'';position:absolute;inset:0;background:rgba(5,20,38,.78); }
.bd-hero::after { content:'';position:absolute;inset:0;background-image:radial-gradient(rgba(255,255,255,.12) 1px,transparent 1px);background-size:30px 30px;opacity:.5; }
.bd-hero-inner { position:relative;z-index:2;width:100%;padding:62px 0 70px; }
.bd-breadcrumb { display:flex;align-items:center;gap:9px;color:rgba(255,255,255,.6);font-size:13px;margin-bottom:34px; }
.bd-breadcrumb button { color:rgba(255,255,255,.78);transition:color .2s; }
.bd-breadcrumb button:hover { color:#fff; }
.bd-eyebrow { display:inline-flex;align-items:center;gap:10px;color:#8ed492;font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase; }
.bd-eyebrow::before { content:'';width:26px;height:2px;border-radius:99px;background:#4CAF50; }
.bd-title { margin-top:14px;color:#fff;font-size:clamp(44px,6vw,68px);font-weight:900;line-height:1.08;letter-spacing:-.035em; }
.bd-desc { max-width:760px;margin-top:20px;color:rgba(255,255,255,.76);font-size:17px;line-height:1.85; }
.bd-badges { display:flex;flex-wrap:wrap;gap:10px;margin-top:26px; }
.bd-badge { display:inline-flex;align-items:center;gap:7px;padding:8px 13px;border:1px solid rgba(255,255,255,.16);border-radius:999px;background:rgba(255,255,255,.08);color:rgba(255,255,255,.85);font-size:12px;backdrop-filter:blur(10px); }
.bd-badge i { color:#78ce7d; }
.bd-content { padding:76px 0 92px; }
.bd-section-head { display:flex;align-items:flex-end;justify-content:space-between;gap:24px;margin-bottom:28px; }
.bd-section-kicker { color:#1565C0;font-size:12px;font-weight:800;letter-spacing:.14em;text-transform:uppercase; }
.bd-section-title { margin-top:8px;color:#111827;font-size:30px;font-weight:800;letter-spacing:-.02em; }
.bd-section-copy { max-width:570px;color:#64748b;font-size:14px;line-height:1.75; }
.bd-feature-grid { display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-bottom:72px; }
.bd-feature-card { padding:24px;border:1px solid #e6eaf0;border-radius:18px;background:#fff;box-shadow:0 8px 28px rgba(15,23,42,.05);transition:transform .25s,box-shadow .25s,border-color .25s; }
.bd-feature-card:hover { transform:translateY(-5px);border-color:#c7d7ef;box-shadow:0 16px 36px rgba(15,23,42,.1); }
.bd-feature-icon { width:48px;height:48px;border-radius:14px;display:flex;align-items:center;justify-content:center;background:#edf5ff;color:#1565C0;font-size:19px; }
.bd-feature-card h3 { margin-top:18px;color:#1e293b;font-size:16px;font-weight:750; }
.bd-feature-card p { margin-top:9px;color:#64748b;font-size:13px;line-height:1.7; }
.bd-flow-card { border:1px solid #e3e8ef;border-radius:22px;background:#fff;box-shadow:0 16px 42px rgba(15,23,42,.07);overflow:hidden; }
.bd-flow-top { display:flex;align-items:center;justify-content:space-between;gap:16px;padding:22px 24px;border-bottom:1px solid #edf0f4; }
.bd-flow-top h3 { color:#1e293b;font-size:19px;font-weight:800; }
.bd-flow-switch { display:flex;gap:4px;padding:4px;border-radius:10px;background:#f1f4f8; }
.bd-flow-switch button { padding:8px 17px;border-radius:8px;color:#64748b;font-size:13px;font-weight:650;transition:all .2s; }
.bd-flow-switch button.active { color:#fff;background:#1565C0;box-shadow:0 5px 14px rgba(21,101,192,.24); }
.bd-flow-panel { display:none;padding:34px 28px 38px; }
.bd-flow-panel.active { display:block; }
.bd-simple-flow { display:flex;align-items:stretch;gap:0;overflow-x:auto;padding:4px 2px 14px; }
.bd-simple-step { flex:1;min-width:145px;position:relative;padding:20px 14px;text-align:center;border:1px solid #e5eaf1;border-radius:16px;background:#fbfcfe; }
.bd-simple-step:not(:last-child) { margin-right:44px; }
.bd-simple-step:not(:last-child)::after { content:'\f061';font-family:'Font Awesome 6 Free';font-weight:900;position:absolute;right:-31px;top:50%;transform:translateY(-50%);color:#76a7d9;font-size:15px; }
.bd-simple-icon { width:48px;height:48px;margin:0 auto;border-radius:50%;display:flex;align-items:center;justify-content:center;background:#eaf3ff;color:#1565C0;font-size:18px; }
.bd-simple-step strong { display:block;margin-top:13px;color:#1e293b;font-size:14px; }
.bd-simple-step span { display:block;margin-top:6px;color:#8491a4;font-size:11px;line-height:1.5; }
.bd-detail-flow { position:relative;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:18px; }
.bd-detail-step { position:relative;padding:20px;border:1px solid #e6eaf0;border-radius:16px;background:#fff;min-height:164px; }
.bd-detail-no { display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border-radius:9px;background:#1565C0;color:#fff;font-size:11px;font-weight:800; }
.bd-detail-step h4 { margin-top:15px;color:#1e293b;font-size:15px;font-weight:750; }
.bd-detail-step p { margin-top:8px;color:#64748b;font-size:12px;line-height:1.65; }
.bd-other { margin-top:72px; }
.bd-other-grid { display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;margin-top:24px; }
.bd-other-card { padding:18px;border:1px solid #e3e8ef;border-radius:15px;background:#fff;text-align:left;transition:all .22s; }
.bd-other-card:hover,.bd-other-card.active { border-color:#1565C0;background:#f2f7fd;transform:translateY(-3px); }
.bd-other-card span { display:block;color:#94a3b8;font-size:10px;font-weight:700;letter-spacing:.1em; }
.bd-other-card strong { display:block;margin-top:6px;color:#1e293b;font-size:14px; }
@media(max-width:1024px){.bd-feature-grid{grid-template-columns:repeat(2,1fr)}.bd-detail-flow{grid-template-columns:repeat(2,1fr)}.bd-other-grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:900px){
.core-hero{min-height:720px;height:100svh;max-height:none}
.core-hero-content{padding:112px 0 190px}
@@ -192,7 +247,35 @@
.subpage-hero-inner{padding:52px 0 48px}
.subpage-desc{font-size:15px;line-height:1.7}
.secondary-page>section{padding-top:56px!important;padding-bottom:64px!important}
.business-detail-page{padding-top:80px}.bd-hero{min-height:390px}.bd-hero-inner{padding:48px 0 56px}.bd-desc{font-size:15px}.bd-content{padding:54px 0 70px}.bd-section-head{display:block}.bd-section-copy{margin-top:14px}.bd-flow-top{align-items:flex-start;flex-direction:column}.bd-detail-flow{grid-template-columns:1fr}.bd-other-grid{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:560px){.bd-feature-grid{grid-template-columns:1fr}.bd-flow-panel{padding:24px 18px 28px}.bd-other-grid{grid-template-columns:1fr}.bd-flow-switch{width:100%}.bd-flow-switch button{flex:1}}
/* 流程图第二套展现:路线图 + 时间轴 */
.bd-flow-card { border:0;background:#0b1d32;box-shadow:0 20px 48px rgba(7,24,43,.18); }
.bd-flow-top { background:#0b1d32;border-bottom:1px solid rgba(255,255,255,.12); }
.bd-flow-top h3 { color:#fff; }
.bd-flow-switch { background:rgba(255,255,255,.1); }
.bd-flow-switch button { color:rgba(255,255,255,.66); }
.bd-flow-switch button.active { color:#0b1d32;background:#fff;box-shadow:none; }
.bd-flow-panel { background:#f5f8fc;overflow-x:auto; }
.bd-simple-flow { min-width:760px;display:grid;grid-template-columns:repeat(6,minmax(120px,1fr));gap:0;position:relative;overflow:visible;padding:24px 8px 30px; }
.bd-simple-flow::before { content:'';position:absolute;left:8%;right:8%;top:54px;height:2px;background:#b7cee7; }
.bd-simple-step { min-width:0;padding:0 9px;border:0;border-radius:0;background:transparent;z-index:1; }
.bd-simple-step:not(:last-child) { margin-right:0; }
.bd-simple-step:not(:last-child)::after { content:'';right:-1px;top:50px;width:7px;height:7px;border:solid #86a9ce;border-width:2px 2px 0 0;transform:rotate(45deg); }
.bd-simple-icon { width:60px;height:60px;border:6px solid #f5f8fc;background:#1565C0;color:#fff;box-shadow:0 0 0 2px #9fbeda;font-size:19px; }
.bd-simple-step strong { margin-top:17px;font-size:13px; }
.bd-simple-step span { max-width:130px;margin:7px auto 0;font-size:11px; }
.bd-detail-flow { display:block;position:relative;max-width:920px;margin:0 auto;padding:4px 0 8px; }
.bd-detail-flow::before { content:'';position:absolute;left:28px;top:20px;bottom:20px;width:2px;background:#c6d9ec; }
.bd-detail-step { display:grid;grid-template-columns:58px 1fr;column-gap:18px;min-height:0;margin:0 0 14px;padding:18px 22px 18px 0;border:1px solid #e2e9f2;border-radius:15px;background:#fff;box-shadow:0 7px 18px rgba(15,23,42,.05); }
.bd-detail-step:nth-child(even) { margin-left:72px; }
.bd-detail-step:nth-child(odd) { margin-right:72px; }
.bd-detail-no { grid-row:1 / span 2;align-self:start;justify-self:center;width:38px;height:38px;border:5px solid #f5f8fc;border-radius:50%;background:#1565C0;box-shadow:0 0 0 2px #9fbeda;font-size:10px;z-index:1; }
.bd-detail-step h4 { margin-top:1px;font-size:15px; }
.bd-detail-step p { margin-top:6px; }
@media(max-width:900px){.bd-detail-step:nth-child(even){margin-left:0}.bd-detail-step:nth-child(odd){margin-right:0}}
.nav-scrolled {
background: rgba(255, 255, 255, 0.98);
@@ -550,7 +633,7 @@
<div class="core-points"><span class="core-point" data-i18n="svc3_li1">托盘拆板组板</span><span class="core-point" data-i18n="svc3_li2">多地址分拣派送</span><span class="core-point" data-i18n="svc3_li4">仓储库存管理</span></div>
</div>
<div class="core-actions">
<button class="core-detail-btn" id="coreDetailBtn" onclick="navigateTo('business')"><span data-i18n="core_detail">查看业务详情</span><i class="fas fa-arrow-right"></i></button>
<button class="core-detail-btn" id="coreDetailBtn" onclick="navigateBusinessDetail('ocean')"><span data-i18n="core_detail">查看业务详情</span><i class="fas fa-arrow-right"></i></button>
<button class="core-arrow" type="button" onclick="changeCoreSlide(-1)" aria-label="上一个"><i class="fas fa-arrow-left"></i></button>
<button class="core-arrow" type="button" onclick="changeCoreSlide(1)" aria-label="下一个"><i class="fas fa-arrow-right"></i></button>
</div>
@@ -571,6 +654,45 @@
</section>
</section>
<!-- 五大业务独立详情页(内容由业务配置动态生成) -->
<section id="businessDetailSection" class="route-section business-detail-page">
<div class="bd-hero" id="bdHero">
<div class="bd-hero-inner">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bd-breadcrumb"><button onclick="navigateTo('home')">首页</button><i class="fas fa-chevron-right text-[9px]"></i><button onclick="navigateTo('business')">业务</button><i class="fas fa-chevron-right text-[9px]"></i><span id="bdBreadcrumbName">业务详情</span></div>
<span class="bd-eyebrow" id="bdEyebrow">Business Solution</span>
<h1 class="bd-title" id="bdTitle">业务详情</h1>
<p class="bd-desc" id="bdDesc"></p>
<div class="bd-badges" id="bdBadges"></div>
</div>
</div>
</div>
<div class="bd-content">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bd-section-head">
<div><div class="bd-section-kicker">SERVICE OVERVIEW</div><h2 class="bd-section-title">服务介绍</h2></div>
<p class="bd-section-copy" id="bdIntro"></p>
</div>
<div class="bd-feature-grid" id="bdFeatures"></div>
<div class="bd-section-head">
<div><div class="bd-section-kicker">HOW IT WORKS</div><h2 class="bd-section-title">业务操作流程</h2></div>
<p class="bd-section-copy">简版快速了解货物流转路径,详细版展示从建单到签收的关键操作节点。</p>
</div>
<div class="bd-flow-card">
<div class="bd-flow-top"><h3 id="bdFlowTitle">业务流程图</h3><div class="bd-flow-switch"><button class="active" data-flow-view="simple" onclick="switchBusinessFlow('simple',this)">简版流程</button><button data-flow-view="detail" onclick="switchBusinessFlow('detail',this)">详细流程</button></div></div>
<div class="bd-flow-panel active" id="bdSimplePanel"><div class="bd-simple-flow" id="bdSimpleFlow"></div></div>
<div class="bd-flow-panel" id="bdDetailPanel"><div class="bd-detail-flow" id="bdDetailFlow"></div></div>
</div>
<div class="bd-other">
<div class="bd-section-head"><div><div class="bd-section-kicker">MORE SOLUTIONS</div><h2 class="bd-section-title">查看其他业务</h2></div></div>
<div class="bd-other-grid" id="bdOtherBusiness"></div>
</div>
</div>
</div>
</section>
<!-- 核心服务 -->
<section id="servicesSection" class="route-section secondary-page">
<div class="subpage-hero" style="--sub-bg:url('assets/core-procurement.jpg')">
@@ -1164,7 +1286,7 @@
// 首页五大核心业务轮播
let coreSlideIndex = 0;
let coreSlideTimer = null;
const coreDetailRoutes = ['business', 'business', 'services', 'services', 'services'];
const coreBusinessSlugs = ['ocean', 'fulfillment', 'procurement', 'trucking', 'pallet'];
function showCoreSlide(index, restart = true) {
const slides = document.querySelectorAll('.core-slide');
@@ -1186,7 +1308,7 @@
}
});
const detailBtn = document.getElementById('coreDetailBtn');
if (detailBtn) detailBtn.onclick = () => navigateTo(coreDetailRoutes[coreSlideIndex]);
if (detailBtn) detailBtn.onclick = () => navigateBusinessDetail(coreBusinessSlugs[coreSlideIndex]);
if (restart) startCoreCarousel();
}
@@ -1197,7 +1319,11 @@
}
document.querySelectorAll('[data-core-tab]').forEach(tab => {
tab.addEventListener('click', () => showCoreSlide(Number(tab.dataset.coreTab)));
tab.addEventListener('click', () => {
const index = Number(tab.dataset.coreTab);
showCoreSlide(index);
navigateBusinessDetail(coreBusinessSlugs[index]);
});
});
const coreHero = document.querySelector('.core-hero');
if (coreHero) {
@@ -1206,6 +1332,79 @@
}
startCoreCarousel();
const businessDetails = {
ocean: {
name:'海运专线',eyebrow:'Ocean Freight',bg:"url('assets/core-ocean.jpg')",
desc:'连接中国主要港口与美国核心仓网,提供整柜、拼箱、美森快船与盐田普船等稳定的中美海运产品。',
intro:'从国内收货、装柜和出口报关,到美国目的港清关、海外仓入库及 FBA 派送,柏悦物流统一协调全链路节点,让货物按计划抵达销售仓。',
badges:[['fa-ship','每周固定船期'],['fa-file-shield','双清合规'],['fa-route','全程可追踪']],
features:[['fa-boxes-stacked','整柜与拼箱','按货量灵活选择 FCL、LCL,支持多票集货与统一出运。'],['fa-calendar-check','稳定船期','核心航线固定班期,提前锁定舱位,方便销售与库存计划。'],['fa-file-contract','清关协同','中美两端资料预审与申报协同,减少查验和滞港风险。'],['fa-warehouse','仓配衔接','到港后可接海外仓、FBA 预约及后续派送服务。']],
simple:[['fa-box-open','国内交货','货物送至集货仓'],['fa-cubes','装柜报关','装柜并完成出口申报'],['fa-ship','海运运输','按船期跨洋运输'],['fa-passport','美国清关','目的港清关提柜'],['fa-warehouse','海外仓入库','清点、上架、备货'],['fa-boxes-packing','FBA派送','预约送仓并签收']],
detail:[['01','需求建单','确认货量、品名、目的仓和船期,建立运输委托。'],['02','国内收货','核对件数、唛头与包装,测量体积重量并完成入仓。'],['03','配载装柜','多票货物按目的地配载,装柜加固并拍照留档。'],['04','出口报关','准备合同、发票、装箱单等资料,完成出口申报。'],['05','国际海运','船舶按计划离港,系统同步开船、到港及异常节点。'],['06','目的港清关','提交进口清关资料,完成税费处理与放行。'],['07','海外仓入库','提柜送仓,完成卸货、清点、上架和库存同步。'],['08','FBA派送','预约 FBA 入仓,安排尾程派送并回传签收凭证。']]
},
fulfillment: {
name:'一件代发',eyebrow:'Order Fulfillment',bg:"url('assets/core-fulfillment.jpg')",
desc:'以美国本土仓为履约中心,连接 Amazon、Walmart、独立站等销售渠道,订单产生后按件拣货、打包并快速发出。',
intro:'适合跨境电商卖家和多渠道品牌,将库存前置到美国仓库,由系统自动接单、分配库存和生成面单,提升订单履约速度。',
badges:[['fa-plug','多平台接入'],['fa-bolt','24小时出库'],['fa-chart-line','库存可视化']],
features:[['fa-arrows-to-circle','订单自动同步','对接主流平台与独立站,减少人工录单和漏单。'],['fa-barcode','精准拣货','按 SKU、库位和批次拣货,支持复核与称重。'],['fa-box','灵活包装','按渠道和商品要求完成装箱、贴标及合单。'],['fa-truck-fast','快速派送','自动匹配 USPS、UPS、FedEx 等渠道,回传轨迹。']],
simple:[['fa-warehouse','库存入仓','商品入库并上架'],['fa-cart-shopping','订单同步','平台订单进入系统'],['fa-hand-holding-box','拣货打包','按单拣货、复核、包装'],['fa-truck-fast','本土派送','匹配渠道并出库'],['fa-house','客户签收','回传轨迹与签收']],
detail:[['01','商品建档','维护 SKU、条码、包装和渠道规则,绑定货主库存。'],['02','头程入仓','货物抵达美国仓,完成收货、质检、上架和库位分配。'],['03','订单接入','API 或平台插件同步订单,校验地址、库存和风控规则。'],['04','订单审核','识别异常订单,合并拆分规则确认后进入履约队列。'],['05','波次拣货','按仓库、承运商和截单时间生成波次,拣货员扫码作业。'],['06','复核包装','复核 SKU 与数量,称重、打印面单并完成包装。'],['07','出库交接','与快递或卡车承运商交接,系统记录出库时间。'],['08','轨迹售后','同步跟踪号和签收状态,支持退件、换标与补发。']]
},
procurement: {
name:'海外采购',eyebrow:'Overseas Procurement',bg:"url('assets/core-procurement.jpg')",
desc:'从供应商寻源到质检集货,提供可控、透明的一站式采购执行服务,帮助品牌稳定获得海外商品与资源。',
intro:'柏悦物流协助客户完成供应商筛选、询价比价、样品确认、批量采购、质检和入仓,适用于电商备货、渠道采购及海外仓补货。',
badges:[['fa-magnifying-glass','供应商寻源'],['fa-clipboard-check','质检验货'],['fa-layer-group','集货发运']],
features:[['fa-address-book','供应商筛选','依据品类、资质、产能和交期建立供应商候选池。'],['fa-tags','询价比价','统一询价口径,比较采购价、交期和贸易条件。'],['fa-clipboard-check','质量把关','按确认样品和检验标准进行抽检并反馈结果。'],['fa-boxes-stacked','集中发运','多供应商货物集中到仓,合并包装后统一发运。']],
simple:[['fa-lightbulb','提出需求','确认品类与采购标准'],['fa-magnifying-glass','供应商寻源','筛选供应商并询价'],['fa-file-signature','下单采购','确认样品、价格和交期'],['fa-clipboard-check','验货质检','按标准抽检并反馈'],['fa-warehouse','集货入仓','集中收货、合包并发运']],
detail:[['01','需求确认','确认 SKU、数量、规格、目标成本和交期要求。'],['02','供应商寻源','筛选供应商并核验营业资质、产能和历史履约。'],['03','询价比价','获取阶梯价格与交期,输出采购建议和成本明细。'],['04','样品确认','寄送样品,确认材质、功能、包装和合规标签。'],['05','批量下单','签订采购订单,跟进排产、付款和交付节点。'],['06','到货验货','核对数量和外观,按抽检标准记录质量结果。'],['07','集货处理','多批货物集中入仓,完成合包、贴标和资料归档。'],['08','统一发运','根据销售计划安排海运、空运或海外仓补货。']]
},
trucking: {
name:'美本土卡车',eyebrow:'US Domestic Trucking',bg:"url('assets/core-trucking.jpg')",
desc:'覆盖美国主要区域的 FTL、LTL 与超大件运输网络,支持港口、海外仓、FBA 及客户地址之间的预约派送。',
intro:'从运输需求确认、车型与承运商匹配,到提货、在途跟踪、预约送货和签收回单,提供端到端的本土卡车管理。',
badges:[['fa-map-location-dot','全美网络'],['fa-truck','FTL / LTL'],['fa-location-crosshairs','GPS追踪']],
features:[['fa-route','线路规划','结合货量、时效和地址规划最优运输线路。'],['fa-truck-front','车型匹配','覆盖整车、零担、尾板和超大件等车型。'],['fa-calendar-days','预约派送','提前与仓库或收货方预约窗口,降低等待成本。'],['fa-file-signature','签收回单','留存 POD、照片和异常记录,方便对账追责。']],
simple:[['fa-file-circle-plus','提交需求','确认货量、地址和时效'],['fa-truck-front','匹配车辆','选择 FTL / LTL 车型'],['fa-box-open','提货装运','司机提货并核验货物'],['fa-location-dot','在途运输','GPS跟踪运输状态'],['fa-pen-to-square','预约签收','送达并回传POD']],
detail:[['01','运输建单','录入提货地、送货地、货物尺寸重量和预约窗口。'],['02','询价派车','根据线路和车型询价,确认承运商与报价。'],['03','预约提货','向发货仓预约提货时间,司机到场核对件数。'],['04','装车交接','检查托盘、唛头和封签,拍照完成交接。'],['05','在途跟踪','同步 GPS、里程和预计到达时间,异常主动预警。'],['06','目的地预约','与海外仓、FBA 或客户确认卸货时间。'],['07','现场交付','完成卸货、签收和异常备注,取得 POD。'],['08','费用结算','核对里程、附加费和等待费,完成承运商对账。']]
},
pallet: {
name:'美本土散板',eyebrow:'US Pallet Breakbulk',bg:"url('assets/core-pallet.jpg')",
desc:'面向托盘货物提供美国本土拆板、分拣、重组和多地址派送服务,适配 B2B 补货与多渠道订单。',
intro:'货物到达美国仓后,按 SKU、客户和目的地拆分托盘,重新组板、贴标并安排尾程派送,减少整板直发的限制。',
badges:[['fa-pallet','托盘作业'],['fa-arrows-split-up-and-left','拆分分拣'],['fa-share-nodes','多点配送']],
features:[['fa-boxes-stacked','拆板清点','按托盘和箱号拆分,清点数量并记录破损。'],['fa-filter','精细分拣','根据客户、SKU、渠道和目的地进行分拣。'],['fa-layer-group','重新组板','按照承运和收货要求重组托盘并加固。'],['fa-truck-ramp-box','多点派送','支持多个仓库、门店和客户地址分批配送。']],
simple:[['fa-pallet','托盘到仓','托盘货物入仓'],['fa-boxes-stacked','拆板清点','拆分并核对箱数'],['fa-filter','分拣组板','按目的地重新组板'],['fa-tags','贴标复核','打印标签并复核'],['fa-truck','分批派送','多地址预约交付']],
detail:[['01','托盘预报','登记托盘数量、尺寸、重量和预计到仓时间。'],['02','到仓收货','检查托盘外观、封签和箱数,登记收货差异。'],['03','拆板作业','按客户和 SKU 拆分托盘,扫描箱号并更新库存。'],['04','分区暂存','将货物放入对应作业区,标记目的地和作业批次。'],['05','订单分拣','根据派送订单拣选箱件,复核数量和包装状态。'],['06','重新组板','按承运商和收货要求组板、缠膜、加固并称重。'],['07','标签复核','打印运输标签和箱唛,校验地址、件数和渠道。'],['08','多点派送','安排卡车或快递分批派送,回传签收与异常凭证。']]
}
};
let activeBusinessSlug = 'ocean';
function renderBusinessDetail(slug) {
const data = businessDetails[slug] || businessDetails.ocean;
activeBusinessSlug = businessDetails[slug] ? slug : 'ocean';
const setText = (id,value) => { const el=document.getElementById(id); if(el)el.textContent=value; };
setText('bdBreadcrumbName',data.name); setText('bdEyebrow',data.eyebrow); setText('bdTitle',data.name); setText('bdDesc',data.desc); setText('bdIntro',data.intro); setText('bdFlowTitle',data.name+'流程图');
const hero=document.getElementById('bdHero'); if(hero)hero.style.setProperty('--bd-bg',data.bg);
const badges=document.getElementById('bdBadges'); if(badges)badges.innerHTML=data.badges.map(item=>`<span class="bd-badge"><i class="fas ${item[0]}"></i>${item[1]}</span>`).join('');
const features=document.getElementById('bdFeatures'); if(features)features.innerHTML=data.features.map(item=>`<article class="bd-feature-card"><div class="bd-feature-icon"><i class="fas ${item[0]}"></i></div><h3>${item[1]}</h3><p>${item[2]}</p></article>`).join('');
const simple=document.getElementById('bdSimpleFlow'); if(simple){simple.style.gridTemplateColumns=`repeat(${data.simple.length},minmax(120px,1fr))`;simple.innerHTML=data.simple.map(item=>`<div class="bd-simple-step"><div class="bd-simple-icon"><i class="fas ${item[0]}"></i></div><strong>${item[1]}</strong><span>${item[2]}</span></div>`).join('');}
const detail=document.getElementById('bdDetailFlow'); if(detail)detail.innerHTML=data.detail.map(item=>`<article class="bd-detail-step"><span class="bd-detail-no">${item[0]}</span><h4>${item[1]}</h4><p>${item[2]}</p></article>`).join('');
const other=document.getElementById('bdOtherBusiness'); if(other)other.innerHTML=Object.entries(businessDetails).map(([key,item],i)=>`<button class="bd-other-card ${key===activeBusinessSlug?'active':''}" onclick="navigateBusinessDetail('${key}')"><span>0${i+1} / BUSINESS</span><strong>${item.name}</strong></button>`).join('');
switchBusinessFlow('simple');
}
function switchBusinessFlow(view,button) {
document.querySelectorAll('[data-flow-view]').forEach(x=>x.classList.toggle('active',x.dataset.flowView===view));
const simple=document.getElementById('bdSimplePanel'),detail=document.getElementById('bdDetailPanel');
if(simple)simple.classList.toggle('active',view==='simple'); if(detail)detail.classList.toggle('active',view==='detail');
}
function navigateBusinessDetail(slug,syncHistory=true) {
renderBusinessDetail(slug);
navigateTo('business-detail',null,false);
updateRouteNav('business');
if(syncHistory)history.pushState({page:'business-detail',slug:activeBusinessSlug},'',window.location.pathname+window.location.search+'#business/'+activeBusinessSlug);
}
function updateRouteNav(page) {
const routeKeys = { home:'nav_home', services:'nav_services', process:'nav_process', business:'nav_business', about:'nav_about', contact:'nav_contact' };
document.querySelectorAll('[data-i18n^="nav_"]').forEach(link => {
@@ -1234,6 +1433,7 @@
'services': 'servicesSection',
'process': 'processSection',
'business': 'businessSection',
'business-detail': 'businessDetailSection',
'about': 'aboutSection',
'contact': 'contactSection',
};
@@ -1277,9 +1477,10 @@
}
function restoreRouteFromUrl() {
const page = window.location.hash.replace('#', '') || 'home';
const hash = window.location.hash.replace('#', '') || 'home';
if(hash.indexOf('business/')===0){ navigateBusinessDetail(hash.split('/')[1],false); return; }
const validPages = ['home', 'services', 'process', 'business', 'about', 'contact'];
navigateTo(validPages.includes(page) ? page : 'home', null, false);
navigateTo(validPages.includes(hash) ? hash : 'home', null, false);
}
window.addEventListener('popstate', restoreRouteFromUrl);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-225
View File
@@ -1,225 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 1100" width="1600" height="1100" role="img" aria-labelledby="title desc">
<title id="title">跨境物流全链路流程图</title>
<desc id="desc">从头程下单、到仓计费、出仓及装船发运,到尾程到港前、到港、到仓、出仓和签收的完整业务流程。</desc>
<defs>
<linearGradient id="pageBg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#F5F9FF"/>
<stop offset="1" stop-color="#F7FBFA"/>
</linearGradient>
<linearGradient id="firstMile" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#155EEF"/>
<stop offset="1" stop-color="#2878FF"/>
</linearGradient>
<linearGradient id="lastMile" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#087E8B"/>
<stop offset="1" stop-color="#16A085"/>
</linearGradient>
<linearGradient id="transit" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#EAF2FF"/>
<stop offset="0.5" stop-color="#DDF5F1"/>
<stop offset="1" stop-color="#EAF2FF"/>
</linearGradient>
<filter id="shadow" x="-15%" y="-15%" width="130%" height="140%">
<feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#16324F" flood-opacity="0.09"/>
</filter>
<filter id="softShadow" x="-20%" y="-20%" width="140%" height="150%">
<feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="#0F3057" flood-opacity="0.10"/>
</filter>
<marker id="arrowBlue" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M1 1L8 5L1 9" fill="none" stroke="#7AA7ED" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</marker>
<marker id="arrowGreen" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto">
<path d="M1 1L8 5L1 9" fill="none" stroke="#62B8AD" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</marker>
<style>
text{font-family:"PingFang SC","Microsoft YaHei","Noto Sans SC",Arial,sans-serif}
.title{font-size:34px;font-weight:700;fill:#102A43;letter-spacing:1px}
.subtitle{font-size:15px;fill:#627D98}
.lane-title{font-size:22px;font-weight:700;fill:#fff}
.lane-en{font-size:11px;font-weight:600;fill:#fff;opacity:.72;letter-spacing:1.7px}
.phase-title{font-size:18px;font-weight:700;fill:#102A43}
.phase-no{font-size:11px;font-weight:700;fill:#fff;letter-spacing:.5px}
.step{font-size:13px;fill:#334E68}
.step-small{font-size:12px;fill:#334E68}
.step-no{font-size:10px;font-weight:700;fill:#fff}
.summary-label{font-size:11px;font-weight:700;fill:#627D98;letter-spacing:1px}
.summary-value{font-size:15px;font-weight:700;fill:#243B53}
</style>
</defs>
<rect width="1600" height="1100" fill="url(#pageBg)"/>
<circle cx="1490" cy="50" r="180" fill="#D9F1ED" opacity=".42"/>
<circle cx="90" cy="1080" r="220" fill="#DFEBFF" opacity=".48"/>
<!-- 标题与概览 -->
<text x="52" y="58" class="title">跨境物流全链路流程</text>
<text x="52" y="88" class="subtitle">从客户下单到海外签收 · 头程与尾程全节点协同</text>
<g transform="translate(1195 28)">
<rect width="112" height="60" rx="14" fill="#fff" stroke="#D9E2EC"/>
<text x="18" y="22" class="summary-label">业务阶段</text><text x="18" y="45" class="summary-value">8 个阶段</text>
<rect x="124" width="112" height="60" rx="14" fill="#fff" stroke="#D9E2EC"/>
<text x="142" y="22" class="summary-label">关键动作</text><text x="142" y="45" class="summary-value">36 个节点</text>
<rect x="248" width="112" height="60" rx="14" fill="#fff" stroke="#D9E2EC"/>
<text x="266" y="22" class="summary-label">最终结果</text><text x="266" y="45" class="summary-value">完成签收</text>
</g>
<!-- 头程泳道 -->
<rect x="30" y="120" width="1540" height="430" rx="24" fill="#FFFFFF" stroke="#D7E4F4" filter="url(#shadow)"/>
<path d="M54 120H1546A24 24 0 0 1 1570 144V194H30V144A24 24 0 0 1 54 120Z" fill="url(#firstMile)"/>
<g transform="translate(58 142)">
<circle cx="22" cy="22" r="22" fill="#fff" opacity=".16"/>
<path d="M10 23h24M14 23v11h16V23M17 19v-6h10v6M21 27h3" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<text x="58" y="18" class="lane-title">头程物流</text>
<text x="58" y="38" class="lane-en">FIRST-MILE LOGISTICS · 国内集货与出口</text>
</g>
<g transform="translate(1306 145)">
<rect width="210" height="42" rx="21" fill="#fff" opacity=".15"/>
<text x="105" y="26" text-anchor="middle" font-size="13" font-weight="600" fill="#fff">下单 → 到仓 → 出仓</text>
</g>
<!-- 头程阶段连接 -->
<path d="M435 365H474" stroke="#7AA7ED" stroke-width="2" marker-end="url(#arrowBlue)"/>
<path d="M850 365H889" stroke="#7AA7ED" stroke-width="2" marker-end="url(#arrowBlue)"/>
<!-- 一、下单 -->
<g transform="translate(55 220)">
<rect width="380" height="300" rx="18" fill="#F8FBFF" stroke="#C9DBF7"/>
<circle cx="32" cy="32" r="17" fill="#155EEF"/><text x="32" y="36" text-anchor="middle" class="phase-no">01</text>
<text x="60" y="39" class="phase-title">下单</text>
<text x="326" y="37" font-size="11" fill="#829AB1">4 个动作</text>
<line x1="20" y1="64" x2="360" y2="64" stroke="#D9E8FA"/>
<g transform="translate(20 82)">
<rect width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="21" r="11" fill="#3B82F6"/><text x="22" y="25" text-anchor="middle" class="step-no">1</text><text x="44" y="26" class="step">客户下单</text>
<rect y="52" width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="73" r="11" fill="#3B82F6"/><text x="22" y="77" text-anchor="middle" class="step-no">2</text><text x="44" y="78" class="step">委托我们下单</text>
<rect y="104" width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="125" r="11" fill="#3B82F6"/><text x="22" y="129" text-anchor="middle" class="step-no">3</text><text x="44" y="130" class="step">快速下单(只有唛头)</text>
<rect y="156" width="340" height="42" rx="10" fill="#fff" stroke="#DFEAF8"/><circle cx="22" cy="177" r="11" fill="#3B82F6"/><text x="22" y="181" text-anchor="middle" class="step-no">4</text><text x="44" y="182" class="step">换标下单(中转、退货)</text>
</g>
</g>
<!-- 二、到仓 -->
<g transform="translate(470 220)">
<rect width="380" height="300" rx="18" fill="#F8FBFF" stroke="#C9DBF7"/>
<circle cx="32" cy="32" r="17" fill="#155EEF"/><text x="32" y="36" text-anchor="middle" class="phase-no">02</text>
<text x="60" y="39" class="phase-title">到仓</text>
<text x="326" y="37" font-size="11" fill="#829AB1">6 个动作</text>
<line x1="20" y1="64" x2="360" y2="64" stroke="#D9E8FA"/>
<g transform="translate(20 76)">
<rect width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="16" r="10" fill="#3B82F6"/><text x="20" y="20" text-anchor="middle" class="step-no">1</text><text x="40" y="21" class="step">预约取货</text>
<rect y="36" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="52" r="10" fill="#3B82F6"/><text x="20" y="56" text-anchor="middle" class="step-no">2</text><text x="40" y="57" class="step">送货上门</text>
<rect y="72" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="88" r="10" fill="#3B82F6"/><text x="20" y="92" text-anchor="middle" class="step-no">3</text><text x="40" y="93" class="step">入库测量(称重计费)</text>
<rect y="108" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="124" r="10" fill="#3B82F6"/><text x="20" y="128" text-anchor="middle" class="step-no">4</text><text x="40" y="129" class="step">调整差异核对</text>
<rect y="144" width="340" height="32" rx="8" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="160" r="10" fill="#3B82F6"/><text x="20" y="164" text-anchor="middle" class="step-no">5</text><text x="40" y="165" class="step">仓库作业(指定库位)</text>
<rect y="180" width="340" height="32" rx="8" fill="#EAF2FF" stroke="#BFD3F5"/><circle cx="20" cy="196" r="10" fill="#155EEF"/><text x="20" y="200" text-anchor="middle" class="step-no">6</text><text x="40" y="201" class="step" font-weight="600">计费</text>
</g>
</g>
<!-- 三、出仓 -->
<g transform="translate(885 220)">
<rect width="660" height="300" rx="18" fill="#F8FBFF" stroke="#C9DBF7"/>
<circle cx="32" cy="32" r="17" fill="#155EEF"/><text x="32" y="36" text-anchor="middle" class="phase-no">03</text>
<text x="60" y="39" class="phase-title">出仓</text>
<text x="604" y="37" font-size="11" fill="#829AB1">9 个动作</text>
<line x1="20" y1="64" x2="640" y2="64" stroke="#D9E8FA"/>
<g transform="translate(20 76)">
<rect width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="18" r="10" fill="#3B82F6"/><text x="20" y="22" text-anchor="middle" class="step-no">1</text><text x="40" y="23" class="step-small">智能排柜</text>
<rect y="42" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="60" r="10" fill="#3B82F6"/><text x="20" y="64" text-anchor="middle" class="step-no">2</text><text x="40" y="65" class="step-small">订舱</text>
<rect y="84" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="102" r="10" fill="#3B82F6"/><text x="20" y="106" text-anchor="middle" class="step-no">3</text><text x="40" y="107" class="step-small">预约托车</text>
<rect y="126" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="144" r="10" fill="#3B82F6"/><text x="20" y="148" text-anchor="middle" class="step-no">4</text><text x="40" y="149" class="step-small">PDA 拣货</text>
<rect y="168" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="20" cy="186" r="10" fill="#3B82F6"/><text x="20" y="190" text-anchor="middle" class="step-no">5</text><text x="40" y="191" class="step-small">拍货柜及铅封照上传</text>
<rect x="320" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="18" r="10" fill="#3B82F6"/><text x="340" y="22" text-anchor="middle" class="step-no">6</text><text x="360" y="23" class="step-small">客户上传报关资料</text>
<rect x="320" y="42" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="60" r="10" fill="#3B82F6"/><text x="340" y="64" text-anchor="middle" class="step-no">7</text><text x="360" y="65" class="step-small">整理给到报关代理报关</text>
<rect x="320" y="84" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="102" r="10" fill="#3B82F6"/><text x="340" y="106" text-anchor="middle" class="step-no">8</text><text x="360" y="107" class="step-small">回执上传系统供客户下载</text>
<rect x="320" y="126" width="300" height="36" rx="9" fill="#fff" stroke="#DFEAF8"/><circle cx="340" cy="144" r="10" fill="#3B82F6"/><text x="340" y="148" text-anchor="middle" class="step-no">9</text><text x="360" y="149" class="step-small">查港务信息(预计开船、到港等)</text>
<g transform="translate(320 171)"><rect width="300" height="32" rx="16" fill="#E7F0FF"/><circle cx="24" cy="16" r="8" fill="#155EEF"/><path d="M20 16h8M24 12v8" stroke="#fff" stroke-width="1.5"/><text x="44" y="21" font-size="12" font-weight="600" fill="#155EEF">完成国内出仓,等待装船</text></g>
</g>
</g>
<!-- 海运衔接带 -->
<g transform="translate(350 570)" filter="url(#softShadow)">
<rect width="900" height="74" rx="37" fill="url(#transit)" stroke="#B9D8DF"/>
<path d="M0 37H286M614 37H900" stroke="#78AFC1" stroke-width="1.5" stroke-dasharray="7 7"/>
<g transform="translate(302 12)">
<circle cx="25" cy="25" r="25" fill="#fff"/>
<path d="M11 29h28l-4 7H16l-5-7Zm5-3h19l-3-11H19l-3 11Zm8-11V9h4v6" fill="none" stroke="#087E8B" stroke-width="2" stroke-linejoin="round"/>
<text x="68" y="23" font-size="16" font-weight="700" fill="#0B6070">装船发运 · 国际海运在途</text>
<text x="68" y="43" font-size="12" fill="#527C86">头程完成,进入海外清关与尾程履约</text>
</g>
</g>
<!-- 尾程泳道 -->
<rect x="30" y="670" width="1540" height="400" rx="24" fill="#FFFFFF" stroke="#CFE7E2" filter="url(#shadow)"/>
<path d="M54 670H1546A24 24 0 0 1 1570 694V744H30V694A24 24 0 0 1 54 670Z" fill="url(#lastMile)"/>
<g transform="translate(58 692)">
<circle cx="22" cy="22" r="22" fill="#fff" opacity=".16"/>
<path d="M10 27h7l3-10h10l3 10h3M15 27a4 4 0 1 0 0 8 4 4 0 0 0 0-8Zm20 0a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<text x="58" y="18" class="lane-title">尾程物流</text>
<text x="58" y="38" class="lane-en">LAST-MILE LOGISTICS · 海外清关、入仓与派送</text>
</g>
<g transform="translate(1185 695)">
<rect width="330" height="42" rx="21" fill="#fff" opacity=".15"/>
<text x="165" y="26" text-anchor="middle" font-size="13" font-weight="600" fill="#fff">到港前 → 到港 → 到仓 → 出仓 → 签收</text>
</g>
<!-- 尾程连接线 -->
<path d="M328 900H352" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<path d="M633 900H657" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<path d="M938 900H962" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<path d="M1243 900H1267" stroke="#62B8AD" stroke-width="2" marker-end="url(#arrowGreen)"/>
<!-- 尾程阶段卡片 -->
<g transform="translate(48 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">01</text><text x="54" y="35" class="phase-title">到港前</text><text x="54" y="53" font-size="11" fill="#648A87">船在海上</text>
<g transform="translate(16 72)">
<rect width="248" height="36" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="18" r="10" fill="#13A18D"/><text x="18" y="22" text-anchor="middle" class="step-no">1</text><text x="38" y="23" class="step-small">FBA 仓预约</text>
<rect y="42" width="248" height="44" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="64" r="10" fill="#13A18D"/><text x="18" y="68" text-anchor="middle" class="step-no">2</text><text x="38" y="61" class="step-small">BOL 预清货</text><text x="38" y="77" font-size="10" fill="#829AB1">时间、费用、目的仓</text>
<rect y="92" width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="113" r="10" fill="#13A18D"/><text x="18" y="117" text-anchor="middle" class="step-no">3</text><text x="38" y="110" class="step-small">清关资料上传到</text><text x="38" y="126" class="step-small">对应货柜</text>
<rect y="140" width="248" height="36" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="158" r="10" fill="#13A18D"/><text x="18" y="162" text-anchor="middle" class="step-no">4</text><text x="38" y="163" class="step-small">明细备注货物中转等</text>
</g>
</g>
<g transform="translate(353 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">02</text><text x="54" y="35" class="phase-title">到港</text>
<g transform="translate(16 82)">
<rect width="248" height="50" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="25" r="10" fill="#13A18D"/><text x="18" y="29" text-anchor="middle" class="step-no">1</text><text x="38" y="30" class="step-small">清关放行</text>
<rect y="60" width="248" height="64" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="92" r="10" fill="#13A18D"/><text x="18" y="96" text-anchor="middle" class="step-no">2</text><text x="38" y="88" class="step-small">DO 或 PU 邮发</text><text x="38" y="106" class="step-small">通知码头车队提柜</text>
<rect y="136" width="248" height="38" rx="19" fill="#E3F6F2"/><text x="124" y="160" text-anchor="middle" font-size="12" font-weight="600" fill="#087E8B">完成提柜 · 运往海外仓</text>
</g>
</g>
<g transform="translate(658 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">03</text><text x="54" y="35" class="phase-title">到仓</text>
<g transform="translate(16 82)">
<rect width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="21" r="10" fill="#13A18D"/><text x="18" y="25" text-anchor="middle" class="step-no">1</text><text x="38" y="26" class="step-small">开柜拍照比对</text>
<rect y="50" width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="71" r="10" fill="#13A18D"/><text x="18" y="75" text-anchor="middle" class="step-no">2</text><text x="38" y="76" class="step-small">根据 PDA 需求打托作业</text>
<rect y="100" width="248" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="121" r="10" fill="#13A18D"/><text x="18" y="125" text-anchor="middle" class="step-no">3</text><text x="38" y="126" class="step-small">派送车队筛选预约</text>
</g>
</g>
<g transform="translate(963 770)">
<rect width="280" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">04</text><text x="54" y="35" class="phase-title">出仓</text>
<g transform="translate(16 72)">
<rect width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="17" r="9" fill="#13A18D"/><text x="18" y="21" text-anchor="middle" class="step-no">1</text><text x="36" y="22" class="step-small">预排车转 BOL 送货</text>
<rect y="40" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="57" r="9" fill="#13A18D"/><text x="18" y="61" text-anchor="middle" class="step-no">2</text><text x="36" y="62" class="step-small">PDA 拣货装车</text>
<rect y="80" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="97" r="9" fill="#13A18D"/><text x="18" y="101" text-anchor="middle" class="step-no">3</text><text x="36" y="102" class="step-small">派送车队到仓装车</text>
<rect y="120" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="137" r="9" fill="#13A18D"/><text x="18" y="141" text-anchor="middle" class="step-no">4</text><text x="36" y="142" class="step-small">整柜派送</text>
<rect y="160" width="248" height="34" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="177" r="9" fill="#13A18D"/><text x="18" y="181" text-anchor="middle" class="step-no">5</text><text x="36" y="182" class="step-small">自提出仓(一件代发)</text>
</g>
</g>
<g transform="translate(1268 770)">
<rect width="284" height="268" rx="16" fill="#F7FCFB" stroke="#CBE7E1"/>
<circle cx="28" cy="29" r="16" fill="#087E8B"/><text x="28" y="33" text-anchor="middle" class="phase-no">05</text><text x="54" y="35" class="phase-title">签收</text>
<g transform="translate(16 82)">
<rect width="252" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="21" r="10" fill="#13A18D"/><text x="18" y="25" text-anchor="middle" class="step-no">1</text><text x="38" y="26" class="step-small">私人仓签收回执上传</text>
<rect y="50" width="252" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="71" r="10" fill="#13A18D"/><text x="18" y="75" text-anchor="middle" class="step-no">2</text><text x="38" y="76" class="step-small">FBA 仓签收回执上传</text>
<rect y="100" width="252" height="42" rx="9" fill="#fff" stroke="#D8EBE7"/><circle cx="18" cy="121" r="10" fill="#13A18D"/><text x="18" y="125" text-anchor="middle" class="step-no">3</text><text x="38" y="126" class="step-small">送仓异常处理</text>
<g transform="translate(0 156)"><rect width="252" height="38" rx="19" fill="#087E8B"/><circle cx="24" cy="19" r="9" fill="#fff" opacity=".2"/><path d="M20 19l3 3 6-7" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><text x="132" y="24" text-anchor="middle" font-size="13" font-weight="700" fill="#fff">全流程完成 · 已签收</text></g>
</g>
</g>
<text x="800" y="1088" text-anchor="middle" font-size="11" fill="#829AB1">HEAD-HAUL &amp; LAST-MILE LOGISTICS PROCESS · 全程节点可追踪</text>
</svg>

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 881 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because it is too large Load Diff
-266
View File
@@ -1,266 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="loginSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1494412574643-ff11b0a5c1c3?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="login_welcome">欢迎回来</h2>
<p class="text-white/80" data-i18n="login_desc">登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat1">全程可视化追踪</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat2">智能订单管理</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="login_feat3">在线询价下单</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="login_title">账号登录</h2>
<p class="text-gray-600 mb-8"><span data-i18n="login_no_account">还没有账号?</span><button onclick="window.location.href='register.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="login_register_link">立即注册</button></p>
<form id="loginForm" class="space-y-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_account_label">邮箱 / 手机号</label>
<div class="relative">
<i class="fas fa-user absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="text" name="account" required placeholder="请输入邮箱或手机号" data-i18n-ph="login_account_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="login_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请输入密码" data-i18n-ph="login_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-center justify-between text-sm">
<label class="flex items-center text-gray-600 cursor-pointer">
<input type="checkbox" class="w-4 h-4 text-brand-blue rounded border-gray-300 mr-2">
<span data-i18n="login_remember">记住我</span>
</label>
<button type="button" onclick="showToast(t('toast_resetpw'))" class="text-brand-blue hover:underline" data-i18n="login_forgot">忘记密码?</button>
</div>
<button type="submit" class="btn-primary w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="login_submit">
登 录
</button>
</form>
<div class="mt-8">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-200"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500" data-i18n="login_other">其他方式登录</span>
</div>
</div>
<div class="flex justify-center space-x-4 mt-6">
<button onclick="showToast('微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-green-600 hover:bg-green-50 transition-colors">
<i class="fab fa-weixin text-xl"></i>
</button>
<button onclick="showToast('企业微信登录即将上线')" class="w-12 h-12 rounded-full border border-gray-200 flex items-center justify-center text-blue-600 hover:bg-blue-50 transition-colors">
<i class="fas fa-briefcase text-xl"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
login_welcome: "欢迎回来",
login_desc: "登录虫虫出海平台,实时追踪货物轨迹,管理订单与库存。",
login_feat1: "全程可视化追踪",
login_feat2: "智能订单管理",
login_feat3: "在线询价下单",
login_title: "账号登录",
login_no_account: "还没有账号?",
login_register_link: "立即注册",
login_account_label: "邮箱 / 手机号",
login_account_ph: "请输入邮箱或手机号",
login_password_label: "密码",
login_password_ph: "请输入密码",
login_remember: "记住我",
login_forgot: "忘记密码?",
login_submit: "登 录",
login_other: "其他方式登录",
toast_resetpw: "请联系客服重置密码",
toast_login_ok: "登录成功!欢迎回来"
},
en: {
login_welcome: "Welcome Back",
login_desc: "Log in to track shipments in real time and manage orders and inventory.",
login_feat1: "Full Visual Tracking",
login_feat2: "Smart Order Management",
login_feat3: "Online Quoting & Ordering",
login_title: "Account Login",
login_no_account: "No account yet?",
login_register_link: "Sign Up Now",
login_account_label: "Email / Phone",
login_account_ph: "Enter email or phone",
login_password_label: "Password",
login_password_ph: "Enter password",
login_remember: "Remember Me",
login_forgot: "Forgot Password?",
login_submit: "Log In",
login_other: "Other Sign-In Methods",
toast_resetpw: "Please contact support to reset your password",
toast_login_ok: "Login successful! Welcome back"
},
es: {
login_welcome: "Bienvenido de nuevo",
login_desc: "Inicia sesión para rastrear envíos en tiempo real y gestionar pedidos e inventario.",
login_feat1: "Seguimiento visual completo",
login_feat2: "Gestión inteligente de pedidos",
login_feat3: "Cotización y pedidos en línea",
login_title: "Inicio de sesión",
login_no_account: "¿Aún no tienes cuenta?",
login_register_link: "Regístrate ahora",
login_account_label: "Correo / Teléfono",
login_account_ph: "Ingresa correo o teléfono",
login_password_label: "Contraseña",
login_password_ph: "Ingresa tu contraseña",
login_remember: "Recuérdame",
login_forgot: "¿Olvidó su contraseña?",
login_submit: "Iniciar sesión",
login_other: "Otros métodos de acceso",
toast_resetpw: "Contacta a soporte para restablecer tu contraseña",
toast_login_ok: "¡Inicio de sesión exitoso! Bienvenido"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_login_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 KiB

-286
View File
@@ -1,286 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%s</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = { theme: { extend: { colors: { brand: { green:'#4CAF50', greenDark:'#2E7D32', blue:'#1565C0', blueDark:'#0D47A1', teal:'#00838F', light:'#F0F7FF', dark:'#0A1628' } }, fontFamily: { sans:['Inter','Noto Sans SC','system-ui','sans-serif'] } } } };
</script>
<style>
:root { --primary:#1565C0; --primary-dark:#0D47A1; --accent:#4CAF50; --accent-dark:#2E7D32; }
* { scroll-behavior:smooth; }
body { font-family:'Inter','Noto Sans SC',system-ui,sans-serif; overflow-x:hidden; }
.gradient-text { background:linear-gradient(135deg,#1565C0 0%,#4CAF50 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; }
.gradient-bg { background:linear-gradient(135deg,#0A1628 0%,#1565C0 50%,#00838F 100%); }
.btn-primary { background:linear-gradient(135deg,#1565C0 0%,#0D47A1 100%); transition:all .3s ease; }
.btn-primary:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(21,101,192,.35); }
.btn-accent { background:linear-gradient(135deg,#4CAF50 0%,#2E7D32 100%); transition:all .3s ease; }
.btn-accent:hover { transform:translateY(-2px); box-shadow:0 10px 30px rgba(76,175,80,.35); }
.input-field { transition:all .3s ease; }
.input-field:focus { border-color:#1565C0; box-shadow:0 0 0 4px rgba(21,101,192,.1); }
.auth-logo { display:block; filter:brightness(0) saturate(100%) invert(20%) sepia(84%) saturate(1926%) hue-rotate(202deg) brightness(94%) contrast(93%); }
.lang-switcher { display:flex; align-items:center; background:rgba(21,101,192,.08); border-radius:9999px; padding:3px; border:1px solid rgba(21,101,192,.15); }
.lang-switcher button { font-size:12px; font-weight:600; padding:4px 9px; border-radius:9999px; color:#0A1628; transition:all .25s ease; white-space:nowrap; }
.lang-switcher button:hover { color:#1565C0; }
.lang-switcher button.active { background:#1565C0; color:#fff; }
</style>
</head>
<body class="text-gray-800 antialiased bg-gray-50">
<!-- 顶部栏 -->
<header class="fixed top-0 inset-x-0 z-50 bg-white/95 backdrop-blur border-b border-gray-100">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<a href="index.html" class="flex items-center" aria-label="返回官网">
<img src="assets/logo.png" alt="柏悦物流" class="auth-logo" style="width:161px;height:60px;object-fit:contain;">
</a>
<div class="lang-switcher" id="langSwitcher">
<button data-lang="zh" class="active" title="中文">中</button>
<button data-lang="en" title="English">EN</button>
<button data-lang="es" title="Español (México)">🇲🇽</button>
</div>
</div>
</header>
<section id="registerSection" class="min-h-screen pt-24 pb-12 bg-gray-50 flex items-center">
<div class="w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-3xl shadow-2xl overflow-hidden">
<div class="grid lg:grid-cols-2">
<!-- 左侧视觉 -->
<div class="hidden lg:block relative">
<img src="https://images.unsplash.com/photo-1566576912321-d58ddd7a6088?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80" alt="物流仓储" class="absolute inset-0 w-full h-full object-cover">
<div class="absolute inset-0 gradient-bg opacity-90"></div>
<div class="relative z-10 p-12 h-full flex flex-col justify-between text-white">
<div>
<img src="assets/logo.png" alt="虫虫出海" style="width:161px;height:60px;object-fit:contain;">
<h2 class="text-3xl font-bold mb-4" data-i18n="reg_welcome">加入我们</h2>
<p class="text-white/80" data-i18n="reg_welcome_desc">注册虫虫出海企业账号,开启高效跨境物流体验。</p>
</div>
<div class="space-y-4">
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat1">新用户专属优惠</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat2">免费获取物流方案</span>
</div>
<div class="flex items-center space-x-3">
<i class="fas fa-check-circle text-brand-green"></i>
<span data-i18n="reg_feat3">专属客户经理对接</span>
</div>
</div>
</div>
</div>
<!-- 右侧表单 -->
<div class="p-8 sm:p-12 lg:p-16">
<div class="max-w-md mx-auto">
<h2 class="text-3xl font-bold text-gray-900 mb-2" data-i18n="reg_title">企业注册</h2>
<p class="text-gray-600 mb-8"><span data-i18n="reg_have_account">已有账号?</span><button onclick="window.location.href='login.html'" class="text-brand-blue font-semibold hover:underline" data-i18n="reg_login_link">立即登录</button></p>
<form id="registerForm" class="space-y-5">
<div class="grid sm:grid-cols-2 gap-5">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_company_label">企业名称</label>
<input type="text" name="company" required placeholder="请输入企业名称" data-i18n-ph="reg_company_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_contact_label">联系人</label>
<input type="text" name="contact" required placeholder="请输入联系人姓名" data-i18n-ph="reg_contact_ph" class="input-field w-full px-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_email_label">邮箱</label>
<div class="relative">
<i class="fas fa-envelope absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="email" name="email" required placeholder="请输入企业邮箱" data-i18n-ph="reg_email_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_phone_label">手机号</label>
<div class="relative">
<i class="fas fa-phone absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="tel" name="phone" required placeholder="请输入手机号" data-i18n-ph="reg_phone_ph" class="input-field w-full pl-11 pr-4 py-3.5 border border-gray-200 rounded-xl outline-none">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2" data-i18n="reg_password_label">密码</label>
<div class="relative">
<i class="fas fa-lock absolute left-4 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input type="password" name="password" required placeholder="请设置8-20位密码" data-i18n-ph="reg_password_ph" class="input-field w-full pl-11 pr-12 py-3.5 border border-gray-200 rounded-xl outline-none">
<button type="button" class="toggle-password absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div class="flex items-start">
<input type="checkbox" id="agree" required class="w-4 h-4 text-brand-blue rounded border-gray-300 mt-1 mr-2">
<label for="agree" class="text-sm text-gray-600">
<span data-i18n="reg_agree">我已阅读并同意</span> <button type="button" onclick="showToast(t('toast_tos'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_tos">《用户服务协议》</button> 和 <button type="button" onclick="showToast(t('toast_pp'))" class="text-brand-blue hover:underline" data-i18n="reg_agree_pp">《隐私政策》</button>
</label>
</div>
<button type="submit" class="btn-accent w-full text-white font-bold py-4 rounded-xl text-lg" data-i18n="reg_submit">
立即注册
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</section> <!-- Toast 提示 -->
<div id="toast" class="fixed top-24 left-1/2 -translate-x-1/2 z-[60] opacity-0 pointer-events-none transition-all duration-300 transform -translate-y-4">
<div class="bg-gray-900 text-white px-6 py-3 rounded-full shadow-2xl flex items-center space-x-2">
<i class="fas fa-info-circle text-brand-green"></i>
<span id="toastMessage">提示信息</span>
</div>
</div>
<script>
let currentLang = localStorage.getItem('cc_lang') || 'zh';
function t(key) {
return (i18n[currentLang] && i18n[currentLang][key] !== undefined) ? i18n[currentLang][key] : key;
}
const i18n = {
zh: {
reg_welcome: "加入我们",
reg_welcome_desc: "注册虫虫出海企业账号,开启高效跨境物流体验。",
reg_feat1: "新用户专属优惠",
reg_feat2: "免费获取物流方案",
reg_feat3: "专属客户经理对接",
reg_title: "企业注册",
reg_have_account: "已有账号?",
reg_login_link: "立即登录",
reg_company_label: "企业名称",
reg_company_ph: "请输入企业名称",
reg_contact_label: "联系人",
reg_contact_ph: "请输入联系人姓名",
reg_email_label: "邮箱",
reg_email_ph: "请输入企业邮箱",
reg_phone_label: "手机号",
reg_phone_ph: "请输入手机号",
reg_password_label: "密码",
reg_password_ph: "请设置8-20位密码",
reg_agree: "我已阅读并同意",
reg_agree_tos: "《用户服务协议》",
reg_agree_pp: "《隐私政策》",
reg_submit: "立即注册",
toast_tos: "服务协议即将上线",
toast_pp: "隐私政策即将上线",
toast_register_ok: "注册成功!欢迎加入虫虫出海"
},
en: {
reg_welcome: "Join Us",
reg_welcome_desc: "Register a Chongchong Global business account and start efficient cross-border logistics.",
reg_feat1: "Exclusive New-User Offers",
reg_feat2: "Free Logistics Plan",
reg_feat3: "Dedicated Account Manager",
reg_title: "Business Registration",
reg_have_account: "Already have an account?",
reg_login_link: "Log In Now",
reg_company_label: "Company Name",
reg_company_ph: "Enter company name",
reg_contact_label: "Contact Person",
reg_contact_ph: "Enter contact name",
reg_email_label: "Email",
reg_email_ph: "Enter company email",
reg_phone_label: "Phone Number",
reg_phone_ph: "Enter phone number",
reg_password_label: "Password",
reg_password_ph: "Set an 8-20 char password",
reg_agree: "I have read and agree to the",
reg_agree_tos: "User Agreement",
reg_agree_pp: "Privacy Policy",
reg_submit: "Sign Up Now",
toast_tos: "Terms of Service coming soon",
toast_pp: "Privacy Policy coming soon",
toast_register_ok: "Registration successful! Welcome to Chongchong Global"
},
es: {
reg_welcome: "Únete a nosotros",
reg_welcome_desc: "Regístrate en la cuenta empresarial de Chongchong Global y comienza una logística eficiente.",
reg_feat1: "Ofertas exclusivas para nuevos usuarios",
reg_feat2: "Plan logístico gratuito",
reg_feat3: "Gerente de cuenta dedicado",
reg_title: "Registro empresarial",
reg_have_account: "¿Ya tienes una cuenta?",
reg_login_link: "Inicia sesión ahora",
reg_company_label: "Nombre de empresa",
reg_company_ph: "Ingresa el nombre de empresa",
reg_contact_label: "Persona de contacto",
reg_contact_ph: "Ingresa el nombre de contacto",
reg_email_label: "Correo electrónico",
reg_email_ph: "Ingresa el correo empresarial",
reg_phone_label: "Número de teléfono",
reg_phone_ph: "Ingresa tu número de teléfono",
reg_password_label: "Contraseña",
reg_password_ph: "Define una contraseña de 8-20 caracteres",
reg_agree: "He leído y acepto los",
reg_agree_tos: "Términos de servicio",
reg_agree_pp: "Política de privacidad",
reg_submit: "Regístrate ahora",
toast_tos: "Términos de servicio próximamente",
toast_pp: "Política de privacidad próximamente",
toast_register_ok: "¡Registro exitoso! Bienvenido a Chongchong Global"
}
};
function applyLanguage(lang) {
if (!i18n[lang]) lang = 'zh';
currentLang = lang;
const langAttr = lang === 'zh' ? 'zh-CN' : (lang === 'es' ? 'es-MX' : 'en');
document.documentElement.setAttribute('lang', langAttr);
document.querySelectorAll('[data-i18n]').forEach(el => {
const k = el.getAttribute('data-i18n');
const v = i18n[lang][k];
if (v !== undefined) { if (v.indexOf('<br>') !== -1) el.innerHTML = v; else el.textContent = v; }
});
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
const k = el.getAttribute('data-i18n-ph');
if (i18n[lang][k] !== undefined) el.setAttribute('placeholder', i18n[lang][k]);
});
document.querySelectorAll('[data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
try { localStorage.setItem('cc_lang', lang); } catch (e) {}
}
document.querySelectorAll('#langSwitcher [data-lang]').forEach(b => {
b.addEventListener('click', () => applyLanguage(b.getAttribute('data-lang')));
});
function showToast(message) {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
toast.classList.remove('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
toast.classList.add('opacity-0', 'pointer-events-none', '-translate-y-4');
toast.classList.remove('opacity-100', 'translate-y-0');
}, 3000);
}
function updateAuthUI() {}
document.querySelectorAll('.toggle-password').forEach(button => {
button.addEventListener('click', function() {
const input = this.previousElementSibling;
const icon = this.querySelector('i');
if (input.type === 'password') { input.type = 'text'; icon.classList.remove('fa-eye'); icon.classList.add('fa-eye-slash'); }
else { input.type = 'password'; icon.classList.remove('fa-eye-slash'); icon.classList.add('fa-eye'); }
});
});
document.getElementById('registerForm').addEventListener('submit', function(e) {
e.preventDefault();
showToast(t('toast_register_ok'));
setTimeout(() => window.location.href = 'workbench.html', 900);
});
applyLanguage(currentLang);
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 KiB

-49
View File
@@ -1,49 +0,0 @@
const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = 8081;
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon'
};
const server = http.createServer((req, res) => {
console.log(`${req.method} ${req.url}`);
let filePath = '.' + req.url;
if (filePath === './') {
filePath = './index.html';
}
const extname = String(path.extname(filePath)).toLowerCase();
const contentType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, (error, content) => {
if (error) {
if (error.code === 'ENOENT') {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('<h1>404 Not Found</h1>', 'utf-8');
} else {
res.writeHead(500);
res.end('Server Error: ' + error.code, 'utf-8');
}
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
}
});
});
server.listen(PORT, () => {
console.log(`[TraePreviewUrl]: http://localhost:${PORT}`);
console.log(`Server running at http://localhost:${PORT}/`);
});
@@ -1,290 +0,0 @@
#!/usr/bin/env python3
"""
柏悦WMS - 分阶段脚本
Phase 1: 登录并保存cookies (需要可见浏览器+手动验证码)
Phase 2: 加载cookies, headless模式抓取所有页面
用法:
python3 scrape_wms_final.py login # 阶段1: 登录保存cookie
python3 scrape_wms_final.py scrape # 阶段2: 用cookie抓取
"""
import asyncio, json, sys, os, time
from playwright.async_api import async_playwright
BASE = "http://39.101.149.173"
API_BASE = "https://adtest.bylogistic.cn"
USERNAME = "admin"
PASSWORD = "ndui#Emdfj23123"
COOKIE_FILE = "/tmp/wms_cookies.json"
OUTPUT_DIR = os.path.expanduser("~/Documents/柏悦")
os.makedirs(OUTPUT_DIR, exist_ok=True)
async def phase_login():
"""打开浏览器让用户登录,然后保存cookies"""
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False, args=["--start-maximized"])
context = await browser.new_context(viewport={"width": 1440, "height": 900})
page = await context.new_page()
print("=== 打开登录页 ===")
await page.goto(f"{BASE}/user/login?redirect=%2Fwms%2FByInstoreList", timeout=30000)
await page.wait_for_timeout(2000)
# 自动填充
inputs_text = page.locator("input[type='text']")
input_pwd = page.locator("input[type='password']")
await inputs_text.nth(0).fill(USERNAME)
await input_pwd.fill(PASSWORD)
print("✅ 账号密码已填写")
print("👉 请在浏览器中输入验证码并点击'确定'")
print("⏳ 等待登录成功...")
try:
await page.wait_for_function(
"() => !window.location.pathname.includes('login')",
timeout=300000 # 等5分钟
)
print(f"✅ 登录成功! URL: {page.url}")
except Exception as e:
print(f"❌ 等待超时: {e}")
try:
await browser.close()
except:
pass
return False
# 立即保存cookies(不等页面加载完)
try:
await page.wait_for_timeout(3000)
except:
pass
try:
cookies = await context.cookies()
with open(COOKIE_FILE, "w") as f:
json.dump(cookies, f, indent=2)
print(f"✅ Cookies已保存到 {COOKIE_FILE} ({len(cookies)} 个)")
except Exception as ce:
print(f"⚠️ Cookie保存出错: {ce}")
# 截图当前页
try:
await page.screenshot(path="/tmp/wms_logged_in.png", full_page=True)
print("📸 当前页面截图: /tmp/wms_logged_in.png")
except Exception as se:
print(f"⚠️ 截图出错: {se}")
# 保持浏览器打开
print("\n浏览器将保持10秒后关闭...")
try:
await asyncio.sleep(10)
except:
pass
try:
await browser.close()
except:
pass
return True
async def phase_scrape():
"""加载cookies并headless抓取所有页面"""
# 读取cookies
if not os.path.exists(COOKIE_FILE):
print(f"❌ Cookie文件不存在: {COOKIE_FILE}")
print("请先运行: python3 scrape_wms_final.py login")
return
with open(COOKIE_FILE) as f:
saved_cookies = json.load(f)
print(f"🍪 加载了 {len(saved_cookies)} 个cookies")
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context(viewport={"width": 1440, "height": 900})
await context.add_cookies(saved_cookies)
page = await context.new_page()
# 监听API
all_apis = []
page.on("response", lambda r: all_apis.append({
"url": r.url, "status": r.status, "method": r.request.method
}))
# 先访问入库单列表(默认首页)
print("\n=== 访问系统首页 ===")
await page.goto(f"{BASE}/wms/ByInstoreList", timeout=20000)
await page.wait_for_timeout(4000)
if "login" in page.url.lower():
print("❌ Cookies已失效,请重新运行 login 命令")
await browser.close()
return
print(f"✅ 已进入系统: {page.url}")
# ===== 抓取菜单结构 =====
print("\n=== 抓取菜单结构 ===")
menu_structure = {}
# 一级菜单
top_menus = page.locator(".ant-menu-submenu-title")
top_count = await top_menus.count()
for i in range(top_count):
try:
name = (await top_menus.nth(i).inner_text()).strip().split("\n")[0].strip()
if not name or len(name) > 20:
continue
# 展开菜单
await top_menus.nth(i).click()
await page.wait_for_timeout(600)
# 获取子菜单
sub_items = page.locator(".ant-menu-submenu-open .ant-menu-item")
subs = []
for j in range(await sub_items.count()):
try:
sn = (await sub_items.nth(j).inner_text()).strip().split("\n")[0]
if sn and len(sn) < 30:
subs.append(sn)
except:
pass
menu_structure[name] = subs
print(f" 📂 {name}: {subs}")
# 收起
await top_menus.nth(i).click()
await page.wait_for_timeout(300)
except Exception as e:
pass
# 所有要访问的页面路径
pages_to_visit = [
("首页", "/wms/Dashboard"),
("入库单列表", "/wms/ByInstoreList"),
("入库单测量", "/wms/ByInstoreList4Measured"),
("测量数据审核", "/wms/ByInstoreList4MeasuredAudit"),
("接收审核", "/wms/ByInstoreList4ReceiveAudit"),
("客户管理", "/wms/ByCustomerList"),
("出库单列表", "/wms/ByOutstoreList"),
("派送单列表", "/wms/ByDispatchList"),
("货柜管理", "/wms/ByContainerList"),
("库存管理", "/wms/ByInventoryList"),
("财务结算", "/wms/ByFinanceList"),
("托盘管理", "/wms/ByTrayList"),
("BOL管理", "/wms/ByBolList"),
("用户管理", "/system/UserList"),
("角色管理", "/system/RoleList"),
("菜单管理", "/system/MenuList"),
("字典管理", "/system/DictList"),
("参数配置", "/system/ConfigList"),
]
# ===== 逐页抓取 =====
print(f"\n=== 逐页抓取 (共{len(pages_to_visit)}页) ===\n")
pages_data = {}
for idx, (name, path) in enumerate(pages_to_visit, 1):
try:
url = BASE + path
print(f"[{idx}/{len(pages_to_visit)}] {name} -> {path}", end=" ... ")
await page.goto(url, timeout=15000)
await page.wait_for_timeout(2500)
if "login" in page.url.lower():
print("❌ 会话过期!")
break
entry = {
"title": await page.title(),
"url": page.url,
}
# 表格列头
headers = []
ths = page.locator(".ant-table-thead th, thead th, [class*='table'] th")
for i in range(min(await ths.count(), 25)):
h = (await ths.nth(i).inner_text()).strip()
if h and h not in headers:
headers.append(h)
# 搜索条件字段
fields = []
labels = page.locator(".ant-form-item-label label, .query-group label, label[for]")
for i in range(min(await labels.count(), 20)):
t = (await labels.nth(i).inner_text()).strip()
if t and t not in fields and len(t) < 30:
fields.append(t)
# 操作按钮
buttons = []
btns = page.locator(".ant-btn:not(.ant-pagination-item):not(.ant-pagination-options-quick-jumper):not([class*='link'])")
for i in range(min(await btns.count(), 15)):
b = (await btns.nth(i).inner_text()).strip()
if b and b not in buttons and len(b) < 20 and b not in ["刷 新","关 闭"]:
buttons.append(b)
# 统计卡片数字
stat_vals = []
stats = page.locator("[class*='statistic'] [class*='value'], [class*='number-card'] [class*='value']")
for i in range(min(await stats.count(), 10)):
s = (await stats.nth(i).inner_text()).strip()
if s:
stat_vals.append(s)
entry["table_headers"] = headers
entry["search_fields"] = fields
entry["buttons"] = buttons
entry["statistics"] = stat_vals
pages_data[name] = entry
print(f"✅ 表头:{len(headers)} 字段:{len(fields)} 按钮:{len(buttons)}")
# 截图每个页面
safe_name = name.replace("/", "_").replace("\\", "_")
await page.screenshot(path=f"/tmp/wms_page_{safe_name}.png")
except Exception as e:
print(f"❌ {e}")
pages_data[name] = {"error": str(e)}
# 保存完整数据
full_data = {
"system_name": "柏悦物流管理系统 (Baiyue WMS)",
"base_url": BASE,
"api_base": API_BASE,
"scrape_time": time.strftime("%Y-%m-%d %H:%M:%S"),
"menu_structure": menu_structure,
"pages": pages_data,
"api_calls": all_apis[-50:], # 最后50个API调用
}
output_file = os.path.join(OUTPUT_DIR, "wms_system_data.json")
with open(output_file, "w", encoding="utf-8") as f:
json.dump(full_data, f, indent=2, ensure_ascii=False)
print(f"\n{'='*60}")
print(f"✅ 全部完成!")
print(f" - 菜单: {len(menu_structure)} 个一级模块")
print(f" - 页面: {len(pages_data)} 个页面")
print(f" - 输出: {output_file}")
print(f"{'='*60}")
await browser.close()
if __name__ == "__main__":
cmd = sys.argv[1] if len(sys.argv) > 1 else "login"
if cmd == "login":
asyncio.run(phase_login())
elif cmd == "scrape":
asyncio.run(phase_scrape())
else:
print(f"Usage: python3 {sys.argv[0]} [login|scrape]")
@@ -1,207 +0,0 @@
#!/usr/bin/env python3
"""
从可见浏览器中提取token,然后用API方式抓取所有数据
"""
import asyncio, json, os, time
from playwright.async_api import async_playwright
BASE = "http://39.101.149.173"
USERNAME = "admin"
PASSWORD = "ndui#Emdfj23123"
TOKEN_FILE = "/tmp/wms_token.json"
OUTPUT_DIR = os.path.expanduser("~/Documents/柏悦")
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False, args=["--start-maximized"])
context = await browser.new_context(viewport={"width": 1440, "height": 900})
page = await context.new_page()
print("=== 打开登录页 ===")
await page.goto(f"{BASE}/user/login?redirect=%2Fwms%2FByInstoreList", timeout=30000)
await page.wait_for_timeout(2000)
# 填充
inputs = page.locator("input[type='text']")
pwd = page.locator("input[type='password']")
await inputs.nth(0).fill(USERNAME)
await pwd.fill(PASSWORD)
print("👉 请在浏览器中输入验证码并点击确定...")
try:
await page.wait_for_function(
"() => !window.location.pathname.includes('login')",
timeout=300000
)
print("✅ 登录成功!")
except:
print("❌ 超时")
await browser.close()
return
await page.wait_for_timeout(3000)
# ========== 抓取完整菜单结构 ==========
print("\n=== 抓取菜单 ===")
menu_structure = {}
page_list = [] # [(display_name, url_path)]
top_menus = page.locator(".ant-menu-submenu-title")
for i in range(await top_menus.count()):
try:
name = (await top_menus.nth(i).inner_text()).strip().split("\n")[0].strip()
if not name or len(name) > 20:
continue
await top_menus.nth(i).click()
await page.wait_for_timeout(500)
subs = []
sub_items = page.locator(".ant-menu-submenu-open .ant-menu-item")
for j in range(await sub_items.count()):
sn = (await sub_items.nth(j).inner_text()).strip().split("\n")[0]
if sn and len(sn) < 30:
subs.append(sn)
menu_structure[name] = subs
print(f" {name}: {subs}")
await top_menus.nth(i).click()
await page.wait_for_timeout(300)
except:
pass
# ========== 逐页截图抓取 ==========
print(f"\n=== 逐页抓取 ===")
# 构建所有要访问的页面
all_pages = [("首页", "/wms/Dashboard")]
for menu_name, subs in menu_structure.items():
for sub in subs:
# 推断URL路径
path_map = {
"入库单列表": "/wms/ByInstoreList",
"入库单测量": "/wms/ByInstoreList4Measured",
"测量数据审核": "/wms/ByInstoreList4MeasuredAudit",
"接收审核": "/wms/ByInstoreList4ReceiveAudit",
"客户管理": "/wms/ByCustomerList",
"出库单列表": "/wms/ByOutstoreList",
"派送单列表": "/wms/ByDispatchList",
"BOL管理": "/wms/ByBolList",
"货柜管理": "/wms/ByContainerList",
"库存管理": "/wms/ByInventoryList",
"财务结算": "/wms/ByFinanceList",
"托盘管理": "/wms/ByTrayList",
"用户管理": "/system/UserList",
"角色管理": "/system/RoleList",
"菜单管理": "/system/MenuList",
"字典管理": "/system/DictList",
"参数配置": "/system/ConfigList",
"通用配置": "/system/ConfigList",
"报表设计": "/wms/Dashboard", # 待确认
"消息中心": "/wms/Dashboard",
"常见案例": "/wms/Dashboard",
"在线开发": "/wms/Dashboard",
"系统监控": "/wms/Dashboard",
"统计报表": "/wms/Dashboard",
}
path = path_map.get(sub, "")
if path:
all_pages.append((f"{menu_name} > {sub}", path))
# 去重
seen = set()
unique_pages = []
for name, path in all_pages:
if path not in seen:
seen.add(path)
unique_pages.append((name, path))
pages_data = {}
success_count = 0
for idx, (name, path) in enumerate(unique_pages, 1):
try:
url = BASE + path
print(f"[{idx}/{len(unique_pages)}] {name}", end=" ... ")
await page.goto(url, timeout=15000)
await page.wait_for_timeout(2500)
if "login" in page.url.lower():
print("会话过期!")
break
# 抓表格列头
headers = []
th_selectors = [".ant-table-thead th", "thead th", "table th"]
for ths in th_selectors:
els = page.locator(ths)
for i in range(min(await els.count(), 30)):
try:
h = (await els.nth(i).inner_text()).strip()
if h and h not in headers:
headers.append(h)
except:
pass
# 搜索字段
fields = []
labels = page.locator("label, .ant-form-item-label")
for i in range(min(await labels.count(), 25)):
try:
t = (await labels.nth(i).inner_text()).strip()
if t and t not in fields and len(t) < 30:
fields.append(t)
except:
pass
# 操作按钮
buttons = []
btns = page.locator("button:not([class*='pagination']):not([class*='quick-jumper'])")
for i in range(min(await btns.count(), 20)):
try:
b = (await btns.nth(i).inner_text()).strip()
if b and b not in buttons and len(b) < 25:
buttons.append(b)
except:
pass
pages_data[name] = {
"url": url,
"title": await page.title(),
"table_headers": headers,
"search_fields": fields,
"buttons": buttons,
}
print(f"✅ headers:{len(headers)} fields:{len(fields)} btns:{len(buttons)}")
success_count += 1
except Exception as e:
print(f"❌ {str(e)[:80]}")
pages_data[name] = {"error": str(e)[:200]}
# 保存数据
output = {
"system_name": "柏悦物流管理系统 (Baiyue WMS)",
"scrape_time": time.strftime("%Y-%m-%d %H:%M:%S"),
"menu_structure": menu_structure,
"pages": pages_data,
"pages_count": success_count,
}
out_path = os.path.join(OUTPUT_DIR, "wms_full_data.json")
with open(out_path, "w", encoding="utf-8") as f:
json.dump(output, f, indent=2, ensure_ascii=False)
print(f"\n✅ 完成! {success_count}/{len(unique_pages)} 页面成功抓取")
print(f"输出: {out_path}")
print("\n浏览器10秒后关闭...")
await asyncio.sleep(10)
await browser.close()
asyncio.run(main())
@@ -1,322 +0,0 @@
{
"system_name": "柏悦物流管理系统 (Baiyue WMS)",
"scrape_time": "2026-06-01 12:06:00",
"menu_structure": {
"客户管理": [
"客户公司管理",
"用户资料",
"海外仓阶梯费用",
"客户账户流水"
],
"入库": [
"入库单列表",
"入库单测量",
"测量数据审核",
"接收审核"
],
"出库": [
"派送单列表",
"BOL",
"自提单"
],
"货柜管理": [
"货柜列表",
"装柜清单"
],
"库存": [
"海外仓库存查询",
"理货管理",
"报损单"
],
"财务结算": [
"客户应收应付",
"客户对账单",
"结算单位应付",
"结算单位对账单",
"洛杉矶仓储成本"
],
"托盘管理": [
"托盘列表",
"生成托盘"
],
"通用": [
"产品信息",
"费用项目",
"FBA编码地址",
"托盘管理",
"库位/货架管理",
"订单流转路径",
"快递公司",
"往来单位"
],
"常见案例": [
"自定义组件",
"对象存储"
],
"在线开发": [
"Online表单开发"
],
"报表设计": [
"积木报表设计",
"生产销售监控",
"智慧物流监控"
],
"消息中心": [
"模板管理",
"消息管理",
"我的消息"
],
"系统管理": [
"用户管理",
"角色管理",
"菜单管理",
"租户管理",
"部门管理",
"我的部门",
"职务管理",
"通讯录",
"数据字典",
"分类字典",
"系统通告",
"菜单管理2"
],
"系统监控": [
"在线用户",
"路由网关",
"定时任务",
"日志管理",
"数据日志",
"SQL监控",
"在线文档"
]
},
"pages": {
"首页": {
"url": "http://39.101.149.173/wms/Dashboard",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"入库 > 入库单列表": {
"url": "http://39.101.149.173/wms/ByInstoreList",
"title": "入库单列表 · 柏悦物流管理系统",
"table_headers": [
"仓库",
"交货仓",
"配载状态",
"是否扣货",
"扣货理由",
"集装箱号",
"柜号",
"公司",
"业务类型",
"提货方式",
"提货时间",
"提货联系人电话",
"提货地址",
"提货邮编",
"提货联系人邮箱",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"开船时间",
"到港时间",
"总箱数",
"总毛重(T.K.GW)"
],
"search_fields": [
"定位仓库",
"交货仓",
"入仓码",
"订单状态",
"费用审核状态"
],
"buttons": [
"查询",
"重置",
"国内仓下单",
"海外分仓下单",
"整托下单",
"高级查询",
"导出报表",
"导出入库清单总表",
"导出唛头",
"导出FBA流转单ZIP",
"下载"
]
},
"入库 > 入库单测量": {
"url": "http://39.101.149.173/wms/ByInstoreList4Measured",
"title": "入库单测量 · 柏悦物流管理系统",
"table_headers": [
"入仓码",
"仓库",
"订单状态",
"配载状态",
"测量数据审核状态",
"唛头文件",
"是否扣货",
"扣货理由",
"公司",
"业务类型",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"总箱数",
"总毛重(T.K.GW)",
"总体积(m³)",
"总材积重",
"总净重(T.K.NW)",
"目的仓类型",
"目的仓",
"货件编号",
"测量完成时间",
"操作"
],
"search_fields": [
"仓库",
"入仓码",
"公司",
"FBA货件编号"
],
"buttons": [
"查询",
"重置",
"下载"
]
},
"入库 > 测量数据审核": {
"url": "http://39.101.149.173/wms/ByInstoreList4MeasuredAudit",
"title": "测量数据审核 · 柏悦物流管理系统",
"table_headers": [
"入仓码",
"仓库",
"订单状态",
"配载状态",
"测量数据审核状态",
"唛头文件",
"是否扣货",
"扣货理由",
"公司",
"业务类型",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"总箱数",
"总毛重(T.K.GW)",
"总体积(m³)",
"总材积重",
"总净重(T.K.NW)",
"目的仓类型",
"目的仓",
"货件编号",
"测量完成时间",
"操作"
],
"search_fields": [
"仓库",
"入仓码",
"公司",
"FBA货件编号"
],
"buttons": [
"查询",
"重置",
"下载"
]
},
"入库 > 接收审核": {
"url": "http://39.101.149.173/wms/ByInstoreList4ReceiveAudit",
"title": "接收审核 · 柏悦物流管理系统",
"table_headers": [
"入仓码",
"仓库",
"订单状态",
"配载状态",
"测量数据审核状态",
"唛头文件",
"是否扣货",
"扣货理由",
"公司",
"业务类型",
"渠道",
"买单/退税",
"是否整柜",
"商检验",
"总箱数",
"总毛重(T.K.GW)",
"总体积(m³)",
"总材积重",
"总净重(T.K.NW)",
"目的仓类型",
"目的仓",
"货件编号",
"测量完成时间",
"操作"
],
"search_fields": [
"仓库",
"入仓码",
"公司",
"FBA货件编号"
],
"buttons": [
"查询",
"重置",
"下载"
]
},
"出库 > 派送单列表": {
"url": "http://39.101.149.173/wms/ByDispatchList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"通用 > 托盘管理": {
"url": "http://39.101.149.173/wms/ByTrayList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"系统管理 > 用户管理": {
"url": "http://39.101.149.173/system/UserList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"系统管理 > 角色管理": {
"url": "http://39.101.149.173/system/RoleList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
},
"系统管理 > 菜单管理": {
"url": "http://39.101.149.173/system/MenuList",
"title": "柏悦物流管理系统",
"table_headers": [],
"search_fields": [],
"buttons": [
"返回首页"
]
}
},
"pages_count": 10
}
@@ -1,393 +0,0 @@
{
"menu_items": [
{
"text": "首页",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "入库单列表",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "入库单测量",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "测量数据审核",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "接收审核",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "刷 新",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "关闭左侧",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "关闭右侧",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "关闭其它",
"selector": ".ant-menu-item",
"href": ""
},
{
"text": "客户管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "入库",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "出库",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "货柜管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "库存",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "财务结算",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "托盘管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "通用",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "常见案例",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "在线开发",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "报表设计",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "消息中心",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "系统管理",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "系统监控",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "统计报表",
"selector": ".ant-menu-submenu-title",
"href": ""
},
{
"text": "入库 入库单列表 入库单测量 测量数据审核 接收审核",
"selector": "li[role='menuitem']",
"href": ""
},
{
"text": "定位仓库 请选择仓库",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "定位仓库",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择仓库",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "交货仓 请选择交货仓",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "交货仓",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择交货仓",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "入仓码",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "订单状态 请选择订单状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "订单状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择订单状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "费用审核状态 请选择费用审核状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "费用审核状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "请选择费用审核状态",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "1",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "2",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "3",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "4",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "5",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "•••",
"selector": "[class*='menu'] [class*='item']",
"href": ""
},
{
"text": "36",
"selector": "[class*='menu'] [class*='item']",
"href": ""
}
],
"all_links": [
"/system/ConfigList",
"/system/DictList",
"/system/MenuList",
"/system/RoleList",
"/system/UserList",
"/wms/ByBolList",
"/wms/ByContainerList",
"/wms/ByCustomerList",
"/wms/ByDispatchList",
"/wms/ByFinanceList",
"/wms/ByInstoreList",
"/wms/ByInstoreList4Measured",
"/wms/ByInstoreList4MeasuredAudit",
"/wms/ByInstoreList4ReceiveAudit",
"/wms/ByInventoryList",
"/wms/ByOutstoreList",
"/wms/ByTrayList",
"/wms/Dashboard",
"/wms/home",
"/wms/index"
],
"api_calls": [
{
"url": "http://39.101.149.173/user/login?redirect=%2Fwms%2FByInstoreList",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285671792?_t=1780285671",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285691766?_t=1780285691",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285694400?_t=1780285694",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285703728?_t=1780285703",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285711155?_t=1780285711",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "http://39.101.149.173/user/login?redirect=%2Fwms%2FByInstoreList",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285725968?_t=1780285725",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/randomImage/1780285750913?_t=1780285750",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/login",
"status": 200,
"method": "POST"
},
{
"url": "https://adtest.bylogistic.cn/sys/permission/getUserPermissionByToken?_t=1780285758",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_company,company_name,id",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_order_status,name,value,value%20not%20in%20(1001%5E%5E1002)",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_customer,realname,id",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_store,store_name,store_no",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_order_status,name,value",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_store,store_name,store_no,parent_store_id!=''",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_order_path,path_name,id",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/dict/getDictItems/by_fba,fba_no,fba_no",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/annountCement/listByUser?_t=1780285758",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/permission/getUserPermissionByToken?_t=1780285762",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/loginfo?_t=1780285763",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/visitInfo?_t=1780285763",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/annountCement/listByUser?_t=1780285763",
"status": 200,
"method": "GET"
},
{
"url": "https://adtest.bylogistic.cn/sys/permission/getUserPermissionByToken?_t=1780285764",
"status": 200,
"method": "GET"
}
]
}
@@ -1,67 +0,0 @@
{
"/system/ConfigList": {
"title": "柏悦物流管理系统",
"url": "http://39.101.149.173/dashboard/analysis",
"headers": [],
"buttons": [],
"form_fields": [],
"content_preview": "欢迎进入 柏悦物流管理系统\n欢迎您,管理员 退出登录\n首页\nCopyright 2022 BaiYue Logisitcs"
},
"/system/DictList": {
"error": "Page.wait_for_timeout: Target page, context or browser has been closed"
},
"/system/MenuList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/system/RoleList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/system/UserList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByBolList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByContainerList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByCustomerList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByDispatchList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByFinanceList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList4Measured": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList4MeasuredAudit": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInstoreList4ReceiveAudit": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByInventoryList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByOutstoreList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/ByTrayList": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/Dashboard": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/home": {
"error": "Page.goto: Target page, context or browser has been closed"
},
"/wms/index": {
"error": "Page.goto: Target page, context or browser has been closed"
}
}
@@ -1,510 +0,0 @@
# 柏悦物流管理系统(Baiyue WMS)系统文档
> **版本**: 基于测试系统 2026-06-01 抓取分析
> **访问地址**: http://39.101.149.173
> **后端API**: https://adtest.bylogistic.cn
> **技术栈**: Ant Design Vue + Spring Boot + MySQL + Redis
> **测试账号**: admin / ndui#Emdfj23123
---
## 目录
1. [系统概述](#1-系统概述)
2. [技术架构](#2-技术架构)
3. [首页仪表盘](#3-首页仪表盘)
4. [客户管理模块](#4-客户管理模块)
5. [入库管理模块](#5-入库管理模块)
6. [出库管理模块](#6-出库管理模块)
7. [货柜管理模块](#7-货柜管理模块)
8. [库存管理模块](#8-库存管理模块)
9. [财务结算模块](#9-财务结算模块)
10. [托盘管理模块](#10-托盘管理模块)
11. [通用配置模块](#11-通用配置模块)
12. [常见案例模块](#12-常见案例模块)
13. [在线开发模块](#13-在线开发模块)
14. [报表设计模块](#14-报表设计模块)
15. [消息中心模块](#15-消息中心模块)
16. [系统管理模块](#16-系统管理模块)
17. [系统监控模块](#17-系统监控模块)
18. [API接口清单](#18-api接口清单)
---
## 1. 系统概述
柏悦物流管理系统(Baiyue WMS)是一套面向跨境物流仓储行业的综合管理系统,覆盖从**客户管理 → 入库 → 出库 → 货柜管理 → 库存 → 托盘 → 财务结算**的全链路业务流程。
### 1.1 核心业务流程
```
客户下单 → 入库单 → 入库测量 → 测量审核 → 接收审核 → 库存管理
↓
出库单 → BOL/派送 → 托盘管理 → 货柜管理 → 财务结算
```
### 1.2 系统模块总览
| 序号 | 模块 | 子页面数 | 说明 |
|------|------|----------|------|
| 1 | 首页 | 1 | 数据看板/仪表盘 |
| 2 | 客户管理 | 4 | 客户公司、用户、阶梯费用、流水 |
| 3 | 入库 | 4 | 入库单、测量、审核、接收 |
| 4 | 出库 | 3 | 派送单、BOL、自提单 |
| 5 | 货柜管理 | 2 | 货柜列表、装柜清单 |
| 6 | 库存 | 3 | 海外仓库存查询、理货、报损 |
| 7 | 财务结算 | 5 | 应收应付、对账单、仓储成本 |
| 8 | 托盘管理 | 2 | 托盘列表、生成托盘 |
| 9 | 通用 | 8 | 产品、费用、FBA地址、库位等 |
| 10 | 常见案例 | 2 | 自定义组件、对象存储 |
| 11 | 在线开发 | 1 | Online表单开发 |
| 12 | 报表设计 | 3 | 积木报表、生产监控、物流监控 |
| 13 | 消息中心 | 3 | 模板、消息管理、我的消息 |
| 14 | 系统管理 | 12 | 用户、角色、菜单、租户等 |
| 15 | 系统监控 | 7 | 在线用户、日志、定时任务等 |
| 16 | 统计报表 | - | 报表设计入口 |
---
## 2. 技术架构
### 2.1 前端技术
- **框架**: Vue.js(SPA 单页应用)
- **UI 组件库**: Ant Design Vue
- **路由**: Vue Router(Hash 模式)
- **状态管理**: Vuex
- **图表**: 集成数据可视化组件
### 2.2 后端技术
- **框架**: Spring Boot(推测,基于 Spring 生态)
- **数据库**: MySQL
- **缓存**: Redis
- **认证**: Token-based(JWT)
- **API 风格**: RESTful
### 2.3 认证流程
```
用户登录 → POST /sys/login → 返回 Token
后续请求 → Header: Authorization: Bearer {token}
权限验证 → GET /sys/permission/getUserPermissionByToken
```
---
## 3. 首页仪表盘
- **URL**: `/wms/Dashboard`
- **页面标题**: 柏悦物流管理系统
首页提供系统总览数据看板,展示关键业务指标(订单数、库存状态、财务概况等)。
---
## 4. 客户管理模块
### 4.1 客户公司管理
管理客户公司的基本信息,包括公司名称、联系方式、合作状态等。
### 4.2 用户资料
管理客户下的用户账号资料,包括登录账号、角色权限等。
### 4.3 海外仓阶梯费用
配置客户在海外仓的阶梯式费用规则,按不同量级设置不同单价。
### 4.4 客户账户流水
查看客户账户的资金流水明细,包括充值、消费、退款等记录。
---
## 5. 入库管理模块
入库是系统的核心业务流程模块,包含四个阶段:入库单创建 → 测量 → 测量审核 → 接收审核。
### 5.1 入库单列表
- **URL**: `/wms/ByInstoreList`
- **页面标题**: 入库单列表 · 柏悦物流管理系统
#### 搜索条件
| 字段 | 说明 |
|------|------|
| 定位仓库 | 筛选指定仓库的入库单 |
| 交货仓 | 筛选指定交货仓的入库单 |
| 入仓码 | 按入仓码精确查询 |
| 订单状态 | 按订单状态筛选(如:待处理、处理中、已完成) |
| 费用审核状态 | 按费用审核状态筛选 |
#### 表格列头(23列)
| 列名 | 说明 |
|------|------|
| 仓库 | 入库目标仓库 |
| 交货仓 | 货物来源交货仓 |
| 配载状态 | 货物配载状态 |
| 是否扣货 | 是否被扣货标记 |
| 扣货理由 | 扣货原因说明 |
| 集装箱号 | 集装箱编号 |
| 柜号 | 货柜编号 |
| 公司 | 客户公司名称 |
| 业务类型 | 业务分类 |
| 提货方式 | 提货方式(自提/送货等) |
| 提货时间 | 预约提货时间 |
| 提货联系人电话 | 提货联系人电话 |
| 提货地址 | 提货地址详情 |
| 提货邮编 | 提货地邮编 |
| 提货联系人邮箱 | 提货联系人邮箱 |
| 渠道 | 业务渠道来源 |
| 买单/退税 | 贸易方式(买单/退税) |
| 是否整柜 | 整柜/拼柜标记 |
| 商检验 | 商检检验标记 |
| 开船时间 | 预计开船时间 |
| 到港时间 | 预计到港时间 |
| 总箱数 | 货物总箱数 |
| 总毛重(T.K.GW) | 总毛重(吨/公斤) |
#### 操作按钮
| 按钮 | 功能 |
|------|------|
| 查询 | 根据搜索条件查询数据 |
| 重置 | 清空搜索条件 |
| 国内仓下单 | 创建国内仓入库单 |
| 海外分仓下单 | 创建海外分仓入库单 |
| 整托下单 | 以整托方式创建入库单 |
| 高级查询 | 打开高级筛选面板 |
| 导出报表 | 导出当前查询结果为Excel |
| 导出入库清单总表 | 导出完整的入库清单 |
| 导出唛头 | 导出唛头标签文件 |
| 导出FBA流转单ZIP | 导出FBA流转单压缩包 |
| 下载 | 下载选中记录的相关文件 |
### 5.2 入库单测量
- **URL**: `/wms/ByInstoreList4Measured`
- **页面标题**: 入库单测量 · 柏悦物流管理系统
对入库货物进行实际测量,记录测量数据。
#### 搜索条件
| 字段 | 说明 |
|------|------|
| 仓库 | 筛选仓库 |
| 入仓码 | 按入仓码查询 |
| 公司 | 按客户公司筛选 |
| FBA货件编号 | 按FBA货件编号查询 |
#### 表格列头(24列)
| 列名 | 说明 |
|------|------|
| 入仓码 | 入仓编码 |
| 仓库 | 目标仓库 |
| 订单状态 | 订单处理状态 |
| 配载状态 | 配载情况 |
| 测量数据审核状态 | 测量审核进度 |
| 唛头文件 | 唛头标签文件 |
| 是否扣货 | 扣货标记 |
| 扣货理由 | 扣货原因 |
| 公司 | 客户公司 |
| 业务类型 | 业务分类 |
| 渠道 | 业务渠道 |
| 买单/退税 | 贸易方式 |
| 是否整柜 | 整柜/拼柜 |
| 商检验 | 商检状态 |
| 总箱数 | 货物总箱数 |
| 总毛重(T.K.GW) | 总毛重 |
| 总体积(m³) | 货物总体积 |
| 总材积重 | 材积重量 |
| 总净重(T.K.NW) | 总净重 |
| 目的仓类型 | 目的仓库类型 |
| 目的仓 | 目的仓库 |
| 货件编号 | 货件编号 |
| 测量完成时间 | 测量完成时间 |
| 操作 | 操作按钮列 |
#### 操作按钮
| 按钮 | 功能 |
|------|------|
| 查询 | 根据条件查询 |
| 重置 | 清空条件 |
| 下载 | 下载数据 |
### 5.3 测量数据审核
- **URL**: `/wms/ByInstoreList4MeasuredAudit`
- **页面标题**: 测量数据审核 · 柏悦物流管理系统
对测量数据进行审核确认。表头和搜索条件与入库单测量一致。
### 5.4 接收审核
- **URL**: `/wms/ByInstoreList4ReceiveAudit`
- **页面标题**: 接收审核 · 柏悦物流管理系统
最终接收审核环节,确认货物入库完成。表头和搜索条件与入库单测量一致。
---
## 6. 出库管理模块
### 6.1 派送单列表
- **URL**: `/wms/ByDispatchList`
管理出库派送订单,包括派送地址、物流方式、派送状态等。
### 6.2 BOL(提单管理)
- **URL**: `/wms/ByBolList`
管理 Bill of Lading(提单),是国际物流中的重要单证。包括提单号、船名航次、发货人、收货人等。
### 6.3 自提单
管理客户自提订单,记录自提预约、提货人信息等。
---
## 7. 货柜管理模块
### 7.1 货柜列表
- **URL**: `/wms/ByContainerList`
管理所有货柜信息,包括柜号、类型、尺寸、状态、当前位置等。
### 7.2 装柜清单
记录每个货柜的装柜明细,展示柜内货物列表和装载方案。
---
## 8. 库存管理模块
### 8.1 海外仓库存查询
- **URL**: `/wms/ByInventoryList`
查询海外仓当前库存情况,支持按仓库、产品、SKU等维度查询。
### 8.2 理货管理
对库存货物进行盘点、整理、移位等操作。
### 8.3 报损单
管理货物损耗/损坏的报损申请和审批流程。
---
## 9. 财务结算模块
### 9.1 客户应收应付
- **URL**: `/wms/ByFinanceList`
管理每个客户的应收账款和应付账款明细。
### 9.2 客户对账单
生成和查看客户对账单,用于与客户进行账务核对。
### 9.3 结算单位应付
按结算单位(如承运商、仓库方)管理应付账款。
### 9.4 结算单位对账单
生成结算单位的对账单。
### 9.5 洛杉矶仓储成本
专门针对洛杉矶仓库的仓储成本核算和分析。
---
## 10. 托盘管理模块
### 10.1 托盘列表
- **URL**: `/wms/ByTrayList`
管理所有托盘信息,包括托盘编号、规格、状态、关联货物等。
### 10.2 生成托盘
根据入库货物自动/手动生成托盘,分配货物到托盘。
---
## 11. 通用配置模块
系统基础数据和配置管理。
| 子模块 | 说明 |
|--------|------|
| 产品信息 | 管理产品/货品基础信息(名称、规格、HS编码等) |
| 费用项目 | 定义各类费用项目(仓储费、操作费、运输费等) |
| FBA编码地址 | 管理Amazon FBA仓库编码和地址信息 |
| 托盘管理 | 托盘基础配置 |
| 库位/货架管理 | 仓库库位和货架的编号和配置 |
| 订单流转路径 | 定义订单在不同节点之间的流转规则 |
| 快递公司 | 合作的快递/物流公司信息 |
| 往来单位 | 管理供应商、承运商等往来单位信息 |
---
## 12. 常见案例模块
### 12.1 自定义组件
展示系统自定义组件的使用示例和开发指南。
### 12.2 对象存储
文件/对象存储功能的配置和使用说明。
---
## 13. 在线开发模块
### 13.1 Online表单开发
低代码表单开发工具,允许管理员在线设计和配置业务表单,无需编写代码。
---
## 14. 报表设计模块
### 14.1 积木报表设计
可视化报表设计器,支持拖拽式设计各类业务报表。
### 14.2 生产销售监控
展示生产和销售的监控数据看板。
### 14.3 智慧物流监控
物流全链路监控大屏,展示实时物流数据。
---
## 15. 消息中心模块
### 15.1 模板管理
管理系统通知的消息模板,支持短信、邮件、站内信等类型。
### 15.2 消息管理
查看和管理所有已发送的系统消息。
### 15.3 我的消息
当前登录用户收到的个人消息列表。
---
## 16. 系统管理模块
系统后台管理功能。
| 子模块 | 说明 |
|--------|------|
| 用户管理 | 管理系统用户账号(创建、编辑、禁用、重置密码) |
| 角色管理 | 定义角色并分配权限 |
| 菜单管理 | 配置系统导航菜单结构 |
| 租户管理 | 多租户隔离管理(适用于SaaS模式) |
| 部门管理 | 组织架构部门管理 |
| 我的部门 | 当前用户所属部门信息 |
| 职务管理 | 定义系统内职务 |
| 通讯录 | 系统用户通讯录 |
| 数据字典 | 系统枚举值和字典数据维护 |
| 分类字典 | 分类树形字典维护 |
| 系统通告 | 发布系统级公告通知 |
| 菜单管理2 | 菜单管理的增强版/备用入口 |
---
## 17. 系统监控模块
系统运行状态监控和运维管理。
| 子模块 | 说明 |
|--------|------|
| 在线用户 | 查看当前在线用户列表,支持强制下线 |
| 路由网关 | 查看系统路由配置和网关状态 |
| 定时任务 | 管理系统的定时任务(Cron Job) |
| 日志管理 | 查看系统操作日志 |
| 数据日志 | 数据变更日志记录 |
| SQL监控 | SQL执行监控(基于Druid连接池) |
| 在线文档 | 系统API文档(Swagger/Knife4j) |
---
## 18. API接口清单
系统后端API部署在 `https://adtest.bylogistic.cn`,以下为已发现的API端点:
### 认证相关
| 接口 | 方法 | 说明 |
|------|------|------|
| `/sys/login` | POST | 用户登录 |
| `/sys/randomImage/{timestamp}` | GET | 获取验证码图片 |
| `/sys/permission/getUserPermissionByToken` | GET | 获取用户权限 |
### 数据字典
| 接口 | 方法 | 说明 |
|------|------|------|
| `/sys/dict/getDictItems/by_company` | GET | 获取公司字典 |
| `/sys/dict/getDictItems/by_order_status` | GET | 获取订单状态字典 |
| `/sys/dict/getDictItems/by_customer` | GET | 获取客户字典 |
| `/sys/dict/getDictItems/by_store` | GET | 获取仓库字典 |
| `/sys/dict/getDictItems/by_order_path` | GET | 获取订单路径字典 |
| `/sys/dict/getDictItems/by_fba` | GET | 获取FBA编码字典 |
### 系统功能
| 接口 | 方法 | 说明 |
|------|------|------|
| `/sys/annountCement/listByUser` | GET | 获取用户公告列表 |
| `/sys/loginfo` | GET | 系统日志信息 |
| `/sys/visitInfo` | GET | 访问统计信息 |
---
## 附录:SKU明细区域列头
在入库单详情页面,SKU明细表格包含以下列:
| 列名 | 说明 |
|------|------|
| 操作 | 编辑/删除操作 |
| # | 序号 |
| 报关HS_CODE | 报关用HS海关编码 |
| 货品图片 | 货品图片预览 |
| 仓/储/微(快递)码/纱 | 仓储编码/微信码/快递码 |
| 中品名 | 中文品名 |
| 英品名 | 英文品名 |
| 清关HS_CODE | 清关用HS海关编码 |
| 箱规长(cm) | 外箱长度 |
| 箱规宽(cm) | 外箱宽度 |
---
> **文档生成时间**: 2026-06-01
> **数据来源**: 测试系统 http://39.101.149.173 实际登录抓取
> **说明**: 部分页面因SPA路由限制未能获取完整表头信息,已根据菜单结构推断标注。如需补充具体页面详情,请登录系统确认后更新。
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,52 @@
# 海外仓货物废弃 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Extend the client workbench with a complete cargo-disposal workflow using one inbound order, detail-level disposal quantities, fixed disposal fees, and four generated relations.
**Architecture:** Keep the existing standalone `workbench.html` architecture. Add one namespaced navigation/page trio and a localStorage-backed store for disposal orders and inbound seed data. Render the create page as expandable inbound-order cards and render list/detail views from normalized disposal order objects.
**Tech Stack:** Standalone HTML/CSS, native JavaScript, browser localStorage, Node built-in test runner, Playwright/browser screenshots.
---
### Task 1: Add failing regression coverage
**Files:** Create `tests/verify-cargo-disposal-prototype.mjs`.
- [ ] Assert overseas navigation order and `cargo-disposal-list` route.
- [ ] Assert list, create, detail page IDs and required fields.
- [ ] Assert expandable inbound card markup, disposal quantity inputs, fee rate text and relation labels.
- [ ] Assert namespaced persistence, validation, fee calculation and four-number generation functions.
- [ ] Run `node --test tests/verify-cargo-disposal-prototype.mjs` and confirm failure because the feature is absent.
### Task 2: Add pages and styles
**Files:** Modify `../官网头程下单/workbench.html`.
- [ ] Add the navigation entry after warehouse transfer.
- [ ] Add list filters/table/pagination, create form with inbound cards, and read-only detail page.
- [ ] Add scoped responsive styles for cards, rows, fee summary, relation grid and mobile layout.
### Task 3: Implement store and rendering
**Files:** Modify `../官网头程下单/workbench.html`.
- [ ] Add inbound seed records and cargo-disposal localStorage key.
- [ ] Implement safe load/save, HTML escaping, list filtering, pagination, card expansion and detail rendering.
- [ ] Keep malformed localStorage non-blocking and preserve seed records.
### Task 4: Implement validation and submission
**Files:** Modify `../官网头程下单/workbench.html`.
- [ ] Implement selection and disposal quantity collection.
- [ ] Validate one inbound order, positive disposal quantity and per-line availability.
- [ ] Calculate `disposalBoxes * 5`, generate disposal/tail/shipment/billing numbers, persist one normalized order and return to list.
### Task 5: Verify and polish
- [ ] Run the new test and the existing storage-transfer and architecture tests.
- [ ] Run `node C:/Users/BY/.codex/skills/impeccable/scripts/detect.mjs --json ../官网头程下单/workbench.html`.
- [ ] Start the existing local server, inspect desktop and mobile screenshots, and fix overflow/overlap issues.
- [ ] Run a browser flow for create, invalid quantity, successful submission, list filtering and detail relations.
@@ -0,0 +1,29 @@
# 海外仓货物废弃下单设计规格
## 1. 目标与范围
在现有客户端工作台原型的“海外仓”菜单下新增“货物废弃”入口,完成新增、查询、订单列表和只读详情。下单时关联一个入库单,并在该入库单的展开卡片中按货物明细填写废弃箱数。提交成功后一次性生成并展示客户端货物废弃单、后台尾程单、废弃出库单和弃货费计费记录。
本次仍是单文件原型实现,不调用真实接口,不改变后台原型数据,不上传文件。入库单候选复用客户端现有入库订单示例数据,并以本地存储保存客户端新增货物废弃单。
## 2. 页面与导航
“货物废弃”排列在“仓储转运单”之后、“退货入库”之前,属于海外仓单一入口。入口默认打开订单列表,列表页按钮进入新增页,列表查看操作进入详情页;新增和详情均提供返回列表操作。
## 3. 查询与列表
查询支持货物废弃单号、入库单号、操作仓、订单状态和创建时间范围。列表展示货物废弃单号、关联入库单、废弃箱数、操作仓、弃货费、尾程单号、废弃出库单号、计费单号、状态、创建时间和查看操作。示例数据覆盖部分废弃和全部废弃场景,新增订单置于列表首位,分页按每页 10 条实现。
## 4. 新增订单
新增页按方案 B 采用可展开入库单卡片。卡片显示入库单号、操作仓、入库时间和可用箱数;展开后显示该入库单的货物明细、SKU、品名、可用箱数和废弃箱数输入框。单次订单只能选择一个入库单,废弃箱数为整数且不得超过明细可用箱数,至少一条明细的废弃箱数大于 0。页面实时汇总废弃箱数与弃货费,固定费率为 5 USD/箱。
提交时依次校验已选择入库单、至少一条废弃明细、数量为合法整数且不超过可用库存。校验失败不生成单号并提示恢复动作。成功时一次性生成:客户端货物废弃单、后台尾程单、后台废弃出库单和后台弃货费计费记录;费用金额等于废弃箱数乘以 5,币种 USD。按钮在提交期间禁用,成功后 Toast 并返回列表。
## 5. 详情与本地数据
详情只读展示订单概览、关联入库单及货物明细、废弃数量、费用计算和四类关联单号。使用独立 localStorage 键保存订单,存储失败时继续展示示例数据并提示,不使页面空白。编号使用日期与序列组合,单次点击只创建一组关联记录。
## 6. 视觉与测试
复用现有工作台的卡片、表格、筛选、Toast、按钮和响应式样式;新增样式统一使用 `cargo-disposal` 前缀。桌面端展示入库单卡片和明细表,移动端改为单列并允许卡片内容自然换行,禁止页面级横向溢出。测试覆盖静态结构、数量校验、费用计算、四单关联、查询过滤、本地持久化以及 1440×1000 和 390×844 响应式无溢出。
@@ -0,0 +1,342 @@
from pathlib import Path
from openpyxl import Workbook
from openpyxl.formatting.rule import FormulaRule
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.worksheet.datavalidation import DataValidation
ROOT = Path(__file__).resolve().parents[1]
OUTPUT_PATH = ROOT / "柏悦物流客户端-需求管理表.xlsx"
HEADERS = [
"序号",
"版本",
"模块",
"需求范围",
"需求描述",
"状态",
"当前进展",
"需求确认时间",
"需求负责人",
"开始开发时间",
"开发负责人",
"开始测试时间",
"测试负责人",
"预计上线时间",
"备注",
]
FUNCTIONS = [
# 业务总览(10)
("业务总览", "快捷下单", "进入海外采购", "点击海外采购业务卡片进入海外采购下单页面。", "快捷入口,复用“出海下单-海外采购”业务页"),
("业务总览", "快捷下单", "进入美国本土卡", "点击美国本土卡业务卡片进入美国本土卡下单页面。", "快捷入口,复用“出海下单-美国本土卡”业务页"),
("业务总览", "快捷下单", "进入美本土散板", "点击美本土散板业务卡片进入美本土散板下单页面。", "快捷入口,复用“出海下单-美本土散板”业务页"),
("业务总览", "账户信息", "查看客户身份", "展示客户头像、账户名称和企业客户类型。", None),
("业务总览", "数据概览", "查看进行中订单", "展示当前进行中订单数量。", None),
("业务总览", "数据概览", "查看已完成订单", "展示当前已完成订单数量。", None),
("业务总览", "数据概览", "查看本月下单", "展示本月累计下单数量。", None),
("业务总览", "帮助与支持", "联系客服", "点击后显示联系客服提示信息。", None),
("业务总览", "帮助与支持", "查看常见问题", "点击后显示常见问题提示信息。", None),
("业务总览", "帮助与支持", "获取报价", "点击后显示获取报价提示信息。", None),
# 头程物流(42)- 头程下单(24)
("头程物流", "头程下单", "选择取货方式", "支持选择自送货-快递、我司提货和自送货-卡车三种取货方式,并切换对应信息区。", None),
("头程物流", "头程下单", "填写快递送货信息", "选择自送货-快递时,可填写预计送达时间和快递单号。", None),
("头程物流", "头程下单", "填写我司提货信息", "选择我司提货时,填写提货地址、提货时间、联系人、联系电话和提货备注。", None),
("头程物流", "头程下单", "填写卡车送货信息", "选择自送货-卡车时,填写预计到达时间、车牌号、司机姓名和司机电话。", None),
("头程物流", "头程下单", "选择收货仓", "支持选择义乌仓、深圳仓、青岛仓、上海仓、宁波仓或厦门仓。", None),
("头程物流", "头程下单", "选择运输渠道", "支持选择美线普货专线、美线敏感货专线、美线快线或美线FBA直送。", None),
("头程物流", "头程下单", "选择买单或退税", "支持在买单和退税两种报关处理方式中选择。", None),
("头程物流", "头程下单", "选择整柜或散货", "支持标记货物为整柜或散货。", None),
("头程物流", "头程下单", "确认是否商检", "支持选择货物是否需要商检。", None),
("头程物流", "头程下单", "设置船期", "支持填写开船时间和到港时间。", None),
("头程物流", "头程下单", "填写订单备注", "支持填写选填的订单备注信息。", None),
("头程物流", "头程下单", "选择目的仓类型", "支持在FBA仓、海外仓和私人仓之间切换目的地信息表单。", None),
("头程物流", "头程下单", "填写FBA仓目的地", "填写亚马逊仓代码、收件人、电话、地址、货件编号、跟踪编号和期望到达时间。", None),
("头程物流", "头程下单", "填写海外仓目的地", "选择海外仓并填写收件人、电话、国家、详细地址、邮编和期望到达时间。", None),
("头程物流", "头程下单", "填写私人仓目的地", "填写收件人、电话、国家、州省、城市、详细地址、邮编和期望到达时间。", None),
("头程物流", "头程下单", "查看最新报价", "在目的地信息区触发查看最新报价操作。", None),
("头程物流", "头程下单", "上传货物 Excel", "支持选择 XLSX 或 XLS 文件导入货物明细。", None),
("头程物流", "头程下单", "下载货物模板", "支持下载货物明细填写模板。", None),
("头程物流", "头程下单", "添加货物行", "在货物明细表中新增一行可编辑货物数据。", None),
("头程物流", "头程下单", "删除所选货物行", "勾选货物明细后批量删除所选行。", None),
("头程物流", "头程下单", "维护货物明细", "维护SKU、品名、图片、HS编码、箱规、数量、重量、体积、申报价值、材质、用途、型号、品牌、货件编号、跟踪编号、UPC和备注。", None),
("头程物流", "头程下单", "选择费用方案", "展示最快、推荐和经济三档时效与预估价格,并支持选择方案。", None),
("头程物流", "头程下单", "重置订单表单", "清空或恢复头程订单表单的当前填写内容。", None),
("头程物流", "头程下单", "提交订单", "校验并提交头程运输订单;原型提示提交后不可修改货物明细。", None),
# 一键导入(13)
("头程物流", "一键导入", "选择取货方式", "支持选择自送货-快递、我司提货和自送货-卡车三种取货方式。", None),
("头程物流", "一键导入", "填写快递送货信息", "选择自送货-快递时填写预计送达时间和快递单号。", None),
("头程物流", "一键导入", "填写我司提货信息", "选择我司提货时填写地址、时间、联系人、联系电话和备注。", None),
("头程物流", "一键导入", "填写卡车送货信息", "选择自送货-卡车时填写到达时间、车牌号、司机姓名和电话。", None),
("头程物流", "一键导入", "选择收货仓与渠道", "选择国内收货仓和美线运输渠道。", None),
("头程物流", "一键导入", "设置贸易属性", "设置买单或退税、整柜或散货以及是否商检。", None),
("头程物流", "一键导入", "填写船期与备注", "填写开船时间、到港时间及选填订单备注。", None),
("头程物流", "一键导入", "上传 Excel", "支持选择 XLSX 或 XLS 文件批量导入FBA订单和货物数据。", None),
("头程物流", "一键导入", "下载模板", "下载一键导入使用的 Excel 模板。", None),
("头程物流", "一键导入", "增删数据行", "支持在批量明细表中添加行及删除勾选行。", None),
("头程物流", "一键导入", "维护批量明细", "维护目的仓、亚马逊仓代码、ShipmentID、ReferenceID、收件信息、产品信息、申报价值、件箱尺寸重量、带电液体属性、海关品名和备注。", None),
("头程物流", "一键导入", "保存草稿", "保存当前一键导入订单内容为草稿。", None),
("头程物流", "一键导入", "提交订单", "确认货物明细无误后提交批量FBA订单。", None),
# 快速下单(5)
("头程物流", "快速下单", "选择渠道", "为每行订单选择美线运输渠道。", None),
("头程物流", "快速下单", "选择目的仓", "为每行订单选择FBA、海外仓或私人仓。", None),
("头程物流", "快速下单", "管理订单行", "支持新增订单行和删除当前订单行;每行提交成功后生成一个订单。", None),
("头程物流", "快速下单", "管理产品", "支持在同一订单行添加多个产品,并填写产品SKU和箱数或删除产品。", None),
("头程物流", "快速下单", "提交订单", "提交快速订单数据。", None),
# 出海下单(18)
("出海下单", "海外采购", "填写商品需求", "填写必填采购品类,并可填写商品规格和商品链接。", None),
("出海下单", "海外采购", "填写数量与预算", "填写采购数量和选填的预算单价。", None),
("出海下单", "海外采购", "填写收货与联系信息", "填写收货地址、联系人和联系电话。", None),
("出海下单", "海外采购", "填写订单备注", "补充其他采购需求说明。", None),
("出海下单", "海外采购", "提交订单", "校验必填信息并提交海外采购订单。", None),
("出海下单", "海外采购", "取消并返回", "取消当前填写并返回业务选择页面。", None),
("出海下单", "美国本土卡", "选择运输类型", "支持选择港口提柜、仓到仓或仓到门。", None),
("出海下单", "美国本土卡", "填写运输路线", "填写起运地、目的地以及两端详细地址、码头或仓号。", None),
("出海下单", "美国本土卡", "填写货物信息", "选择柜型并填写件数、总重量和总体积。", None),
("出海下单", "美国本土卡", "填写联系人与要求时间", "填写联系人、电话或微信邮箱等联系方式和要求时间。", None),
("出海下单", "美国本土卡", "提交订单", "校验必填信息并提交美国本土卡订单。", None),
("出海下单", "美国本土卡", "取消并返回", "取消当前填写并返回业务选择页面。", None),
("出海下单", "美本土散板", "设置运输方案", "选择拼车或优先拼载、是否打盘以及港口提柜、仓到仓或仓到门运输类型。", None),
("出海下单", "美本土散板", "填写运输路线", "填写起运地、目的地以及两端详细地址、码头或仓号。", None),
("出海下单", "美本土散板", "填写货物信息", "选择柜型并填写件数、总重量和总体积。", None),
("出海下单", "美本土散板", "填写联系人与要求时间", "填写联系人、联系方式和要求时间。", None),
("出海下单", "美本土散板", "提交订单", "校验必填信息并提交美本土散板订单。", None),
("出海下单", "美本土散板", "取消并返回", "取消当前填写并返回业务选择页面。", None),
# 海外仓(36)- 贴标申请(6)
("海外仓", "贴标申请", "填写订单信息", "填写客户单号、目的海外仓、贴标截止时间、联系人、联系电话和备注。", None),
("海外仓", "贴标申请", "上传面单贴标文件", "支持点击或拖拽上传多张 PNG、JPG 或 JPEG 图片。", None),
("海外仓", "贴标申请", "增删货物行", "支持添加货物明细行、清空货物行及勾选行。", None),
("海外仓", "贴标申请", "维护货物明细", "维护商品SKU、图片、中文品名、箱数、移仓箱数、货件编号、追踪编码、换标新SKU、贴标要求和换标文件。", None),
("海外仓", "贴标申请", "重置申请", "重置当前贴标申请内容。", None),
("海外仓", "贴标申请", "提交申请", "确认信息后提交贴标申请。", None),
# 仓储转运单(12)
("海外仓", "仓储转运单", "打开新增订单页", "打开仓储转运单新增订单页面。", "客户端入口;新增页与订单列表同属“仓储转运单”入口"),
("海外仓", "仓储转运单", "填写基础与派送信息", "填写进仓编号、操作仓、FBA目的仓代码、详细派送地址、发货箱数、派送方式、期望到达时间、FBA货件编号、FBA追踪编号、FBA BOX编号和订单备注。", "字段参考海外仓转亚马逊仓贴标发货计划"),
("海外仓", "仓储转运单", "维护货物明细", "支持添加和删除货物明细,维护商品SKU、中文品名和箱数,并校验发货箱数等于明细箱数之和。", None),
("海外仓", "仓储转运单", "选择是否换标", "支持订单级选择是否换标;选择否时不生成换标单,选择是时展示换标字段。", None),
("海外仓", "仓储转运单", "维护换标信息", "选择换标时按货物明细填写换标后SKU、贴标要求并上传换标文件。", "换标字段作为后台“海外仓-换标单”数据来源"),
("海外仓", "仓储转运单", "提交并生成后台尾程订单", "校验通过后创建客户端仓储转运单,并将成功订单写入后台0805版本“海外仓-尾程订单”。", "客户端转运单与后台尾程订单一一关联"),
("海外仓", "仓储转运单", "条件生成后台换标单", "选择换标时同时创建后台0805版本“海外仓-换标单”,不换标时不创建换标单。", "与客户端仓储转运单及尾程订单一一关联"),
("海外仓", "仓储转运单", "设置查询条件", "支持按转运单号、进仓编号、操作仓、FBA目的仓代码、是否换标、订单状态和创建时间范围筛选。", None),
("海外仓", "仓储转运单", "查询与重置", "按当前条件查询仓储转运单,支持重置条件并回到第一页。", None),
("海外仓", "仓储转运单", "查看订单列表", "展示转运单号、进仓编号、箱数、操作仓、FBA目的仓、货件编号、派送方式、换标标记、尾程订单号、换标单号、状态和创建时间。", None),
("海外仓", "仓储转运单", "查看订单详情及关联单号", "查看基础信息、货物明细、换标信息以及后台尾程订单号和换标单号。", "只读详情页"),
("海外仓", "仓储转运单", "分页浏览", "支持分页浏览仓储转运单列表。", None),
# 货物废弃(12)
("海外仓", "货物废弃", "打开新增订单页", "打开货物废弃新增订单页面,按入库单查看可废弃库存。", "客户端入口;新增页与订单列表同属“货物废弃”入口"),
("海外仓", "货物废弃", "选择关联入库单", "单次货物废弃单选择一个入库单,并展开查看该入库单的货物明细。", "采用可展开入库单卡片方案"),
("海外仓", "货物废弃", "填写明细废弃箱数", "按入库明细填写废弃箱数,支持部分废弃;数量必须为整数且不得超过可用箱数。", None),
("海外仓", "货物废弃", "实时计算弃货费", "按废弃箱数乘以5 USD/箱实时计算弃货费。", "固定单价:5 USD/箱"),
("海外仓", "货物废弃", "提交并生成尾程单", "提交成功后生成一张后台尾程单,并与客户端货物废弃单关联。", "一一关联"),
("海外仓", "货物废弃", "生成废弃出库单", "提交成功后生成一张出库类型为“废弃”的后台出库单,并关联原入库单及废弃明细。", "一一关联"),
("海外仓", "货物废弃", "生成弃货费计费单", "提交成功后生成弃货费计费记录,金额等于废弃箱数乘以5 USD。", "币种 USD"),
("海外仓", "货物废弃", "设置查询条件", "支持按货物废弃单号、入库单号、操作仓、订单状态和创建时间范围筛选。", None),
("海外仓", "货物废弃", "查询与重置", "按当前条件查询货物废弃订单,支持重置条件并返回第一页。", None),
("海外仓", "货物废弃", "查看订单列表", "展示货物废弃单号、入库单、废弃箱数、操作仓、弃货费、尾程单、废弃出库单、计费单、状态和创建时间。", None),
("海外仓", "货物废弃", "查看订单详情及关联单据", "查看关联入库单、废弃货物明细、费用计算以及货物废弃单、尾程单、废弃出库单和计费单号。", "只读详情页"),
("海外仓", "货物废弃", "分页浏览", "支持分页浏览货物废弃订单列表。", None),
# 退货换标(12)
("海外仓", "退货换标", "打开新增订单页", "打开退货换标新增订单页面。", "整合退货入库、退货换标和退货出库"),
("海外仓", "退货换标", "填写退货信息", "填写来源FBA仓、移除订单Order ID、承运商、跟踪号、退货原因和退货备注。", None),
("海外仓", "退货换标", "维护多SKU货物明细", "支持在一张退货单中添加多个SKU,维护中文品名、FNSKU和退件数量。", None),
("海外仓", "退货换标", "选择是否换标", "下单时按整张退货单选择是否换标;不换标只生成尾程订单。", None),
("海外仓", "退货换标", "选择贴标要求", "换标时可选Case1只换箱唛、Case2只换SKU标或Case3同时更换。", None),
("海外仓", "退货换标", "上传换标文件", "按Case要求上传箱唛和SKU标签文件。", None),
("海外仓", "退货换标", "填写尾程信息", "填写目的FBA仓、派送地址、目的仓代码、期望到达时间和尾程备注。", None),
("海外仓", "退货换标", "条件生成后台换标单", "勾选换标时生成后台海外仓换标单并关联换标状态。", "后台海外仓-换标单"),
("海外仓", "退货换标", "生成后台尾程订单", "提交成功后始终生成后台海外仓尾程订单并关联尾程状态。", "后台海外仓-尾程订单"),
("海外仓", "退货换标", "设置查询条件", "支持按单号、来源仓、目的仓、商品SKU、是否换标、换标状态、尾程状态和创建时间筛选。", None),
("海外仓", "退货换标", "查看订单列表", "展示退货换标单号、仓库、SKU汇总、退件总数、换标与尾程状态及关联单号。", None),
("海外仓", "退货换标", "查看订单详情", "查看退货信息、多SKU明细、换标文件、尾程信息、备注及后台关联单号。", "只读详情页"),
# 订单管理(13)
("订单管理", "入库订单列表", "筛选订单", "支持按订单号、收货仓、渠道、客户、创建时间、付款状态、出货状态、操作状态和目的仓类型筛选。", None),
("订单管理", "入库订单列表", "查询订单", "按当前筛选条件查询入库订单。", None),
("订单管理", "入库订单列表", "重置查询条件", "清空当前入库订单筛选条件。", None),
("订单管理", "入库订单列表", "重置所有状态", "触发入库订单所有状态重置操作。", None),
("订单管理", "入库订单列表", "下载唛头", "对勾选的入库订单下载唛头文件。", None),
("订单管理", "入库订单列表", "导出订单尾程信息", "导出勾选订单的尾程信息。", None),
("订单管理", "入库订单列表", "查看订单列表", "展示单号、件数、重量、渠道、目的仓、客户、货物统计、申报信息、跟踪编号、国际单号、操作人和备注。", None),
("订单管理", "入库订单列表", "分页浏览", "支持切换每页10、20、50或100条,并进行上一页和下一页翻页。", None),
("订单管理", "派送订单列表", "筛选订单", "支持按订单号、派送仓、渠道、客户、创建时间、付款状态、派送状态、操作状态和目的仓类型筛选。", None),
("订单管理", "派送订单列表", "查询订单", "按当前筛选条件查询派送订单。", None),
("订单管理", "派送订单列表", "重置查询", "清空当前派送订单筛选条件。", None),
("订单管理", "派送订单列表", "查看订单列表", "展示单号、件数、重量、渠道、目的仓、客户、派送状态、货物统计、申报信息、跟踪编号、国际单号、操作人和备注。", None),
("订单管理", "派送订单列表", "分页浏览", "支持切换每页10、20、50或100条,并进行上一页和下一页翻页。", None),
# 财务结算(5)
("财务结算", "应付账单", "查看账单列表", "展示状态、单据状态、账单编号、日期、币种、汇率、账期、应付金额、已付金额和备注等账单信息。", None),
("财务结算", "应付账单", "查看明细", "点击账单记录进入对应账单明细页面。", "下钻至“账单明细”页面"),
("财务结算", "应付账单", "分页浏览", "支持使用页码、上一页和下一页浏览账单列表。", None),
("财务结算", "账单明细", "查看费用明细", "按入仓单号查看SKU、渠道、FBA仓、箱数、重量、体积、费用类型、币种、金额及结算日期。", None),
("财务结算", "账单明细", "返回账单列表", "通过面包屑返回应付账单列表。", None),
# 公司信息(5)
("公司信息", "企业基本信息", "查看企业资料", "查看公司名称、统一信用代码、企业类型、注册地、成立日期、注册资本、法人姓名和电话。", None),
("公司信息", "联系信息", "查看企业联系人", "查看联系人、手机、邮箱、微信和地址。", None),
("公司信息", "资质文件", "查看文件列表", "查看营业执照、法人身份证、货代资质证书和银行开户许可证的验证状态与有效期。", None),
("公司信息", "资质文件", "查看文件", "打开资质文件弹窗,查看文件类型、验证状态、有效期、上传时间和预览。", None),
("公司信息", "资质文件", "下载文件", "从资质文件查看弹窗触发文件下载。", None),
# 我的信息(9)
("我的信息", "账户余额", "查看余额", "展示账户当前余额。", None),
("我的信息", "账户余额", "查看余额明细", "打开余额明细弹窗,展示当前余额、本月收入和本月支出。", "弹窗详情"),
("我的信息", "账户余额", "筛选余额明细", "支持今天、近7天、本月、近30天快捷筛选,也支持自定义开始和结束日期查询及重置。", None),
("我的信息", "账户余额", "查看余额流水", "展示交易时间、业务类型、关联单号、收支金额、交易后余额和状态。", None),
("我的信息", "账户余额", "充值", "打开充值弹窗进行账户充值。", "与顶部全局充值入口复用同一弹窗"),
("我的信息", "基本信息", "查看个人资料", "查看用户名、真实姓名、手机号、邮箱、微信和所属公司。", None),
("我的信息", "基本信息", "编辑个人资料", "进入编辑模式修改用户名、真实姓名、手机号、邮箱和微信;所属公司不可编辑。", None),
("我的信息", "基本信息", "保存或取消修改", "支持保存个人资料修改或取消编辑。", None),
("我的信息", "修改密码", "修改登录密码", "填写当前密码、新密码和确认新密码后提交修改。", None),
# 全局工具(6)
("全局工具", "多语言", "切换界面语言", "支持切换中文、英文和西班牙语界面。", None),
("全局工具", "充值", "打开充值弹窗", "点击顶部充值按钮打开账户充值弹窗。", "与“我的信息-账户余额”充值入口复用"),
("全局工具", "充值", "设置充值金额", "支持直接输入金额或选择50、100、200、500元预设金额。", None),
("全局工具", "充值", "选择支付并确认", "支持选择微信支付或支付宝,显示对应扫码提示并确认充值。", None),
("全局工具", "返回官网", "返回官网首页", "点击顶部返回官网链接跳转到 index.html。", None),
("全局工具", "退出登录", "退出当前账户", "点击侧边栏退出登录入口执行退出操作。", None),
]
def build_workbook() -> Workbook:
workbook = Workbook()
sheet = workbook.active
sheet.title = "需求管理表"
sheet.sheet_view.showGridLines = False
header_fill = PatternFill("solid", fgColor="4472C4")
header_font = Font(name="微软雅黑", size=11, bold=True, color="FFFFFF")
body_font = Font(name="微软雅黑", size=10, color="1F1F1F")
alternate_fill = PatternFill("solid", fgColor="EAF2F8")
module_change_fill = PatternFill("solid", fgColor="D9EAD3")
thin_gray = Side(style="thin", color="B7C9DC")
border = Border(left=thin_gray, right=thin_gray, top=thin_gray, bottom=thin_gray)
for column, header in enumerate(HEADERS, start=1):
cell = sheet.cell(1, column, header)
cell.fill = header_fill
cell.font = header_font
cell.border = border
cell.alignment = Alignment(horizontal="center", vertical="center")
previous_module = None
for sequence, (module, secondary, feature, description, remark) in enumerate(
FUNCTIONS, start=1
):
row = sequence + 1
version = "0805" if (module, secondary) == ("海外仓", "仓储转运单") else "0812" if (module, secondary) in (("海外仓", "货物废弃"), ("海外仓", "退货换标")) else None
values = [
sequence,
version,
module,
f"{secondary}:{feature}",
description,
None,
None,
None,
None,
None,
None,
None,
None,
None,
remark,
]
is_new_module = module != previous_module
for column, value in enumerate(values, start=1):
cell = sheet.cell(row, column, value)
cell.font = body_font
cell.border = border
cell.alignment = Alignment(
horizontal="left" if column in (4, 5, 7, 15) else "center",
vertical="center",
wrap_text=True,
)
if row % 2 == 0:
cell.fill = alternate_fill
if is_new_module and column == 3:
cell.fill = module_change_fill
cell.font = Font(name="微软雅黑", size=10, bold=True, color="274E13")
previous_module = module
sheet.freeze_panes = "A2"
sheet.auto_filter.ref = f"A1:O{len(FUNCTIONS) + 1}"
sheet.row_dimensions[1].height = 30
for row in range(2, len(FUNCTIONS) + 2):
sheet.row_dimensions[row].height = 52
widths = {
"A": 8,
"B": 10,
"C": 14,
"D": 34,
"E": 58,
"F": 12,
"G": 24,
"H": 16,
"I": 14,
"J": 16,
"K": 14,
"L": 16,
"M": 14,
"N": 16,
"O": 42,
}
for column, width in widths.items():
sheet.column_dimensions[column].width = width
for column in (8, 10, 12, 14):
for row in range(2, len(FUNCTIONS) + 2):
sheet.cell(row, column).number_format = "yyyy-mm-dd"
statuses = "待确认,待开发,开发中,待测试,测试中,待上线,已上线,已暂停"
status_validation = DataValidation(
type="list",
formula1=f'"{statuses}"',
allow_blank=True,
)
status_validation.error = "请选择下拉列表中的状态"
status_validation.errorTitle = "状态值无效"
status_validation.prompt = "请选择当前需求状态"
status_validation.promptTitle = "需求状态"
status_validation.showErrorMessage = True
status_validation.showInputMessage = True
sheet.add_data_validation(status_validation)
status_validation.add(f"F2:F{len(FUNCTIONS) + 1}")
paused_fill = PatternFill("solid", fgColor="FCE4D6")
online_fill = PatternFill("solid", fgColor="E2F0D9")
sheet.conditional_formatting.add(
f"A2:O{len(FUNCTIONS) + 1}",
FormulaRule(formula=['$F2="已暂停"'], fill=paused_fill),
)
sheet.conditional_formatting.add(
f"A2:O{len(FUNCTIONS) + 1}",
FormulaRule(formula=['$F2="已上线"'], fill=online_fill),
)
sheet.print_title_rows = "1:1"
sheet.sheet_properties.pageSetUpPr.fitToPage = True
sheet.page_setup.orientation = "landscape"
sheet.page_setup.fitToWidth = 1
sheet.page_setup.fitToHeight = 0
sheet.print_area = f"A1:O{len(FUNCTIONS) + 1}"
return workbook
def main() -> None:
workbook = build_workbook()
workbook.save(OUTPUT_PATH)
print(f"已生成:{OUTPUT_PATH}")
if __name__ == "__main__":
main()
@@ -0,0 +1,180 @@
from pathlib import Path
import unittest
from collections import Counter
from openpyxl import load_workbook
ROOT = Path(__file__).resolve().parents[1]
WORKBOOK_PATH = ROOT / "柏悦物流客户端-需求管理表.xlsx"
HEADERS = [
"序号",
"版本",
"模块",
"需求范围",
"需求描述",
"状态",
"当前进展",
"需求确认时间",
"需求负责人",
"开始开发时间",
"开发负责人",
"开始测试时间",
"测试负责人",
"预计上线时间",
"备注",
]
TRANSFER_SCOPES = {
"仓储转运单:打开新增订单页",
"仓储转运单:填写基础与派送信息",
"仓储转运单:维护货物明细",
"仓储转运单:选择是否换标",
"仓储转运单:维护换标信息",
"仓储转运单:提交并生成后台尾程订单",
"仓储转运单:条件生成后台换标单",
"仓储转运单:设置查询条件",
"仓储转运单:查询与重置",
"仓储转运单:查看订单列表",
"仓储转运单:查看订单详情及关联单号",
"仓储转运单:分页浏览",
}
DISPOSAL_SCOPES = {
"货物废弃:打开新增订单页", "货物废弃:选择关联入库单", "货物废弃:填写明细废弃箱数",
"货物废弃:实时计算弃货费", "货物废弃:提交并生成尾程单", "货物废弃:生成废弃出库单",
"货物废弃:生成弃货费计费单", "货物废弃:设置查询条件", "货物废弃:查询与重置",
"货物废弃:查看订单列表", "货物废弃:查看订单详情及关联单据", "货物废弃:分页浏览",
}
RETURN_RELABEL_SCOPES = {
"退货换标:打开新增订单页", "退货换标:填写退货信息", "退货换标:维护多SKU货物明细",
"退货换标:选择是否换标", "退货换标:选择贴标要求", "退货换标:上传换标文件",
"退货换标:填写尾程信息", "退货换标:条件生成后台换标单", "退货换标:生成后台尾程订单",
"退货换标:设置查询条件", "退货换标:查看订单列表", "退货换标:查看订单详情",
}
class RequirementsWorkbookTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
if not WORKBOOK_PATH.exists():
raise AssertionError(f"成品工作簿不存在:{WORKBOOK_PATH}")
cls.workbook = load_workbook(WORKBOOK_PATH)
cls.sheet = cls.workbook["需求管理表"]
@classmethod
def tearDownClass(cls):
if hasattr(cls, "workbook"):
cls.workbook.close()
def test_structure_matches_requested_template(self):
self.assertEqual(self.workbook.sheetnames, ["需求管理表"])
self.assertEqual(
[self.sheet.cell(1, column).value for column in range(1, 16)],
HEADERS,
)
self.assertEqual((self.sheet.max_row, self.sheet.max_column), (151, 15))
self.assertEqual(
[self.sheet.cell(row, 1).value for row in range(2, 152)],
list(range(1, 151)),
)
def test_management_fields_start_empty(self):
empty_columns = (6, 7, 8, 9, 10, 11, 12, 13, 14)
for row in range(2, 152):
for column in empty_columns:
self.assertIsNone(self.sheet.cell(row, column).value)
scope = self.sheet.cell(row, 4).value
version = self.sheet.cell(row, 2).value
if scope in TRANSFER_SCOPES:
self.assertEqual(version, "0805")
elif scope in DISPOSAL_SCOPES or scope in RETURN_RELABEL_SCOPES:
self.assertEqual(str(version), "0812")
else:
self.assertIsNone(version)
def test_modules_scopes_and_descriptions_follow_0805_mapping(self):
expected_counts = {
"业务总览": 10,
"头程物流": 42,
"出海下单": 18,
"海外仓": 42,
"订单管理": 13,
"财务结算": 5,
"公司信息": 5,
"我的信息": 9,
"全局工具": 6,
}
module_counts = Counter(
self.sheet.cell(row, 3).value for row in range(2, self.sheet.max_row + 1)
)
self.assertEqual(module_counts, Counter(expected_counts))
for row in range(2, self.sheet.max_row + 1):
scope = self.sheet.cell(row, 4).value
description = self.sheet.cell(row, 5).value
self.assertEqual(scope.count(":"), 1, f"第 {row} 行需求范围格式错误")
secondary, feature = scope.split(":")
self.assertTrue(secondary.strip(), f"第 {row} 行缺少二级栏目")
self.assertTrue(feature.strip(), f"第 {row} 行缺少功能点名称")
self.assertTrue(description.strip(), f"第 {row} 行缺少需求描述")
def test_representative_0805_functions_are_present(self):
scopes = {
self.sheet.cell(row, 4).value for row in range(2, self.sheet.max_row + 1)
}
self.assertTrue(
{
"头程下单:选择取货方式",
"一键导入:上传 Excel",
"贴标申请:上传面单贴标文件",
"入库订单列表:导出订单尾程信息",
"资质文件:下载文件",
"账户余额:筛选余额明细",
}.issubset(scopes)
)
def test_transfer_order_scopes_and_version_are_present_once(self):
scopes = [
self.sheet.cell(row, 4).value
for row in range(2, self.sheet.max_row + 1)
]
transfer_rows = {
self.sheet.cell(row, 4).value: row
for row in range(2, self.sheet.max_row + 1)
if self.sheet.cell(row, 4).value in TRANSFER_SCOPES
}
self.assertEqual(set(transfer_rows), TRANSFER_SCOPES)
self.assertEqual(sum(scope in TRANSFER_SCOPES for scope in scopes), len(TRANSFER_SCOPES))
for scope, row in transfer_rows.items():
self.assertEqual(self.sheet.cell(row, 2).value, "0805", scope)
def test_cargo_disposal_scopes_and_version_are_present_once(self):
disposal_rows = {self.sheet.cell(row, 4).value: row for row in range(2, self.sheet.max_row + 1) if self.sheet.cell(row, 4).value in DISPOSAL_SCOPES}
self.assertEqual(set(disposal_rows), DISPOSAL_SCOPES)
for scope, row in disposal_rows.items():
self.assertEqual(str(self.sheet.cell(row, 2).value), "0812", scope)
def test_return_relabel_scopes_replace_legacy_return_modules(self):
scopes = [self.sheet.cell(row, 4).value for row in range(2, self.sheet.max_row + 1)]
return_rows = {scope: row for row, scope in enumerate(scopes, start=2) if scope in RETURN_RELABEL_SCOPES}
self.assertEqual(set(return_rows), RETURN_RELABEL_SCOPES)
self.assertFalse(any(scope.startswith("退货入库:") or scope.startswith("退货出库:") for scope in scopes))
for scope, row in return_rows.items():
self.assertEqual(str(self.sheet.cell(row, 2).value), "0812", scope)
def test_filter_freeze_and_status_dropdown_are_configured(self):
self.assertEqual(self.sheet.freeze_panes, "A2")
self.assertEqual(self.sheet.auto_filter.ref, "A1:O151")
validations = list(self.sheet.data_validations.dataValidation)
self.assertEqual(len(validations), 1)
self.assertEqual(str(validations[0].sqref), "F2:F151")
self.assertIn("待开发", validations[0].formula1)
self.assertIn("已上线", validations[0].formula1)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,87 @@
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { test } from 'node:test';
const htmlPath = new URL('../柏悦物流客户端-栏目架构图.html', import.meta.url);
const markdownPath = new URL('../柏悦物流客户端-栏目架构图.md', import.meta.url);
test('standalone architecture HTML exists', () => {
assert.equal(existsSync(htmlPath), true, 'HTML output should exist');
});
test('architecture data preserves the approved information hierarchy', () => {
assert.equal(existsSync(htmlPath), true, 'HTML output should exist');
const html = readFileSync(htmlPath, 'utf8');
const match = html.match(/<script id="architecture-data" type="application\/json">([\s\S]*?)<\/script>/);
assert.ok(match, 'embedded architecture JSON should exist');
const data = JSON.parse(match[1]);
assert.equal(data.groups.length, 8, 'there should be eight first-level columns');
const businessEntries = data.groups
.flatMap((group) => group.children)
.filter((child) => child.businessEntry);
assert.equal(businessEntries.length, 13, 'there should be thirteen second-level business entries');
const warehouse = data.groups.find((group) => group.title === '海外仓');
assert.ok(warehouse, 'the overseas warehouse group should exist');
assert.deepEqual(
warehouse.children.map((child) => child.title),
['贴标申请', '仓储转运单', '货物废弃', '退货换标'],
);
const returnRelabel = warehouse.children.find((child) => child.title === '退货换标');
assert.ok(returnRelabel, 'the consolidated return relabel entry should exist');
assert.deepEqual(
returnRelabel.children.filter((child) => child.kind === '模块').map((child) => child.title),
['新增订单', '查询订单', '订单列表'],
);
assert.deepEqual(
returnRelabel.children
.filter((child) => child.kind === '联动')
.map(({ title, relation, integration }) => ({ title, relation, integration })),
[
{ title: '后台海外仓-尾程订单', relation: '提交后联动', integration: true },
{ title: '后台海外仓-换标单', relation: '选择换标时联动', integration: true },
],
);
const transferOrder = warehouse.children.find((child) => child.title === '仓储转运单');
assert.ok(transferOrder, 'the warehouse transfer order entry should exist');
assert.equal(transferOrder.businessEntry, true);
assert.deepEqual(
transferOrder.children
.filter((child) => child.kind === '模块')
.map((child) => child.title),
['新增订单', '查询订单', '订单列表'],
);
assert.deepEqual(
transferOrder.children
.filter((child) => child.kind === '联动')
.map(({ title, relation, integration }) => ({ title, relation, integration })),
[
{ title: '后台海外仓-尾程订单', relation: '提交后联动', integration: true },
{ title: '后台海外仓-换标单', relation: '选择换标时联动', integration: true },
],
);
assert.deepEqual(data.utilities.map((item) => item.title), ['多语言', '充值', '返回官网', '退出登录']);
});
test('architecture markdown documents the warehouse transfer order integrations', () => {
const markdown = readFileSync(markdownPath, 'utf8');
assert.match(markdown, /仓储转运单/);
assert.match(markdown, /尾程订单/);
assert.match(markdown, /换标单/);
});
test('page is offline, responsive, and exposes accessible controls', () => {
assert.equal(existsSync(htmlPath), true, 'HTML output should exist');
const html = readFileSync(htmlPath, 'utf8');
assert.match(html, /<html lang="zh-CN">/);
assert.match(html, /name="viewport"/);
assert.match(html, /aria-expanded/);
assert.match(html, /id="expand-all"/);
assert.match(html, /id="collapse-all"/);
assert.match(html, /@media \(max-width: 720px\)/);
assert.doesNotMatch(html, /<(?:script|img)[^>]+src="https?:/i);
assert.doesNotMatch(html, /<link[^>]+href="https?:/i);
assert.doesNotMatch(html, /url\(["']?https?:/i);
});
@@ -0,0 +1,75 @@
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { test } from 'node:test';
const prototypePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '官网头程下单', 'workbench.html');
const html = existsSync(prototypePath) ? readFileSync(prototypePath, 'utf8') : '';
function page(id) {
const start = html.indexOf(`id="${id}"`);
assert.ok(start >= 0, `${id} should exist`);
const next = html.indexOf('<div class="page-content', start + 1);
return html.slice(Math.max(0, html.lastIndexOf('<div', start)), next >= 0 ? next : html.length);
}
function fn(name) {
const start = html.indexOf(`function ${name}(`);
assert.ok(start >= 0, `${name} should exist`);
const next = html.indexOf('\n function ', start + 1);
return html.slice(start, next >= 0 ? next : html.length);
}
test('cargo disposal navigation and page trio exist', () => {
const warehouse = html.match(/<!-- 海外仓 -->([\s\S]*?)<!-- 订单管理 -->/);
assert.ok(warehouse);
assert.match(warehouse[1], /仓储转运单[\s\S]*货物废弃[\s\S]*退货换标/);
assert.match(warehouse[1], /dashNavigate\('cargo-disposal-list', this\)/);
for (const id of ['page-cargo-disposal-list', 'page-cargo-disposal-create', 'page-cargo-disposal-detail']) page(id);
});
test('cargo disposal create page uses expandable inbound cards and fee rule', () => {
const create = page('page-cargo-disposal-create');
for (const copy of ['选择入仓码', '入仓码搜索', '废弃箱数', '可用箱数', '弃货费', '5 USD/箱', '展开']) assert.match(create, new RegExp(copy));
assert.match(create, /id="cargo-disposal-inbound-search"/);
assert.match(create, /oninput="filterCargoDisposalInboundCards\(\)"/);
assert.match(create, /cargo-disposal-inbound-card/);
assert.match(create, /cargo-disposal-quantity/);
});
test('cargo disposal inbound-code search filters cards and exposes a no-results state', () => {
const filter = fn('filterCargoDisposalInboundCards');
assert.match(filter, /trim\(\)\.toLowerCase\(\)/);
assert.match(filter, /inboundNo\.toLowerCase\(\)\.includes/);
assert.match(filter, /renderCargoDisposalInboundCards/);
assert.match(fn('renderCargoDisposalInboundCards'), /未找到匹配的入仓码/);
});
test('cargo disposal list and detail expose four relations', () => {
const list = page('page-cargo-disposal-list');
const detail = page('page-cargo-disposal-detail');
for (const copy of ['货物废弃单号', '关联入库单', '入仓码', '废弃出库单号', '计费单号']) assert.match(list, new RegExp(copy));
assert.doesNotMatch(list, /<th>尾程单号<\/th>/);
assert.match(fn('renderCargoDisposalList'), /escapeCargoDisposalHtml\(order\.inboundNo\).*escapeCargoDisposalHtml\(order\.outboundOrderNo\)/);
for (const copy of ['客户端货物废弃单号', '后台尾程单号', '废弃出库单号', '弃货费计费单号']) assert.match(detail, new RegExp(copy));
});
test('cargo disposal implementation contains safe validation and fixed fee generation', () => {
assert.match(html, /CARGO_DISPOSAL_KEY\s*=\s*['"]by_cargo_disposal_orders_0805['"]/);
for (const name of ['loadCargoDisposalOrders', 'renderCargoDisposalList', 'toggleCargoDisposalInbound', 'filterCargoDisposalInboundCards', 'calculateCargoDisposalFee', 'validateCargoDisposalForm', 'submitCargoDisposalOrder', 'openCargoDisposalDetail']) fn(name);
assert.match(html, /CARGO_DISPOSAL_FEE_PER_BOX\s*=\s*5/);
assert.match(fn('validateCargoDisposalForm'), /availableBoxes/);
const submit = fn('submitCargoDisposalOrder');
for (const marker of ['tailOrderNo', 'outboundOrderNo', 'billingNo']) assert.match(submit, new RegExp(marker));
});
test('cargo disposal list includes several stable sample orders without persisting duplicates', () => {
assert.match(html, /const\s+CARGO_DISPOSAL_ORDER_SEED\s*=\s*\[/);
const seedStart = html.indexOf('const CARGO_DISPOSAL_ORDER_SEED');
const seedEnd = html.indexOf('];', seedStart);
const seed = html.slice(seedStart, seedEnd);
assert.ok((seed.match(/id:'cargo-disposal-seed-/g) || []).length >= 3);
assert.match(fn('loadCargoDisposalOrders'), /CARGO_DISPOSAL_ORDER_SEED/);
assert.match(fn('saveCargoDisposalOrders'), /CARGO_DISPOSAL_ORDER_SEED/);
});
@@ -0,0 +1,67 @@
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { test } from 'node:test';
const prototypePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '官网头程下单', 'workbench.html');
const html = existsSync(prototypePath) ? readFileSync(prototypePath, 'utf8') : '';
function page(id) {
const start = html.indexOf(`id="${id}"`);
assert.ok(start >= 0, `${id} should exist`);
const next = html.indexOf('<div class="page-content', start + 1);
return html.slice(Math.max(0, html.lastIndexOf('<div', start)), next >= 0 ? next : html.length);
}
function fn(name) {
const start = html.indexOf(`function ${name}(`);
assert.ok(start >= 0, `${name} should exist`);
const next = html.indexOf('\n function ', start + 1);
return html.slice(start, next >= 0 ? next : html.length);
}
test('labeling list has add action, query controls, and order table', () => {
const list = page('page-labeling-list');
assert.match(list, /贴标申请/);
assert.match(list, /onclick="openLabelingCreate\(\)"/);
for (const id of ['labeling-filter-order-no', 'labeling-filter-customer-no', 'labeling-filter-status']) {
assert.match(list, new RegExp(`id="${id}"`));
}
assert.match(list, /onclick="filterLabelingOrders\(\)"/);
assert.match(list, /id="labeling-list-body"/);
});
test('labeling create page exposes order form and the three approved requirements', () => {
const create = page('page-labeling');
const addRow = fn('addLabelingRow');
assert.match(create, /新增贴标申请/);
assert.match(create, /id="labeling-form"/);
for (const copy of ['换箱唛', '换SKU标', '既换箱唛,也换SKU标']) assert.match(addRow, new RegExp(copy));
assert.match(addRow, /name=\\?"labelRequirement/);
assert.match(create, /type="button"[^>]*onclick="addLabelingRow\(\)"/);
assert.match(create, /type="button"[^>]*onclick="clearLabelingRows\(\)"/);
});
test('labeling list persistence and query functions are defined', () => {
for (const name of ['loadLabelingOrders', 'saveLabelingOrders', 'renderLabelingList', 'filterLabelingOrders', 'resetLabelingFilters', 'openLabelingList', 'openLabelingCreate', 'submitLabeling']) fn(name);
assert.match(fn('submitLabeling'), /saveLabelingOrders/);
assert.match(fn('filterLabelingOrders'), /labeling-filter-order-no/);
assert.match(fn('renderLabelingList'), /labelRequirement/);
});
test('labeling list includes several stable sample orders without persisting duplicates', () => {
assert.match(html, /const\s+LABELING_ORDER_SEED\s*=\s*\[/);
const seedStart = html.indexOf('const LABELING_ORDER_SEED');
const seedEnd = html.indexOf('];', seedStart);
const seed = html.slice(seedStart, seedEnd);
assert.ok((seed.match(/id:'labeling-seed-/g) || []).length >= 3);
assert.match(fn('loadLabelingOrders'), /LABELING_ORDER_SEED/);
assert.match(fn('saveLabelingOrders'), /LABELING_ORDER_SEED/);
});
test('labeling submit rejects an empty detail table after rows are removed', () => {
const submit = fn('submitLabeling');
assert.match(submit, /if\s*\(\s*!rows\.length\s*\)/);
assert.match(submit, /请至少添加一行货物明细/);
});
@@ -0,0 +1,223 @@
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { test } from 'node:test';
const prototypePath = new URL('../../官网头程下单/workbench.html', import.meta.url);
const html = existsSync(prototypePath) ? readFileSync(prototypePath, 'utf8') : '';
function extractPageFragment(pageId) {
const idMatch = new RegExp(`id=["']${pageId}["']`).exec(html);
if (!idMatch) return null;
const start = html.lastIndexOf('<div', idMatch.index);
const remainingHtml = html.slice(idMatch.index + idMatch[0].length);
const nextPageMatch = /<div\s+class=["'][^"']*\bpage-content\b[^"']*["'][^>]*>/.exec(remainingHtml);
const end = nextPageMatch
? idMatch.index + idMatch[0].length + nextPageMatch.index
: html.length;
return html.slice(start, end);
}
function extractFirstTableHeaders(pageFragment, message) {
const tableMatch = pageFragment?.match(/<table[^>]*>([\s\S]*?)<\/table>/);
assert.ok(tableMatch, message);
return [...tableMatch[1].matchAll(/<th[^>]*>([\s\S]*?)<\/th>/g)].map((match) =>
match[1].replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim(),
);
}
function extractFunctionFragment(functionName) {
const start = html.indexOf(`function ${functionName}(`);
assert.ok(start >= 0, `${functionName} should exist`);
const next = html.indexOf('\n function ', start + 1);
return html.slice(start, next >= 0 ? next : html.length);
}
test('overseas menu is consolidated to the three approved entries', () => {
assert.equal(existsSync(prototypePath), true, 'workbench prototype should exist');
const warehouseMenu = html.match(/<!-- 海外仓 -->([\s\S]*?)<!-- 订单管理 -->/);
assert.ok(warehouseMenu, 'overseas warehouse menu should exist');
const labels = [...warehouseMenu[1].matchAll(/<a[^>]*class=["'][^"']*dash-nav-link\s+sub[^"']*["'][^>]*>[\s\S]*?<span[^>]*>([^<]+)<\/span>/g)].map((match) =>
match[1].trim(),
);
assert.deepEqual(labels, ['贴标申请', '仓储转运单', '货物废弃', '退货换标']);
assert.doesNotMatch(warehouseMenu[1], /退货入库|退货出库/);
});
test('return relabel list, create, and detail pages exist', () => {
for (const pageId of ['page-return-relabel-list', 'page-return-relabel-create', 'page-return-relabel-detail']) {
assert.ok(new RegExp(`id=["']${pageId}["']`).test(html), `${pageId} should exist`);
}
});
test('return relabel list table has the consolidated order columns', () => {
const listPage = extractPageFragment('page-return-relabel-list');
assert.ok(listPage, 'return relabel list page should exist');
assert.deepEqual(extractFirstTableHeaders(listPage, 'return relabel list table should exist'), [
'退货换标单号',
'来源FBA仓',
'目的FBA仓',
'SKU汇总',
'退件总数',
'是否换标',
'贴标要求',
'后台尾程订单号',
'后台换标单号',
'换标状态',
'尾程订单状态',
'创建时间',
'查看',
]);
for (const filterId of [
'return-relabel-filter-sku',
'return-relabel-filter-enabled',
'return-relabel-filter-tail-status',
]) {
assert.ok(listPage.includes(`id="${filterId}"`), `${filterId} should exist`);
}
});
test('return relabel create form exposes order, multi-SKU, relabel, file, and tail fields', () => {
const createPage = extractPageFragment('page-return-relabel-create');
assert.ok(createPage, 'return relabel create page should exist');
for (const field of [
'来源 FBA 仓库',
'目的 FBA 仓库',
'移除订单 Order ID',
'物流承运商',
'物流跟踪号',
'退货原因',
'上传箱唛',
'上传 SKU 标签',
'派送地址',
'目的仓代码',
'期望到达时间',
]) {
assert.ok(createPage.includes(field), `create page should contain field: ${field}`);
}
assert.deepEqual(extractFirstTableHeaders(createPage, 'return relabel SKU table should exist'), [
'商品SKU',
'中文品名',
'FNSKU',
'退件数量',
'操作',
]);
for (const caseLabel of ['Case1', 'Case2', 'Case3']) {
assert.ok(createPage.includes(caseLabel), `create page should contain ${caseLabel}`);
}
assert.match(createPage, /是否换标/);
for (const section of ['退货信息', '货物明细', '换标设置', '尾程信息']) {
assert.ok(createPage.includes(section), `create page should contain section: ${section}`);
}
for (const meaning of ['只换箱唛', '只换SKU标', '既换箱唛,又换SKU标']) {
assert.ok(createPage.includes(meaning), `create page should explain: ${meaning}`);
}
assert.match(createPage, /name=["']returnRemark["']/);
assert.match(createPage, /name=["']tailRemark["']/);
});
test('return relabel detail keeps backend relations and separate statuses', () => {
const detailPage = extractPageFragment('page-return-relabel-detail');
const detailFunction = extractFunctionFragment('openReturnRelabelDetail');
assert.ok(detailPage, 'return relabel detail page should exist');
for (const label of ['后台尾程订单号', '后台换标单号', '换标状态', '尾程订单状态']) {
assert.ok(detailPage.includes(label), `detail page should contain ${label}`);
}
for (const label of ['是否换标', '贴标要求', '上传箱唛', '上传 SKU 标签', '期望到达时间', '退件总数']) {
assert.ok(detailFunction.includes(label), `detail renderer should contain ${label}`);
}
});
test('return relabel persistence and interaction functions use the approved contract', () => {
assert.ok(
/const\s+RETURN_RELABEL_KEY\s*=\s*["']by_return_relabel_orders_0812["']/.test(html),
'RETURN_RELABEL_KEY should use by_return_relabel_orders_0812',
);
for (const functionName of [
'loadReturnRelabelOrders',
'saveReturnRelabelOrders',
'renderReturnRelabelList',
'filterReturnRelabelOrders',
'resetReturnRelabelFilters',
'addReturnRelabelSkuRow',
'removeReturnRelabelSkuRow',
'toggleReturnRelabel',
'updateFileRequirements',
'validateReturnRelabelForm',
'submitReturnRelabelOrder',
'openReturnRelabelDetail',
]) {
assert.match(html, new RegExp(`function\\s+${functionName}\\s*\\(`), `${functionName} should be defined`);
}
});
test('return relabel submit contract always creates tail order and conditionally creates relabel order', () => {
const submit = extractFunctionFragment('submitReturnRelabelOrder');
assert.match(submit, /submitButton\.disabled\s*=\s*true/);
assert.match(submit, /submitButton\.disabled\s*=\s*false/);
assert.match(submit, /backendTailOrderNo|tailOrderNo|tailOrderId/);
assert.match(submit, /if\s*\([^)]*(?:relabel|换标)[^)]*\)[\s\S]*(?:backendRelabelOrderNo|relabelOrderNo|relabelOrderId)/i);
assert.match(submit, /(?:relabel|换标)[\s\S]*(?:未换标|'-'|"-")/i);
});
test('return relabel Case rules and status labels are represented statically', () => {
const fileRequirements = extractFunctionFragment('updateFileRequirements');
assert.match(fileRequirements, /Case1/);
assert.match(fileRequirements, /Case2/);
assert.match(fileRequirements, /Case3/);
assert.match(fileRequirements, /箱唛/);
assert.match(fileRequirements, /SKU 标签|SKU标/);
for (const status of ['待换标', '换标中', '已完成', '待入库', '已入库待出库', '出库中']) {
assert.ok(html.includes(status), `status label should be present: ${status}`);
}
});
test('return relabel filters, form reset, seed merge, and visible UI follow the full specification', () => {
const listPage = extractPageFragment('page-return-relabel-list');
const createPage = extractPageFragment('page-return-relabel-create');
const detailPage = extractPageFragment('page-return-relabel-detail');
const loader = extractFunctionFragment('loadReturnRelabelOrders');
const opener = extractFunctionFragment('openReturnRelabelCreate');
const filter = extractFunctionFragment('filterReturnRelabelOrders');
const submit = extractFunctionFragment('submitReturnRelabelOrder');
const tailStatus = listPage.match(/id=["']return-relabel-filter-tail-status["'][\s\S]*?<\/select>/)?.[0] || '';
assert.match(tailStatus, />已完成</);
assert.match(filter, /returnRelabelDateValue|normalizeReturnRelabelDate|slice\(0,\s*10\)/);
assert.match(opener, /\.reset\(\)/, 'opening create page should reset the form');
assert.match(opener, /return-relabel-sku-body[\s\S]*(?:innerHTML\s*=\s*["']{2}|replaceChildren)/);
assert.match(loader, /RETURN_RELABEL_SEED[\s\S]*(?:concat|\.\.\.)/);
assert.doesNotMatch(loader, /saveReturnRelabelOrders\(RETURN_RELABEL_SEED\)/);
assert.doesNotMatch(createPage, /return-relabel-section-index/);
assert.doesNotMatch(createPage, /return-relabel-case-copy/);
assert.doesNotMatch(detailPage, /return-relabel-detail-contract/);
assert.match(createPage, /<th[^>]*>操作<\/th>/);
assert.match(submit, /datePart\s*=\s*now\.getFullYear\(\)/);
assert.match(submit, /createdAt:\s*datePart/);
assert.match(submit, /relabel[\s\S]*(?:换标单|backendRelabelOrderNo)/);
});
test('return relabel validation and persistence hardening cover important edge cases', () => {
const loader = extractFunctionFragment('loadReturnRelabelOrders');
const fileRequirements = extractFunctionFragment('updateFileRequirements');
const validator = extractFunctionFragment('validateReturnRelabelForm');
const submit = extractFunctionFragment('submitReturnRelabelOrder');
for (const field of ['orderId', 'carrier', 'trackingNo', 'address', 'destinationCode']) {
assert.ok(validator.includes(`'${field}'`), `${field} should be trimmed and validated`);
}
assert.match(validator, /form\.elements\[field\][\s\S]*\.trim\(\)/);
assert.match(fileRequirements, /Case1[\s\S]*(?:disabled|hidden)/);
assert.match(fileRequirements, /Case2[\s\S]*(?:disabled|hidden)/);
assert.match(loader, /Array\.isArray\(item\.skus\)/);
assert.match(loader, /item\.skus\.every\(s=>s&&typeof s==='object'/);
assert.match(loader, /validLocal\.concat\(RETURN_RELABEL_SEED\)/);
assert.match(submit, /getFullYear\(\)/);
assert.doesNotMatch(submit, /datePart\s*=\s*now\.toISOString/);
const renderer = extractFunctionFragment('renderReturnRelabelList');
assert.doesNotMatch(renderer, /onclick=["'][^"']*openReturnRelabelDetail/);
assert.match(renderer, /data-return-relabel-id/);
});
@@ -0,0 +1,113 @@
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
import { test } from 'node:test';
const prototypePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '官网头程下单', 'workbench.html');
const html = existsSync(prototypePath) ? readFileSync(prototypePath, 'utf8') : '';
function fn(name) {
const start = html.indexOf(`function ${name}(`);
assert.ok(start >= 0, `${name} should exist`);
const next = html.indexOf('\n function ', start + 1);
return html.slice(start, next >= 0 ? next : html.length);
}
test('sea order navigation exposes only three business entry points', () => {
const group = html.match(/<!-- 出海下单 -->([\s\S]*?)<!-- 海外仓 -->/);
assert.ok(group, 'sea order menu should exist');
assert.equal((group[1].match(/data-sea-action=/g) || []).length, 0);
const entries = [...group[1].matchAll(/data-business="(Procurement|Truck|Pallet)"[^>]*onclick="([^"]+)"/g)];
assert.equal(entries.length, 3);
for (const [type, label] of [['Procurement', '海外采购'], ['Truck', '美国本土卡'], ['Pallet', '美本土散板']]) {
const entry = entries.find(([, , onclick]) => onclick.includes(`openSeaBusiness('${type}','list'`));
assert.ok(entry, `${label} navigation entry should open its list page`);
assert.match(entry[0], new RegExp(`data-business="${type}"`));
}
});
test('three sea business list pages include add, query, and order lists', () => {
const cases = [
['sea-procurement-page', 'Procurement', '海外采购', ['订单号', '采购品类', '商品规格', '采购数量', '预算单价', '状态', '操作']],
['sea-truck-page', 'Truck', '美国本土卡', ['操作', '订单编号', '运输类型', '起运地', '目的地', '柜型', '总重量(kg)', '预算总价', '要求时间', '状态']],
['sea-pallet-page', 'Pallet', '美本土散板', ['操作', '订单编号', '运输方式', '是否打盘', '运输类型', '起运地', '目的地', '总重量(kg)', '预算总价', '要求时间', '状态']],
];
for (const [id, type, label, headers] of cases) {
const start = html.indexOf(`id="${id}"`);
assert.ok(start >= 0, `${id} should exist`);
const page = html.slice(start, html.indexOf('</section>', start) + 10);
assert.match(page, new RegExp(label));
assert.match(page, new RegExp(`openSeaBusiness\\('${type}','new'`));
assert.match(page, new RegExp(`querySeaOrders\\('${type}'\\)`));
assert.match(page, new RegExp(`resetSeaOrders\\('${type}'\\)`));
for (const header of headers) assert.match(page, new RegExp(`<th>${header.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}</th>`));
}
const truckStart = html.indexOf('id="sea-truck-page"');
const truckPage = html.slice(truckStart, html.indexOf('</section>', truckStart) + 10);
assert.doesNotMatch(truckPage, /<th>联系人<\/th>|<th>联系方式<\/th>/);
assert.match(html.slice(html.indexOf('data-order-form="Truck"'), html.indexOf('</form>', html.indexOf('data-order-form="Truck"'))), /name="预算总价"/);
const palletStart = html.indexOf('id="sea-pallet-page"');
const palletPage = html.slice(palletStart, html.indexOf('</section>', palletStart) + 10);
assert.doesNotMatch(palletPage, /<th>联系人<\/th>|<th>联系方式<\/th>/);
assert.match(html.slice(html.indexOf('data-order-form="Pallet"'), html.indexOf('</form>', html.indexOf('data-order-form="Pallet"'))), /name="预算总价"/);
});
test('all sea business orders support edit and detail actions', () => {
for (const name of ['openSeaOrderEditor', 'closeSeaOrderEditor', 'saveSeaOrderEdit', 'showSeaOrderDetail']) fn(name);
assert.match(fn('renderSeaOrders'), /openSeaOrderEditor/);
assert.match(fn('renderSeaOrders'), /showSeaOrderDetail/);
assert.match(html, /id="sea-order-edit-modal"/);
assert.match(html, /id="sea-order-edit-form"/);
assert.match(fn('openSeaOrderEditor'), /data-order-form/);
assert.match(fn('saveSeaOrderEdit'), /seaOrders\[type\]/);
});
test('sea business details expose business, carrier, and progress information', () => {
const details = [
['sea-procurement-detail', ['商品信息', '订单信息', '快递信息']],
['sea-truck-detail', ['运输信息', '货物信息', '承运商信息', '运输进度']],
['sea-pallet-detail', ['运输信息', '货物信息', '承运商信息', '运输进度']],
];
for (const [id, sections] of details) {
const start = html.indexOf(`id="${id}"`);
assert.ok(start >= 0, `${id} should exist`);
const detail = html.slice(start, html.indexOf('</section>', start) + 10);
for (const section of sections) assert.match(detail, new RegExp(section));
assert.match(detail, /data-detail-content/);
}
const procurementDetail = html.slice(html.indexOf('id="sea-procurement-detail"'), html.indexOf('</section>', html.indexOf('id="sea-procurement-detail"')) + 10);
assert.doesNotMatch(procurementDetail, /采购进度/);
assert.doesNotMatch(procurementDetail, /承运商信息/);
assert.match(procurementDetail, /data-detail-content="预算总价"/);
for (const field of ['订单号', '采购数量', '预算单价', '订单状态', '联系人', '联系电话', '收货地址', '订单备注']) {
assert.match(procurementDetail, new RegExp(field));
}
for (const field of ['国内快递公司', '国内运单号', '海外快递公司', '海外运单号']) {
assert.match(procurementDetail, new RegExp(`data-detail-content="${field}"`));
}
});
test('sea business routing reuses existing forms and isolates order state', () => {
for (const name of ['showSeaBusinessPage', 'showSeaBusinessOrderForm', 'openSeaBusiness', 'renderSeaOrders', 'querySeaOrders', 'resetSeaOrders']) fn(name);
assert.match(html, /const\s+seaOrders\s*=\s*\{\s*Procurement:\s*\[/);
assert.match(html, /data-order-form="Procurement"/);
assert.match(html, /data-order-form="Truck"/);
assert.match(html, /data-order-form="Pallet"/);
assert.doesNotMatch(fn('openSeaBusiness'), /data-sea-action/);
assert.match(fn('openSeaBusiness'), /dash-nav-link\[data-business=/);
assert.match(fn('openOrder'), /openSeaBusiness\(type,'new'/);
assert.match(fn('querySeaOrders'), /seaOrders\[type\]/);
assert.match(html, /function\s+collectSeaOrderFields\s*\(/);
});
test('sea business controls match overseas warehouse colors', () => {
const styleStart = html.indexOf('.sea-business-page');
const styleEnd = html.indexOf('</style>', styleStart);
const styles = html.slice(styleStart, styleEnd);
assert.match(styles, /\.sea-business-page[^{}]*\.btn-primary|\.sea-business-form-panel[^{}]*\.btn-primary/);
assert.match(styles, /#667eea/);
assert.match(styles, /#764ba2/);
assert.match(styles, /\.sea-business-page \.sea-order-table th/);
assert.match(styles, /color:\s*#667eea/);
});
@@ -0,0 +1,199 @@
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { test } from 'node:test';
const prototypePath = new URL('../../官网头程下单/workbench.html', import.meta.url);
const html = existsSync(prototypePath) ? readFileSync(prototypePath, 'utf8') : '';
function extractPageFragment(pageId) {
const idMatch = new RegExp(`id=["']${pageId}["']`).exec(html);
if (!idMatch) return null;
const start = html.lastIndexOf('<div', idMatch.index);
const remainingHtml = html.slice(idMatch.index + idMatch[0].length);
const nextPageMatch = /<div\s+class=["'][^"']*\bpage-content\b[^"']*["'][^>]*>/.exec(remainingHtml);
const end = nextPageMatch
? idMatch.index + idMatch[0].length + nextPageMatch.index
: html.length;
return html.slice(start, end);
}
function extractTableHeaders(pageFragment, tableId) {
const tableMatch = pageFragment?.match(
new RegExp(`<table[^>]*id=["']${tableId}["'][^>]*>([\\s\\S]*?)<\\/table>`),
);
assert.ok(tableMatch, `${tableId} should exist in its page`);
return [...tableMatch[1].matchAll(/<th[^>]*>([\s\S]*?)<\/th>/g)].map((match) =>
match[1].replace(/<[^>]+>/g, '').trim(),
);
}
function extractFunctionFragment(functionName) {
const start = html.indexOf(`function ${functionName}(`);
assert.ok(start >= 0, `${functionName} should exist`);
const next = html.indexOf('\n function ', start + 1);
return html.slice(start, next >= 0 ? next : html.length);
}
test('storage transfer entry appears in the overseas warehouse menu in the approved order', () => {
assert.equal(existsSync(prototypePath), true, 'workbench prototype should exist');
const warehouseMenu = html.match(/<!-- 海外仓 -->([\s\S]*?)<!-- 订单管理 -->/);
assert.ok(warehouseMenu, 'overseas warehouse menu should exist');
const menuLabels = [...warehouseMenu[1].matchAll(/<a[^>]*class=["'][^"']*dash-nav-link\s+sub[^"']*["'][^>]*>[\s\S]*?<span[^>]*>([^<]+)<\/span>/g)].map((match) =>
match[1].trim(),
);
assert.deepEqual(menuLabels, ['贴标申请', '仓储转运单', '货物废弃', '退货换标']);
assert.ok(
warehouseMenu[1].includes("dashNavigate('storage-transfer-list', this)"),
"warehouse transfer menu should call dashNavigate('storage-transfer-list', this)",
);
});
test('storage transfer list, create, and detail pages exist', () => {
for (const pageId of [
'page-storage-transfer-list',
'page-storage-transfer-create',
'page-storage-transfer-detail',
]) {
assert.ok(new RegExp(`id=["']${pageId}["']`).test(html), `${pageId} should exist`);
}
});
test('storage transfer list has the approved filters, columns, actions, and pagination', () => {
const listPage = extractPageFragment('page-storage-transfer-list');
assert.ok(listPage, 'storage transfer list page fragment should exist');
assert.match(
listPage,
/<input(?=[^>]*id=["']storage-transfer-filter-created-start["'])(?=[^>]*type=["']date["'])[^>]*>/,
);
assert.match(
listPage,
/<input(?=[^>]*id=["']storage-transfer-filter-created-end["'])(?=[^>]*type=["']date["'])[^>]*>/,
);
assert.deepEqual(extractTableHeaders(listPage, 'storage-transfer-list-table'), [
'转运单号',
'进仓编号',
'发货箱数',
'操作仓',
'FBA 目的仓',
'FBA货件编号',
'派送方式',
'是否换标',
'后台尾程订单号',
'后台换标单号',
'状态',
'创建时间',
'查看',
]);
assert.ok(listPage.includes('onclick="openStorageTransferCreate()"'));
assert.ok(listPage.includes('onclick="filterStorageTransferOrders()"'));
assert.ok(listPage.includes('onclick="resetStorageTransferFilters()"'));
assert.ok(listPage.includes('onclick="changeStorageTransferPage(-1)"'));
assert.ok(listPage.includes('onclick="changeStorageTransferPage(1)"'));
});
test('storage transfer persistence and interaction functions are defined', () => {
assert.ok(
/const\s+STORAGE_TRANSFER_KEY\s*=\s*["']by_storage_transfer_orders_0805["']/.test(html),
'storage transfer orders should use the approved local storage key',
);
for (const functionName of [
'loadStorageTransferOrders',
'saveStorageTransferOrders',
'renderStorageTransferList',
'filterStorageTransferOrders',
'validateStorageTransferForm',
'submitStorageTransferOrder',
'openStorageTransferDetail',
'toggleStorageTransferRelabel',
]) {
assert.ok(
new RegExp(`function\\s+${functionName}\\s*\\(`).test(html),
`${functionName} should be defined`,
);
}
});
test('storage transfer form and detail expose required fields and backend relationships', () => {
const createPage = extractPageFragment('page-storage-transfer-create');
const detailPage = extractPageFragment('page-storage-transfer-detail');
assert.ok(createPage, 'storage transfer create page fragment should exist');
assert.ok(detailPage, 'storage transfer detail page fragment should exist');
for (const field of [
'进仓编号',
'操作仓',
'FBA 目的仓代码',
'详细派送地址',
'发货箱数',
'派送方式',
'期望到达时间',
'FBA 货件编号',
'FBA 追踪编号',
'FBA BOX 编号',
'是否换标',
]) {
assert.ok(createPage.includes(field), `storage transfer create page should contain field: ${field}`);
}
assert.ok(
createPage.includes('选择换标时同时生成'),
'storage transfer create page should explain the relabel relationship',
);
for (const relationshipCopy of ['后台尾程订单号', '后台换标单号']) {
assert.ok(
detailPage.includes(relationshipCopy),
`storage transfer detail page should contain relationship: ${relationshipCopy}`,
);
}
assert.deepEqual(extractTableHeaders(createPage, 'storage-transfer-cargo-table'), [
'商品SKU',
'中文品名',
'箱数',
'换标后SKU',
'贴标要求',
'换标文件',
'操作',
]);
const cargoTable = createPage.match(
/<table[^>]*id=["']storage-transfer-cargo-table["'][^>]*>([\s\S]*?)<\/table>/,
)[1];
assert.doesNotMatch(cargoTable, /每箱数量|备注/);
const yesRadio = createPage.match(
/<input(?=[^>]*type=["']radio["'])(?=[^>]*name=["']relabel["'])(?=[^>]*value=["']yes["'])[^>]*>/,
);
const noRadio = createPage.match(
/<input(?=[^>]*type=["']radio["'])(?=[^>]*name=["']relabel["'])(?=[^>]*value=["']no["'])[^>]*>/,
);
assert.ok(yesRadio, 'relabel yes radio should exist');
assert.ok(noRadio, 'relabel no radio should exist');
assert.doesNotMatch(yesRadio[0], /\bchecked\b/);
assert.match(noRadio[0], /\bchecked\b/);
assert.deepEqual(extractTableHeaders(detailPage, 'storage-transfer-detail-cargo-table'), [
'商品SKU',
'中文品名',
'箱数',
'换标后SKU',
'贴标要求',
'换标文件',
]);
});
test('storage transfer cargo interactions preserve one row and hide inactive relabel fields', () => {
const addRow = extractFunctionFragment('addStorageTransferCargoRow');
const removeRow = extractFunctionFragment('removeStorageTransferCargoRow');
const toggleRelabel = extractFunctionFragment('toggleStorageTransferRelabel');
const submitOrder = extractFunctionFragment('submitStorageTransferOrder');
assert.match(addRow, /storage-transfer-relabel-column/);
assert.match(removeRow, /addStorageTransferCargoRow\(\)/);
assert.match(toggleRelabel, /\.hidden\s*=\s*!enabled/);
assert.match(submitOrder, /submitButton\.disabled\s*=\s*true/);
assert.match(submitOrder, /submitButton\.disabled\s*=\s*false/);
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,196 @@
# 柏悦物流客户端栏目架构图
> 整理日期:2026-08-10
> 整理依据:`../官网头程下单/workbench.html` 当前实现的侧边栏、页面视图与全局操作。
## 一、栏目架构图
```mermaid
flowchart TB
ROOT[柏悦物流客户端]
ROOT --> HOME[业务总览]
ROOT --> FIRST[头程物流]
ROOT --> GLOBAL[出海下单]
ROOT --> WAREHOUSE[海外仓]
ROOT --> ORDER[订单管理]
ROOT --> FINANCE[财务结算]
ROOT --> COMPANY[公司信息]
ROOT --> PROFILE[我的信息]
HOME --> HOME_ORDER[快捷下单]
HOME_ORDER --> HOME_PURCHASE[海外采购]
HOME_ORDER --> HOME_TRUCK[美国本土卡]
HOME_ORDER --> HOME_PALLET[美本土散板]
HOME --> HOME_ACCOUNT[账户信息]
HOME --> HOME_DATA[数据概览]
HOME --> HOME_HELP[帮助与支持]
HOME_HELP --> SERVICE[联系客服]
HOME_HELP --> FAQ[常见问题]
HOME_HELP --> QUOTE[获取报价]
FIRST --> FIRST_ORDER[头程下单]
FIRST --> FIRST_IMPORT[一键导入]
FIRST --> FIRST_QUICK[快速下单]
FIRST_ORDER --> PICKUP[取货方式]
FIRST_ORDER --> DEST[目的地信息]
FIRST_ORDER --> CARGO[货物明细]
FIRST_ORDER --> ESTIMATE[费用预估]
PICKUP --> EXPRESS[自送货-快递]
PICKUP --> PICKUP_BY_US[我司提货]
PICKUP --> SELF_TRUCK[自送货-卡车]
DEST --> FBA[FBA仓]
DEST --> OVERSEAS_DEST[海外仓]
DEST --> PRIVATE_DEST[私人仓]
GLOBAL --> PURCHASE[海外采购]
GLOBAL --> TRUCK[美国本土卡]
GLOBAL --> PALLET[美本土散板]
WAREHOUSE --> LABEL[贴标申请]
WAREHOUSE --> TRANSFER[仓储转运单]
TRANSFER --> TRANSFER_CREATE[新增订单]
TRANSFER --> TRANSFER_QUERY[查询订单]
TRANSFER --> TRANSFER_LIST[订单列表]
TRANSFER -.提交后联动.-> TAIL_ORDER[后台海外仓-尾程订单]
TRANSFER -.选择换标时联动.-> RELABEL_ORDER[后台海外仓-换标单]
WAREHOUSE --> DISPOSAL[货物废弃]
DISPOSAL --> DISPOSAL_CREATE[新增订单]
DISPOSAL --> DISPOSAL_QUERY[查询订单]
DISPOSAL --> DISPOSAL_LIST[订单列表]
DISPOSAL -.关联.-> INBOUND_ORDER[相关入库单]
DISPOSAL -.提交后联动.-> DISPOSAL_TAIL[后台尾程单]
DISPOSAL -.提交后联动.-> DISPOSAL_OUT[后台出库单-废弃]
DISPOSAL -.提交后联动.-> DISPOSAL_FEE[后台计费-弃货费]
WAREHOUSE --> RETURN_SWAP[退货换标]
RETURN_SWAP --> RETURN_SWAP_NEW[新增订单]
RETURN_SWAP --> RETURN_SWAP_QUERY[查询订单]
RETURN_SWAP --> RETURN_SWAP_LIST[订单列表]
RETURN_SWAP -.提交后联动.-> RETURN_SWAP_TAIL[后台海外仓-尾程订单]
RETURN_SWAP -.选择换标时联动.-> RETURN_SWAP_RELABEL[后台海外仓-换标单]
ORDER --> INBOUND[入库订单列表]
ORDER --> DELIVERY[派送订单列表]
FINANCE --> PAYABLE[应付账单]
PAYABLE -.详情下钻.-> BILL_DETAIL[账单明细]
COMPANY --> COMPANY_BASIC[企业基本信息]
COMPANY --> COMPANY_CONTACT[联系信息]
COMPANY --> QUALIFICATION[资质文件]
PROFILE --> BALANCE[账户余额]
PROFILE --> PROFILE_BASIC[基本信息]
PROFILE --> PASSWORD[修改密码]
BALANCE -.操作.-> TOP_UP[充值]
BALANCE -.详情下钻.-> BALANCE_DETAIL[余额明细]
ROOT -.全局操作.-> UTILITIES[全局工具]
UTILITIES --> LANG[多语言:中 / EN / ES]
UTILITIES --> GLOBAL_TOP_UP[充值]
UTILITIES --> BACK_SITE[返回官网]
UTILITIES --> LOGOUT[退出登录]
HOME_PURCHASE -.复用业务页.-> PURCHASE
HOME_TRUCK -.复用业务页.-> TRUCK
HOME_PALLET -.复用业务页.-> PALLET
```
## 二、栏目树
```text
柏悦物流客户端
├─ 业务总览
│ ├─ 快捷下单
│ │ ├─ 海外采购
│ │ ├─ 美国本土卡
│ │ └─ 美本土散板
│ ├─ 账户信息
│ ├─ 数据概览
│ └─ 帮助与支持
│ ├─ 联系客服
│ ├─ 常见问题
│ └─ 获取报价
├─ 头程物流
│ ├─ 头程下单
│ │ ├─ 取货方式
│ │ │ ├─ 自送货-快递
│ │ │ ├─ 我司提货
│ │ │ └─ 自送货-卡车
│ │ ├─ 目的地信息
│ │ │ ├─ FBA仓
│ │ │ ├─ 海外仓
│ │ │ └─ 私人仓
│ │ ├─ 货物明细
│ │ └─ 费用预估
│ ├─ 一键导入
│ └─ 快速下单
├─ 出海下单
│ ├─ 海外采购
│ ├─ 美国本土卡
│ └─ 美本土散板
├─ 海外仓
│ ├─ 贴标申请
│ ├─ 仓储转运单
│ │ ├─ 新增订单
│ │ ├─ 查询订单
│ │ ├─ 订单列表
│ │ ├─ 后台海外仓-尾程订单(提交后联动)
│ │ └─ 后台海外仓-换标单(选择换标时联动)
│ ├─ 货物废弃
│ │ ├─ 新增订单
│ │ ├─ 查询订单
│ │ ├─ 订单列表
│ │ ├─ 相关入库单(选择一张入库单并按明细填写废弃箱数)
│ │ ├─ 后台尾程单(提交后联动)
│ │ ├─ 后台出库单-废弃(提交后联动)
│ │ └─ 后台计费-弃货费(废弃箱数 × 5 USD/箱)
│ └─ 退货换标
│ ├─ 新增订单
│ ├─ 查询订单
│ ├─ 订单列表
│ ├─ 后台海外仓-尾程订单(提交后联动)
│ └─ 后台海外仓-换标单(选择换标时联动)
├─ 订单管理
│ ├─ 入库订单列表
│ └─ 派送订单列表
├─ 财务结算
│ └─ 应付账单
│ └─ 账单明细(详情页)
├─ 公司信息
│ ├─ 企业基本信息
│ ├─ 联系信息
│ └─ 资质文件
├─ 我的信息
│ ├─ 账户余额
│ │ ├─ 充值
│ │ └─ 余额明细
│ ├─ 基本信息
│ └─ 修改密码
└─ 全局工具
├─ 多语言(中 / EN / ES)
├─ 充值
├─ 返回官网
└─ 退出登录
```
## 三、层级说明
| 层级 | 定义 | 示例 |
|---|---|---|
| 一级栏目 | 侧边栏直接入口或业务分组 | 业务总览、头程物流、海外仓、订单管理 |
| 二级栏目 | 侧边栏分组下的可访问页面 | 头程下单、贴标申请、应付账单 |
| 页面模块 | 页面内业务区块或标签,不单独占用侧边栏 | 取货方式、目的地信息、企业基本信息 |
| 跨端联动 | 客户端提交后在后台生成的关联业务单据 | 尾程订单、换标单、废弃出库单、弃货费计费单 |
| 详情页 | 从列表或账户模块下钻进入 | 账单明细、余额明细 |
| 全局工具 | 跨栏目使用的系统级操作 | 多语言、充值、返回官网、退出登录 |
## 四、整理结论
1. 当前客户端共有 **8 个一级栏目**:业务总览、头程物流、出海下单、海外仓、订单管理、财务结算、公司信息、我的信息。
2. 五个折叠业务分组下共有 **13 个二级业务入口**;公司信息、我的信息为一级直达页面,其子项属于页面内标签或功能。
3. 业务总览中的“海外采购、美国本土卡、美本土散板”是快捷入口,复用“出海下单”下的三个业务页面,不应重复计为独立栏目。
4. 账单明细、余额明细属于下钻页面;充值、多语言、返回官网、退出登录属于全局工具,不应与主栏目并列统计。
5. 仓储转运单提交后与后台 0805 版本“海外仓-尾程订单”一一关联;选择换标时,再与“海外仓-换标单”一一关联,后台单据不重复计为客户端栏目。
6. 货物废弃单关联一张入库单并按明细填写废弃箱数;提交后一次生成尾程单、废弃出库单和弃货费计费单,弃货费固定为 5 USD/箱。
7. 退货入库、退货换标和退货出库已整合为客户端“退货换标”工作台,支持新增、查询、列表和详情;提交始终生成尾程订单,选择换标时额外生成后台换标单。
-455
View File
@@ -1,455 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>虫虫出海 - 用户登录</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', sans-serif;
display: flex;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
}
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* ===== 左侧品牌区 ===== */
.brand-side {
width: 50%;
min-width: 420px;
position: relative;
background: linear-gradient(155deg, rgba(30,64,175,0.85) 0%, rgba(37,99,235,0.75) 40%, rgba(59,130,246,0.65) 100%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
}
/* 浮动装饰圆圈 */
.brand-side::before {
content: '';
position: absolute;
top: -20%; right: -20%;
width: 80%; height: 80%;
background: radial-gradient(ellipse, rgba(255,255,255,.2) 0%, transparent 70%);
animation: float1 8s ease-in-out infinite;
pointer-events: none;
}
.brand-side::after {
content: '';
position: absolute;
bottom: -15%; left: -15%;
width: 70%; height: 70%;
background: radial-gradient(ellipse, rgba(147,197,253,.3) 0%, transparent 65%);
animation: float2 10s ease-in-out infinite;
pointer-events: none;
}
@keyframes float1 {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(-30px, 30px) scale(1.05); }
}
@keyframes float2 {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(20px, -20px) scale(1.1); }
}
.brand-content {
position: relative;
z-index: 1;
text-align: center;
padding: 40px;
animation: fadeInUp 0.8s ease-out;
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.logo-wrap {
width: 100px; height: 100px;
margin: 0 auto 24px;
border-radius: 26px;
overflow: hidden;
background: rgba(255,255,255,.95);
padding: 12px;
box-shadow: 0 12px 40px rgba(0,0,0,.15), 0 0 0 1px rgba(255,255,255,.5) inset;
animation: logoFloat 3s ease-in-out infinite;
}
@keyframes logoFloat {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-8px); }
}
.logo-wrap img { width: 100%; height: 100%; object-fit: contain; }
.brand-title { font-size: 36px; font-weight: 800; color: #fff; letter-spacing: 4px; text-shadow: 0 4px 20px rgba(0,0,0,.15); }
.brand-subtitle { font-size: 15px; color: rgba(255,255,255,.88); margin-top: 12px; letter-spacing: 2px; }
.feature-list { margin-top: 56px; text-align: left; display: inline-block; }
.feature-item {
color: rgba(255,255,255,.9);
font-size: 15px;
line-height: 2.6;
padding-left: 24px;
position: relative;
opacity: 0;
animation: fadeInLeft 0.6s ease-out forwards;
}
.feature-item:nth-child(1) { animation-delay: 0.2s; }
.feature-item:nth-child(2) { animation-delay: 0.4s; }
.feature-item:nth-child(3) { animation-delay: 0.6s; }
@keyframes fadeInLeft {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
.feature-item::before {
content: '✓';
position: absolute;
left: 0; top: 2px;
width: 18px; height: 18px;
border-radius: 50%;
background: rgba(255,255,255,.25);
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
}
/* ===== 右侧登录区 ===== */
.login-side {
flex: 1;
min-width: 400px;
display: flex;
align-items: center;
justify-content: center;
padding: 40px;
}
.login-card {
width: 100%;
max-width: 420px;
padding: 52px 44px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 24px;
box-shadow: 0 25px 80px rgba(0,0,0,.15), 0 0 0 1px rgba(255,255,255,.5) inset;
animation: cardFloat 0.8s ease-out;
}
@keyframes cardFloat {
from { opacity: 0; transform: translateY(40px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* 标题区 */
.card-header { margin-bottom: 36px; }
.card-title {
font-size: 30px;
font-weight: 800;
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: 1px;
}
.card-desc {
font-size: 15px;
color: #64748b;
margin-top: 10px;
}
/* 表单 */
.form-group { margin-bottom: 22px; }
.form-input {
width: 100%;
height: 52px;
border: 2px solid #e2e8f0;
border-radius: 14px;
padding: 0 18px;
font-size: 15px;
color: #1e293b;
outline: none;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
background: #f8fafc;
}
.form-input:focus {
border-color: #667eea;
background: #fff;
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
transform: translateY(-2px);
}
.form-input::placeholder { color: #94a3b8; }
/* 密码框 */
.password-wrap { position: relative; }
.password-toggle {
position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
border: none; background: none; cursor: pointer;
padding: 6px; color: #64748b; font-size: 18px; line-height: 1;
transition: all .2s;
}
.password-toggle:hover { color: #667eea; transform: translateY(-50%) scale(1.1); }
/* 验证码 */
.captcha-row {
display: flex;
gap: 14px;
align-items: center;
}
.captcha-row .form-input { flex: 1; }
.captcha-img {
width: 120px; height: 52px;
border: 2px solid #e2e8f0;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
user-select: none;
font-size: 22px;
font-weight: 700;
letter-spacing: 6px;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
flex-shrink: 0;
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
}
.captcha-img:hover {
border-color: #667eea;
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
/* 按钮 */
.submit-btn {
width: 100%;
height: 54px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
background-size: 200% 200%;
color: #fff;
border: none;
border-radius: 14px;
font-size: 16px;
font-weight: 700;
cursor: pointer;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
letter-spacing: 4px;
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
}
.submit-btn:hover {
background-position: 100% 0;
transform: translateY(-3px);
box-shadow: 0 12px 32px rgba(102, 126, 234, 0.5);
}
.submit-btn:active { transform: translateY(-1px); }
/* 底部辅助行 */
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 28px;
}
.remember-me {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #64748b;
cursor: pointer;
user-select: none;
transition: color .2s;
}
.remember-me:hover { color: #475569; }
.remember-me input { accent-color: #667eea; width: 18px; height: 18px; cursor: pointer; }
.forgot-link {
font-size: 14px;
color: #667eea;
text-decoration: none;
font-weight: 500;
transition: all .2s;
}
.forgot-link:hover { color: #764ba2; text-decoration: underline; }
/* 底部注册链接 */
.register-tip {
text-align: center;
margin-top: 28px;
font-size: 14px;
color: #94a3b8;
}
.register-tip a {
color: #667eea;
text-decoration: none;
font-weight: 600;
transition: all .2s;
}
.register-tip a:hover { color: #764ba2; text-decoration: underline; }
/* 错误提示 */
.error-msg {
font-size: 13px;
color: #ef4444;
margin-top: 6px;
display: none;
padding-left: 4px;
animation: shake 0.3s ease-out;
}
.error-msg.show { display: block; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-4px); }
75% { transform: translateX(4px); }
}
/* 响应式 */
@media (max-width: 900px) {
body { flex-direction: column; }
.brand-side { min-width: auto; min-height: 240px; width: 100%; }
.brand-content { padding: 32px; }
.feature-list { display: none; }
.login-side { min-width: auto; padding: 24px 16px; }
.login-card { max-width: 420px; padding: 40px 28px; }
}
</style>
</head>
<body>
<!-- 左侧品牌 -->
<div class="brand-side">
<div class="brand-content">
<div class="logo-wrap">
<img src="logo.png" alt="Logo">
</div>
<h1 class="brand-title">虫虫出海</h1>
<p class="brand-subtitle">一站式跨境物流服务平台</p>
<div class="feature-list">
<div class="feature-item">安全可靠 · 全程可追踪</div>
<div class="feature-item">专业团队 · 快速响应</div>
<div class="feature-item">透明报价 · 无隐藏费用</div>
</div>
</div>
</div>
<!-- 右侧登录 -->
<div class="login-side">
<div class="login-card">
<div class="card-header">
<h2 class="card-title">欢迎回来</h2>
<p class="card-desc">请登录您的账户以继续</p>
</div>
<form id="loginForm" autocomplete="off">
<div class="form-group">
<input type="text" class="form-input" id="loginUsername" placeholder="用户名">
<div class="error-msg" id="loginUserError">请输入用户名</div>
</div>
<div class="form-group">
<div class="password-wrap">
<input type="password" class="form-input" id="loginPassword" placeholder="密码">
<button type="button" class="password-toggle" onclick="togglePassword()">👁</button>
</div>
<div class="error-msg" id="loginPassError">请输入密码</div>
</div>
<div class="form-group">
<div class="captcha-row">
<input type="text" class="form-input" id="loginCaptcha" placeholder="验证码" maxlength="4">
<div class="captcha-img" id="loginCaptchaImg" onclick="refreshCaptcha('login')"></div>
</div>
<div class="error-msg" id="loginCaptchaError">验证码错误</div>
</div>
<div class="form-options">
<label class="remember-me">
<input type="checkbox"> 记住我
</label>
<a href="javascript:void(0)" class="forgot-link">忘记密码?</a>
</div>
<button type="submit" class="submit-btn">登 录</button>
</form>
<div class="register-tip">没有账号?<a href="register.html">立即注册</a></div>
</div>
</div>
<script>
// 验证码
let currentCaptcha = {};
function generateCaptcha() {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
let c = '';
for (let i = 0; i < 4; i++) c += chars.charAt(Math.floor(Math.random() * chars.length));
return c;
}
function refreshCaptcha(type) {
const c = generateCaptcha();
currentCaptcha[type] = c;
const el = document.getElementById(type + 'CaptchaImg');
if (!el) return;
el.textContent = c;
el.style.transform = 'rotate(' + (Math.random() * 8 - 4) + 'deg)';
el.style.color = ['#2563eb','#3b82f6','#1d4ed8','#1e40af'][Math.floor(Math.random()*4)];
}
refreshCaptcha('login');
// 密码显隐
function togglePassword() {
const pw = document.getElementById('loginPassword');
pw.type = pw.type === 'password' ? 'text' : 'password';
}
// 表单提交
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
var username = document.getElementById('loginUsername').value.trim();
var password = document.getElementById('loginPassword').value;
var captcha = document.getElementById('loginCaptcha').value.trim();
document.querySelectorAll('#loginForm .error-msg').forEach(function(el) { el.classList.remove('show'); });
var hasError = false;
if (!username) { document.getElementById('loginUserError').classList.add('show'); hasError = true; }
if (!password) { document.getElementById('loginPassError').classList.add('show'); hasError = true; }
if (captcha.toUpperCase() !== currentCaptcha['login'].toUpperCase()) {
document.getElementById('loginCaptchaError').classList.add('show');
refreshCaptcha('login');
hasError = true;
}
if (hasError) return;
alert('登录成功!欢迎回来,' + username + '!');
});
// 实时清除错误
document.querySelectorAll('.form-input').forEach(function(input) {
input.addEventListener('input', function() {
var group = this.closest('.form-group');
if (!group) return;
var err = group.querySelector('.error-msg');
if (err) err.classList.remove('show');
});
});
</script>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 764 KiB

-511
View File
@@ -1,511 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>虫虫出海 - 企业注册</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
position: relative;
overflow-x: hidden;
}
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* 装饰性浮动元素 */
body::before {
content: '';
position: fixed;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 60%);
animation: rotate 30s linear infinite;
pointer-events: none;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ====== 主容器:居中卡片式 ====== */
.container {
width: 100%;
max-width: 580px;
position: relative;
z-index: 1;
animation: fadeInUp 0.8s ease-out;
}
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
/* 顶部品牌条 */
.top-bar {
text-align: center;
margin-bottom: 32px;
}
.logo-wrap {
width: 120px; height: 120px;
margin: 0 auto 18px;
border-radius: 32px;
background: rgba(255,255,255,0.95);
padding: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,.2), 0 0 0 1px rgba(255,255,255,.5) inset;
overflow: hidden;
animation: logoFloat 3s ease-in-out infinite;
}
@keyframes logoFloat {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.logo-wrap img { width: 100%; height: 100%; object-fit: contain; filter: none; }
.logo-wrap img.no-filter { filter: none; }
.brand-name {
font-size: 30px;
font-weight: 800;
color: #fff;
letter-spacing: 3px;
margin-bottom: 6px;
text-shadow: 0 4px 20px rgba(0,0,0,.2);
}
.brand-sub { font-size: 14px; color: rgba(255,255,255,0.85); letter-spacing: 1.5px; }
/* 卡片 */
.form-card {
background: rgba(255,255,255,0.96);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 28px;
padding: 44px 40px;
box-shadow: 0 25px 80px rgba(0,0,0,.15), 0 0 0 1px rgba(255,255,255,.5) inset;
border: none;
}
/* 标题行 */
.card-header {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 30px;
}
.card-title {
font-size: 24px; font-weight: 800;
background: linear-gradient(135deg, #1e293b 0%, #475569 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
position: relative;
padding-left: 16px;
}
.card-title::before {
content: '';
position: absolute; left: 0; top: 2px; bottom: 2px;
width: 4px; border-radius: 3px;
background: linear-gradient(180deg, #667eea, #764ba2);
}
.back-link {
font-size: 14px; color: #64748b; text-decoration: none;
transition: all .2s;
font-weight: 500;
padding: 6px 12px;
border-radius: 8px;
}
.back-link:hover { color: #667eea; background: rgba(102,126,234,0.08); }
.back-link::before { content: '← '; font-size: 14px; }
/* 分组标题 */
.section-label {
font-size: 12px; font-weight: 700; color: #94a3b8;
text-transform: uppercase; letter-spacing: 2.5px;
margin-bottom: 16px; margin-top: 24px;
}
.section-label:first-of-type { margin-top: 0; }
/* 表单项 - 单列 */
.form-group { margin-bottom: 20px; position: relative; }
.form-label {
display: block; font-size: 14px; font-weight: 600;
color: #475569; margin-bottom: 8px;
}
.form-label .required { color: #ef4444; margin-right: 3px; font-size: 15px; }
.form-input, .form-select {
width: 100%; padding: 14px 18px; font-size: 15px;
border: 2px solid #e2e8f0; border-radius: 14px; outline: none;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
background: #f8fafc; color: #1e293b;
appearance: none;
height: 54px;
}
.form-input:hover, .form-select:hover {
border-color: #cbd5e1;
background: #fff;
}
.form-input:focus, .form-select:focus {
border-color: #667eea;
background: #fff;
box-shadow: 0 0 0 5px rgba(102,126,234,.12);
transform: translateY(-2px);
}
.form-input::placeholder { color: #94a3b8; }
.form-select {
cursor: pointer;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat; background-position: right 18px center; padding-right: 42px;
}
/* 营业执照上传区 */
.license-upload-area {
border: 2px dashed #cbd5e1;
border-radius: 16px; padding: 28px;
text-align: center; cursor: pointer;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
background: linear-gradient(135deg, #fafbfc 0%, #f8fafc 100%);
position: relative;
overflow: hidden;
}
.license-upload-area:hover {
border-color: #667eea;
background: linear-gradient(135deg, #eff6ff 0%, #f0f9ff 100%);
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(102,126,234,0.15);
}
.license-upload-area.has-image {
border-style: solid; border-color: #a78bfa; padding: 6px; background: #fff;
}
.license-upload-area.has-image .upload-placeholder { display: none; }
.upload-placeholder .upload-icon-big {
font-size: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1;
margin-bottom: 8px;
}
.upload-placeholder .upload-hint {
font-size: 14px; color: #64748b; font-weight: 600;
}
.upload-placeholder .upload-desc {
font-size: 12px; color: #94a3b8; margin-top: 6px;
}
.license-preview-img {
width: 100%; max-height: 180px; object-fit: cover;
border-radius: 12px; display: none;
}
.has-image .license-preview-img { display: block; }
.license-upload-area input[type="file"] { display: none; }
/* 验证码 */
.captcha-group { display: flex; gap: 14px; align-items: stretch; }
.captcha-group .form-input { flex: 1; }
.captcha-img {
flex-shrink: 0; width: 130px;
height: auto; min-height: 54px;
border-radius: 14px; cursor: pointer;
border: 2px solid #e2e8f0;
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
display: flex; align-items: center; justify-content: center;
padding: 0 16px; font-size: 22px; font-weight: 800;
letter-spacing: 6px; color: #667eea; user-select: none;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
}
.captcha-img:hover {
border-color: #667eea;
background: linear-gradient(135deg, #dbeafe 0%, #cffafe 100%);
transform: scale(1.05);
box-shadow: 0 6px 20px rgba(102,126,234,0.2);
}
/* 错误提示 */
.error-msg {
color: #ef4444;
font-size: 13px;
margin-top: 6px;
display: none;
padding-left: 4px;
animation: shake 0.3s ease-out;
}
.error-msg.show { display: block; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-4px); }
75% { transform: translateX(4px); }
}
/* 提交按钮 */
.submit-btn {
width: 100%; padding: 16px; font-size: 16px; font-weight: 700;
color: white;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
background-size: 200% 200%;
border: none; border-radius: 14px;
cursor: pointer;
transition: all .3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 10px 30px rgba(102,126,234,.4);
letter-spacing: 1.5px;
margin-top: 10px;
}
.submit-btn:hover {
background-position: 100% 0;
transform: translateY(-3px);
box-shadow: 0 16px 40px rgba(102,126,234,.5);
}
.submit-btn:active { transform: translateY(-1px); }
/* 底部链接 */
.form-footer { text-align: center; margin-top: 22px; font-size: 14px; color: #64748b; }
.form-footer a {
color: #667eea;
text-decoration: none;
font-weight: 700;
transition: all .2s;
}
.form-footer a:hover { color: #764ba2; text-decoration: underline; }
/* 分割线装饰 */
.divider {
height: 1px;
background: linear-gradient(90deg, transparent, #e2e8f0, #cbd5e1, #e2e8f0, transparent);
margin: 26px 0;
}
@keyframes cardShake {
0%,100%{transform:translateX(0)} 25%{transform:translateX(-8px)} 75%{transform:translateX(8px)}
}
.shake { animation: cardShake .45s; }
/* 响应式 */
@media (max-width: 580px) {
body { padding: 24px 16px; }
.form-card { padding: 32px 26px; border-radius: 22px; }
.brand-name { font-size: 26px; }
.logo-wrap { width: 100px; height: 100px; border-radius: 26px; padding: 16px; }
.captcha-group { flex-direction: column; }
.captcha-img { width: 100%; min-height: 52px; }
}
</style>
</head>
<body>
<div class="container">
<!-- 品牌头部 -->
<div class="top-bar">
<div class="logo-wrap">
<img src="logo.png" alt="虫虫出海" class="no-filter">
</div>
<h1 class="brand-name">虫虫出海</h1>
<p class="brand-sub">一站式跨境物流服务平台</p>
</div>
<!-- 注册表单 -->
<div class="form-card">
<div class="card-header">
<h2 class="card-title">企业注册</h2>
<a href="login-register.html" class="back-link">返回登录</a>
</div>
<form id="registerForm">
<!-- 基本信息 -->
<div class="section-label">基本信息</div>
<div class="form-group">
<label class="form-label"><span class="required">*</span>公司名称</label>
<input type="text" class="form-input" id="regCompany" placeholder="请输入完整公司名称" required autocomplete="organization">
<div class="error-msg" id="regCompanyError">请输入公司名称</div>
</div>
<div class="form-group">
<label class="form-label"><span class="required">*</span>法人姓名</label>
<input type="text" class="form-input" id="regLegalPerson" placeholder="法人代表姓名" required>
<div class="error-msg" id="regLegalPersonError">请输入法人姓名</div>
</div>
<div class="form-group">
<label class="form-label"><span class="required">*</span>法人电话</label>
<input type="tel" class="form-input" id="regLegalPhone" placeholder="用于业务联系的手机号码" required>
<div class="error-msg" id="regLegalPhoneError">请输入法人电话</div>
</div>
<div class="divider"></div>
<!-- 证件信息 -->
<div class="section-label">证件信息</div>
<div class="form-group">
<label class="form-label"><span class="required">*</span>营业执照 / 证件照</label>
<div class="license-upload-area" id="licenseUploadArea" onclick="document.getElementById('regLicenseFile').click()">
<div class="upload-placeholder">
<div class="upload-icon-big">📄</div>
<div class="upload-hint">点击上传证件图片</div>
<div class="upload-desc">支持 JPG、PNG 格式,不超过 5MB</div>
</div>
<img class="license-preview-img" id="licensePreview" alt="">
<input type="file" id="regLicenseFile" accept="image/*" onchange="handleLicenseUpload(this)">
</div>
<div class="error-msg" id="regLicenseError">请上传营业执照或相关证件</div>
</div>
<div class="form-group">
<label class="form-label">法人身份证号</label>
<input type="text" class="form-input" id="regIdCard" placeholder="18位身份证号">
</div>
<div class="divider"></div>
<!-- 地址与发票 -->
<div class="section-label">地址与发票</div>
<div class="form-group">
<label class="form-label">国家/地区</label>
<select class="form-select" id="regCountry">
<option value="中国">中国</option>
<option value="中国香港">中国香港</option>
<option value="中国台湾">中国台湾</option>
<option value="中国澳门">中国澳门</option>
<option value="美国">美国</option>
<option value="英国">英国</option>
<option value="德国">德国</option>
<option value="法国">法国</option>
<option value="日本">日本</option>
<option value="韩国">韩国</option>
<option value="澳大利亚">澳大利亚</option>
<option value="加拿大">加拿大</option>
<option value="其他">其他</option>
</select>
</div>
<div class="form-group">
<label class="form-label">详细地址</label>
<input type="text" class="form-input" id="regAddress" placeholder="公司注册地址或经营地址">
</div>
<div class="form-group">
<label class="form-label">发票抬头</label>
<input type="text" class="form-input" id="regInvoiceTitle" placeholder="用于开具发票的名称(选填)">
</div>
<div class="divider"></div>
<!-- 验证码 -->
<div class="form-group">
<label class="form-label"><span class="required">*</span>验证码</label>
<div class="captcha-group">
<input type="text" class="form-input" id="regCaptcha" placeholder="输入下方验证码" required maxlength="4">
<div class="captcha-img" id="regCaptchaImg" onclick="refreshCaptcha()" title="点击刷新验证码"></div>
</div>
<div class="error-msg" id="regCaptchaError">验证码错误,请重新输入</div>
</div>
<button type="submit" class="submit-btn">提交注册申请</button>
<div class="form-footer">
已有账号?<a href="login-register.html">立即登录 →</a>
</div>
</form>
</div>
</div>
<script>
let currentCaptcha = '';
let licenseUploaded = false;
function generateCaptcha() {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
let c = '';
for (let i = 0; i < 4; i++) c += chars.charAt(Math.floor(Math.random() * chars.length));
return c;
}
function refreshCaptcha() {
currentCaptcha = generateCaptcha();
const img = document.getElementById('regCaptchaImg');
img.textContent = currentCaptcha;
img.style.transform = 'rotate(' + (Math.random() * 8 - 4) + 'deg)';
img.style.color = ['#667eea','#764ba2','#818cf8','#a78bfa'][Math.floor(Math.random()*4)];
}
refreshCaptcha();
// 营业执照上传
function handleLicenseUpload(input) {
var file = input.files[0];
if (!file) return;
if (file.size > 5*1024*1024) { alert('图片大小不能超过 5MB'); input.value=''; return; }
var reader = new FileReader();
reader.onload = function(e) {
var preview = document.getElementById('licensePreview');
preview.src = e.target.result;
var area = document.getElementById('licenseUploadArea');
area.classList.add('has-image');
licenseUploaded = true;
document.getElementById('regLicenseError').classList.remove('show');
};
reader.readAsDataURL(file);
}
// 表单提交
document.getElementById('registerForm').addEventListener('submit', function(e) {
e.preventDefault();
var company = document.getElementById('regCompany').value.trim();
var legalPerson = document.getElementById('regLegalPerson').value.trim();
var legalPhone = document.getElementById('regLegalPhone').value.trim();
var captcha = document.getElementById('regCaptcha').value.trim();
document.querySelectorAll('.error-msg').forEach(function(el){ el.classList.remove('show'); });
var hasError = false;
if (!company) { document.getElementById('regCompanyError').classList.add('show'); hasError=true; }
if (!legalPerson) { document.getElementById('regLegalPersonError').classList.add('show'); hasError=true; }
if (!legalPhone) { document.getElementById('regLegalPhoneError').classList.add('show'); hasError=true; }
if (!licenseUploaded) { document.getElementById('regLicenseError').classList.add('show'); hasError=true; }
if (captcha.toUpperCase() !== currentCaptcha.toUpperCase()) {
document.getElementById('regCaptchaError').classList.add('show');
refreshCaptcha(); hasError=true;
}
if (hasError) {
this.querySelector('.submit-btn').classList.add('shake');
setTimeout(() => this.querySelector('.submit-btn').classList.remove('shake'), 450);
return;
}
alert('注册申请已提交!\n\n公司名称:' + company +
'\n\n我们将尽快审核您的资料。');
window.location.href = 'login-register.html';
});
// 实时清除错误
document.querySelectorAll('.form-input,.form-select').forEach(function(input){
input.addEventListener('input', function(){
var group = this.closest('.form-group');
if (group) { var err = group.querySelector('.error-msg'); if (err) err.classList.remove('show'); }
});
});
</script>
</body>
</html>
@@ -0,0 +1 @@
[ 630ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8877/favicon.ico:0
@@ -0,0 +1 @@
[ 131ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8877/favicon.ico:0
@@ -0,0 +1 @@
[ 168ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8877/favicon.ico:0
@@ -0,0 +1,19 @@
[ 1593ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1593ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1593ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1593ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1593ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1593ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1615ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
[ 467779ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 467779ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 467779ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 467779ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 467779ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 467779ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 860532ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 860532ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 860532ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 860532ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 860532ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 860532ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,14 @@
[ 1391ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1391ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1391ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1391ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1391ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1391ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1415ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
[ 1801401ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1801401ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1801401ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1801401ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1801401ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1801401ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 5777223ms] [WARNING] cdn.tailwindcss.com should not be used in production. To use Tailwind CSS in production, install it as a PostCSS plugin or use the Tailwind CLI: https://tailwindcss.com/docs/installation @ https://cdn.tailwindcss.com/:63
@@ -0,0 +1,7 @@
[ 1679ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1679ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1679ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1679ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1679ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1679ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1701ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
@@ -0,0 +1,36 @@
[ 68874ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 68874ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 68874ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 68874ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 68874ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 68874ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 244989ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 244990ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 244990ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 244990ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 244990ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 244990ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 291939ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 291939ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 291939ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 291939ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 291940ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 291940ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 409949ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 409950ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 409950ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 409950ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 409950ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 409950ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 456975ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 456976ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 456976ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 456976ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 456976ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 456976ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 490715ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 490715ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 490715ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 490715ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 490715ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 490715ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,13 @@
[ 1761ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1762ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1762ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1762ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1762ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1762ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1978ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
[ 3265ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 3265ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 3265ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 3265ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 3265ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 3265ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,19 @@
[ 37479ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 37479ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 37479ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 37479ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 37479ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 37479ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 38118ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
[ 41061ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 41062ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 41062ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 41062ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 41062ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 41062ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 171042ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 171042ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 171043ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 171043ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 171043ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 171043ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,13 @@
[ 65503ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 65504ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 65504ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 65504ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 65504ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 65504ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 65524ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
[ 67595ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 67595ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 67595ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 67595ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 67596ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 67596ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,13 @@
[ 1667ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1668ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1668ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1668ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1668ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1668ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1683ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
[ 977469ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 977470ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 977470ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 977470ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 977470ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 977470ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,13 @@
[ 1032ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1032ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1032ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1032ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1032ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1032ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1059ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
[ 517481ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 517482ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 517482ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 517482ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 517482ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 517482ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,7 @@
[ 1188ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1188ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1188ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1188ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1188ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1188ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 3135ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8765/favicon.ico:0
@@ -0,0 +1,7 @@
[ 1711ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1712ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1712ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1712ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1712ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1712ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 1727ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://127.0.0.1:8877/favicon.ico:0
@@ -0,0 +1,6 @@
[ 2056ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2056ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2056ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2056ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2057ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2057ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8877/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,6 @@
[ 771ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 771ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 771ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 771ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 771ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 771ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8765/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,12 @@
[ 2284ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2284ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2284ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2284ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2285ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 2285ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 726289ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 726290ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 726290ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 726290ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 726290ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 726290ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8891/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,6 @@
[ 882ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8771/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 882ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8771/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 882ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) @ http://127.0.0.1:8771/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 882ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8771/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 882ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8771/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
[ 882ms] [VERBOSE] [DOM] Password field is not contained in a form: (More info: https://goo.gl/9p2vKq) %o @ http://127.0.0.1:8771/%E8%B4%A7%E4%BB%A3SaaS%E5%B9%B3%E5%8F%B0%E5%8E%9F%E5%9E%8B.html:0
@@ -0,0 +1,123 @@
- main [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]:
- heading "海外采购单业务流程图" [level=1] [ref=e5]
- generic [ref=e6]: 依据:0805 版货代 SaaS 平台原型 · 海外业务 / 采购订单
- generic [ref=e7]: 原型事实 + 业务补全建议
- region "图例" [ref=e8]:
- generic [ref=e9]: 原型事实
- generic [ref=e11]: 业务补全建议
- generic [ref=e13]: 资料退回 / 到货差异
- generic [ref=e15]: 状态:待审核 → 已确认 → 采购中 → 已完成
- generic "双泳道图,可横向滚动查看" [ref=e16]:
- generic [ref=e17]:
- region [ref=e18]:
- generic [ref=e19]:
- heading "流程图 A:原型事实泳道图" [level=2] [ref=e20]
- generic [ref=e21]: 2 条泳道
- generic [ref=e22]:
- generic [ref=e23]:
- generic [ref=e24]: 录入
- generic [ref=e25]: 提交
- generic [ref=e26]: 校验
- generic [ref=e27]: 建单
- generic [ref=e28]: 确认
- generic [ref=e29]: 执行
- generic [ref=e30]: 完成
- generic [ref=e31]:
- generic [ref=e32]:
- generic [ref=e33]: 后台操作员
- generic [ref=e34]: 进入新增页填写采购信息 →
- generic [ref=e36]: 提交订单 ↘
- generic [ref=e38]: 编辑页手工切换状态 ↘
- generic [ref=e40]: 编辑页手工切换状态 ↘
- generic [ref=e42]: 筛选 / 详情 / 编辑
- generic [ref=e44]:
- generic [ref=e45]: 系统
- generic [ref=e47]:
- text: 必填校验
- generic [ref=e48]: 失败提示并返回表单
- text: →
- generic [ref=e49]: 生成 PO、预算总价状态:待审核 →
- generic [ref=e51]: 状态:已确认 →
- generic [ref=e53]: 状态:采购中 →
- generic [ref=e55]: 状态:已完成
- generic [ref=e57]: 原型没有独立审核、驳回和验收操作。四个状态由“编辑采购订单”页下拉框手工切换;新增页校验采购品类、采购数量、联系人和联系电话。
- region [ref=e58]:
- generic [ref=e59]:
- heading "流程图 B:业务补全建议泳道图" [level=2] [ref=e60]
- generic [ref=e61]: 6 条泳道
- generic [ref=e62]:
- generic [ref=e63]:
- generic [ref=e64]: 需求
- generic [ref=e65]: 初审
- generic [ref=e66]: 审核
- generic [ref=e67]: 确认
- generic [ref=e68]: 下单
- generic [ref=e69]: 发货
- generic [ref=e70]: 跟踪
- generic [ref=e71]: 验收
- generic [ref=e72]: 归档
- generic [ref=e73]:
- generic [ref=e74]:
- generic [ref=e75]: 客户
- generic [ref=e77]:
- text: 提交采购需求
- generic [ref=e78]: 链接 / 规格 / 数量 / 预算
- text: ↘
- generic [ref=e80]:
- text: 补充或修正资料
- generic [ref=e81]: 资料退回后重提
- text: ↗
- generic [ref=e82]:
- generic [ref=e83]: 客服
- generic [ref=e84]: 初审资料创建内部采购单 ↘
- generic [ref=e86]: 协同处理到货差异 ↘
- generic [ref=e88]:
- generic [ref=e89]: 海外采购
- generic [ref=e91]:
- text: 审核资料与预算
- generic [ref=e92]: 不通过:资料退回
- text: →
- generic [ref=e93]: 确认采购方案 →
- generic [ref=e95]: 向供应商下单 ↘
- generic [ref=e97]: 跟踪物流与异常 ↘
- generic [ref=e99]: 确认费用并归档
- generic [ref=e101]:
- generic [ref=e102]: 供应商与物流
- generic [ref=e104]:
- text: 备货发货
- generic [ref=e105]: 回传中美快递单号与 ETA
- text: ↘
- generic [ref=e106]: 补发 / 退款 / 换货 ↗
- generic [ref=e108]:
- generic [ref=e109]: 收货仓
- generic [ref=e111]:
- text: 到货验收
- generic [ref=e112]: 数量 / 规格 / 外观
- generic [ref=e113]: 不一致:登记到货差异
- text: ↗
- generic [ref=e114]:
- generic [ref=e115]: 系统
- generic [ref=e116]: 生成 PO状态:待审核 →
- generic [ref=e118]: 状态:已确认 →
- generic [ref=e120]: 状态:采购中 →
- generic [ref=e123]:
- text: 状态:已完成
- generic [ref=e124]: 记录全过程日志
- generic [ref=e125]: 建议新增审核记录、供应商和采购凭证、实际采购金额、物流轨迹、到货验收、到货差异及异常处理字段。以上节点均为业务补全建议。
- generic [ref=e126]:
- article [ref=e127]:
- heading "A 图原型字段" [level=3] [ref=e128]
- list [ref=e129]:
- listitem [ref=e130]: 采购品类、商品名称、商品规格、商品链接、采购数量。
- listitem [ref=e131]: 预算单价、预算总价、收货地址、操作费。
- listitem [ref=e132]: 中国快递单号、美国快递单号、预计到达时间。
- listitem [ref=e133]: 联系人、联系电话、订单备注。
- article [ref=e134]:
- heading "B 图建议控制点" [level=3] [ref=e135]
- list [ref=e136]:
- listitem [ref=e137]: 审核不通过必须记录资料退回原因、审核人和时间。
- listitem [ref=e138]: 采购中必须关联供应商订单、采购凭证和物流轨迹。
- listitem [ref=e139]: 到货差异关闭前不得进入已完成。
- listitem [ref=e140]: 验收通过后确认实际费用并归档。
@@ -0,0 +1,123 @@
- main [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]:
- heading "海外采购单业务流程图" [level=1] [ref=e5]
- generic [ref=e6]: 依据:0805 版货代 SaaS 平台原型 · 海外业务 / 采购订单
- generic [ref=e7]: 原型事实 + 业务补全建议
- region "图例" [ref=e8]:
- generic [ref=e9]: 原型事实
- generic [ref=e11]: 业务补全建议
- generic [ref=e13]: 资料退回 / 到货差异
- generic [ref=e15]: 状态:待审核 → 已确认 → 采购中 → 已完成
- generic "双泳道图,可横向滚动查看" [ref=e16]:
- generic [ref=e17]:
- region [ref=e18]:
- generic [ref=e19]:
- heading "流程图 A:原型事实泳道图" [level=2] [ref=e20]
- generic [ref=e21]: 2 条泳道
- generic [ref=e22]:
- generic [ref=e23]:
- generic [ref=e24]: 录入
- generic [ref=e25]: 提交
- generic [ref=e26]: 校验
- generic [ref=e27]: 建单
- generic [ref=e28]: 确认
- generic [ref=e29]: 执行
- generic [ref=e30]: 完成
- generic [ref=e31]:
- generic [ref=e32]:
- generic [ref=e33]: 后台操作员
- generic [ref=e34]: 进入新增页填写采购信息 →
- generic [ref=e36]: 提交订单 ↘
- generic [ref=e38]: 编辑页手工切换状态 ↘
- generic [ref=e40]: 编辑页手工切换状态 ↘
- generic [ref=e42]: 筛选 / 详情 / 编辑
- generic [ref=e44]:
- generic [ref=e45]: 系统
- generic [ref=e47]:
- text: 必填校验
- generic [ref=e48]: 失败提示并返回表单
- text: →
- generic [ref=e49]: 生成 PO、预算总价状态:待审核 →
- generic [ref=e51]: 状态:已确认 →
- generic [ref=e53]: 状态:采购中 →
- generic [ref=e55]: 状态:已完成
- generic [ref=e57]: 原型没有独立审核、驳回和验收操作。四个状态由“编辑采购订单”页下拉框手工切换;新增页校验采购品类、采购数量、联系人和联系电话。
- region [ref=e58]:
- generic [ref=e59]:
- heading "流程图 B:业务补全建议泳道图" [level=2] [ref=e60]
- generic [ref=e61]: 6 条泳道
- generic [ref=e62]:
- generic [ref=e63]:
- generic [ref=e64]: 需求
- generic [ref=e65]: 初审
- generic [ref=e66]: 审核
- generic [ref=e67]: 确认
- generic [ref=e68]: 下单
- generic [ref=e69]: 发货
- generic [ref=e70]: 跟踪
- generic [ref=e71]: 验收
- generic [ref=e72]: 归档
- generic [ref=e73]:
- generic [ref=e74]:
- generic [ref=e75]: 客户
- generic [ref=e77]:
- text: 提交采购需求
- generic [ref=e78]: 链接 / 规格 / 数量 / 预算
- text: ↘
- generic [ref=e80]:
- text: 补充或修正资料
- generic [ref=e81]: 资料退回后重提
- text: ↗
- generic [ref=e82]:
- generic [ref=e83]: 客服
- generic [ref=e84]: 初审资料创建内部采购单 ↘
- generic [ref=e86]: 协同处理到货差异 ↘
- generic [ref=e88]:
- generic [ref=e89]: 海外采购
- generic [ref=e91]:
- text: 审核资料与预算
- generic [ref=e92]: 不通过:资料退回
- text: →
- generic [ref=e93]: 确认采购方案 →
- generic [ref=e95]: 向供应商下单 ↘
- generic [ref=e97]: 跟踪物流与异常 ↘
- generic [ref=e99]: 确认费用并归档
- generic [ref=e101]:
- generic [ref=e102]: 供应商与物流
- generic [ref=e104]:
- text: 备货发货
- generic [ref=e105]: 回传中美快递单号与 ETA
- text: ↘
- generic [ref=e106]: 补发 / 退款 / 换货 ↗
- generic [ref=e108]:
- generic [ref=e109]: 收货仓
- generic [ref=e111]:
- text: 到货验收
- generic [ref=e112]: 数量 / 规格 / 外观
- generic [ref=e113]: 不一致:登记到货差异
- text: ↗
- generic [ref=e114]:
- generic [ref=e115]: 系统
- generic [ref=e116]: 生成 PO状态:待审核 →
- generic [ref=e118]: 状态:已确认 →
- generic [ref=e120]: 状态:采购中 →
- generic [ref=e123]:
- text: 状态:已完成
- generic [ref=e124]: 记录全过程日志
- generic [ref=e125]: 建议新增审核记录、供应商和采购凭证、实际采购金额、物流轨迹、到货验收、到货差异及异常处理字段。以上节点均为业务补全建议。
- generic [ref=e126]:
- article [ref=e127]:
- heading "A 图原型字段" [level=3] [ref=e128]
- list [ref=e129]:
- listitem [ref=e130]: 采购品类、商品名称、商品规格、商品链接、采购数量。
- listitem [ref=e131]: 预算单价、预算总价、收货地址、操作费。
- listitem [ref=e132]: 中国快递单号、美国快递单号、预计到达时间。
- listitem [ref=e133]: 联系人、联系电话、订单备注。
- article [ref=e134]:
- heading "B 图建议控制点" [level=3] [ref=e135]
- list [ref=e136]:
- listitem [ref=e137]: 审核不通过必须记录资料退回原因、审核人和时间。
- listitem [ref=e138]: 采购中必须关联供应商订单、采购凭证和物流轨迹。
- listitem [ref=e139]: 到货差异关闭前不得进入已完成。
- listitem [ref=e140]: 验收通过后确认实际费用并归档。
@@ -0,0 +1,123 @@
- main [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]:
- heading "海外采购单业务流程图" [level=1] [ref=e5]
- generic [ref=e6]: 依据:0805 版货代 SaaS 平台原型 · 海外业务 / 采购订单
- generic [ref=e7]: 原型事实 + 业务补全建议
- region "图例" [ref=e8]:
- generic [ref=e9]: 原型事实
- generic [ref=e11]: 业务补全建议
- generic [ref=e13]: 资料退回 / 到货差异
- generic [ref=e15]: 状态:待审核 → 已确认 → 采购中 → 已完成
- generic "双泳道图,可横向滚动查看" [ref=e16]:
- generic [ref=e17]:
- region [ref=e18]:
- generic [ref=e19]:
- heading "流程图 A:原型事实泳道图" [level=2] [ref=e20]
- generic [ref=e21]: 2 条泳道
- generic [ref=e22]:
- generic [ref=e23]:
- generic [ref=e24]: 录入
- generic [ref=e25]: 提交
- generic [ref=e26]: 校验
- generic [ref=e27]: 建单
- generic [ref=e28]: 确认
- generic [ref=e29]: 执行
- generic [ref=e30]: 完成
- generic [ref=e31]:
- generic [ref=e32]:
- generic [ref=e33]: 后台操作员
- generic [ref=e34]: 进入新增页填写采购信息 →
- generic [ref=e36]: 提交订单 ↘
- generic [ref=e38]: 编辑页手工切换状态 ↘
- generic [ref=e40]: 编辑页手工切换状态 ↘
- generic [ref=e42]: 筛选 / 详情 / 编辑
- generic [ref=e44]:
- generic [ref=e45]: 系统
- generic [ref=e47]:
- text: 必填校验
- generic [ref=e48]: 失败提示并返回表单
- text: →
- generic [ref=e49]: 生成 PO、预算总价状态:待审核 →
- generic [ref=e51]: 状态:已确认 →
- generic [ref=e53]: 状态:采购中 →
- generic [ref=e55]: 状态:已完成
- generic [ref=e57]: 原型没有独立审核、驳回和验收操作。四个状态由“编辑采购订单”页下拉框手工切换;新增页校验采购品类、采购数量、联系人和联系电话。
- region [ref=e58]:
- generic [ref=e59]:
- heading "流程图 B:业务补全建议泳道图" [level=2] [ref=e60]
- generic [ref=e61]: 6 条泳道
- generic [ref=e62]:
- generic [ref=e63]:
- generic [ref=e64]: 需求
- generic [ref=e65]: 初审
- generic [ref=e66]: 审核
- generic [ref=e67]: 确认
- generic [ref=e68]: 下单
- generic [ref=e69]: 发货
- generic [ref=e70]: 跟踪
- generic [ref=e71]: 验收
- generic [ref=e72]: 归档
- generic [ref=e73]:
- generic [ref=e74]:
- generic [ref=e75]: 客户
- generic [ref=e77]:
- text: 提交采购需求
- generic [ref=e78]: 链接 / 规格 / 数量 / 预算
- text: ↘
- generic [ref=e80]:
- text: 补充或修正资料
- generic [ref=e81]: 资料退回后重提
- text: ↗
- generic [ref=e82]:
- generic [ref=e83]: 客服
- generic [ref=e84]: 初审资料创建内部采购单 ↘
- generic [ref=e86]: 协同处理到货差异 ↘
- generic [ref=e88]:
- generic [ref=e89]: 海外采购
- generic [ref=e91]:
- text: 审核资料与预算
- generic [ref=e92]: 不通过:资料退回
- text: →
- generic [ref=e93]: 确认采购方案 →
- generic [ref=e95]: 向供应商下单 ↘
- generic [ref=e97]: 跟踪物流与异常 ↘
- generic [ref=e99]: 确认费用并归档
- generic [ref=e101]:
- generic [ref=e102]: 供应商与物流
- generic [ref=e104]:
- text: 备货发货
- generic [ref=e105]: 回传中美快递单号与 ETA
- text: ↘
- generic [ref=e106]: 补发 / 退款 / 换货 ↗
- generic [ref=e108]:
- generic [ref=e109]: 收货仓
- generic [ref=e111]:
- text: 到货验收
- generic [ref=e112]: 数量 / 规格 / 外观
- generic [ref=e113]: 不一致:登记到货差异
- text: ↗
- generic [ref=e114]:
- generic [ref=e115]: 系统
- generic [ref=e116]: 生成 PO状态:待审核 →
- generic [ref=e118]: 状态:已确认 →
- generic [ref=e120]: 状态:采购中 →
- generic [ref=e123]:
- text: 状态:已完成
- generic [ref=e124]: 记录全过程日志
- generic [ref=e125]: 建议新增审核记录、供应商和采购凭证、实际采购金额、物流轨迹、到货验收、到货差异及异常处理字段。以上节点均为业务补全建议。
- generic [ref=e126]:
- article [ref=e127]:
- heading "A 图原型字段" [level=3] [ref=e128]
- list [ref=e129]:
- listitem [ref=e130]: 采购品类、商品名称、商品规格、商品链接、采购数量。
- listitem [ref=e131]: 预算单价、预算总价、收货地址、操作费。
- listitem [ref=e132]: 中国快递单号、美国快递单号、预计到达时间。
- listitem [ref=e133]: 联系人、联系电话、订单备注。
- article [ref=e134]:
- heading "B 图建议控制点" [level=3] [ref=e135]
- list [ref=e136]:
- listitem [ref=e137]: 审核不通过必须记录资料退回原因、审核人和时间。
- listitem [ref=e138]: 采购中必须关联供应商订单、采购凭证和物流轨迹。
- listitem [ref=e139]: 到货差异关闭前不得进入已完成。
- listitem [ref=e140]: 验收通过后确认实际费用并归档。
@@ -0,0 +1,261 @@
- main [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]:
- heading "海外采购单业务流程" [level=1] [ref=e5]
- paragraph [ref=e6]: Baiyue Logistics · Process Control Sheet
- generic "文档信息" [ref=e7]:
- generic [ref=e8]:
- generic [ref=e9]: 原型依据
- generic [ref=e10]: 0805 版
- generic [ref=e11]:
- generic [ref=e12]: 文档修订
- generic [ref=e13]: REV. 01
- generic [ref=e14]:
- generic [ref=e15]: 图表范围
- generic [ref=e16]: A / B 双泳道
- generic [ref=e17]:
- generic [ref=e18]: 输出用途
- generic [ref=e19]: 评审 / 归档
- region "图例和图表索引" [ref=e20]:
- generic [ref=e21]:
- generic [ref=e22]: 正常流
- generic [ref=e24]: 退回 / 异常
- generic [ref=e26]: 完成态
- generic [ref=e28]: 业务补全建议
- generic [ref=e30]: 原型状态:待审核 → 已确认 → 采购中 → 已完成
- navigation "流程图索引" [ref=e31]:
- link "A · 原型事实" [ref=e32] [cursor=pointer]:
- /url: "#flow-a"
- link "B · 业务建议" [ref=e33] [cursor=pointer]:
- /url: "#flow-b"
- generic [ref=e35]:
- region [ref=e36]:
- generic [ref=e37]:
- generic [ref=e38]:
- generic [ref=e39]: A
- generic [ref=e40]:
- heading "流程图 A:原型事实泳道图" [level=2] [ref=e41]
- paragraph [ref=e42]: 系统现状 · 状态由编辑页手工切换
- generic [ref=e43]:
- generic [ref=e44]: 2 条泳道
- generic [ref=e45]: 7 个阶段
- generic "流程图 A,可横向滚动" [ref=e46]:
- generic [ref=e47]:
- generic [ref=e48]:
- generic [ref=e49]: S01
- generic [ref=e50]: 录入
- generic [ref=e51]:
- generic [ref=e52]: S02
- generic [ref=e53]: 提交
- generic [ref=e54]:
- generic [ref=e55]: S03
- generic [ref=e56]: 校验
- generic [ref=e57]:
- generic [ref=e58]: S04
- generic [ref=e59]: 建单
- generic [ref=e60]:
- generic [ref=e61]: S05
- generic [ref=e62]: 确认
- generic [ref=e63]:
- generic [ref=e64]: S06
- generic [ref=e65]: 执行
- generic [ref=e66]:
- generic [ref=e67]: S07
- generic [ref=e68]: 完成
- generic [ref=e69]:
- generic [ref=e70]:
- generic [ref=e71]:
- text: 后台操作员
- generic [ref=e72]: Admin operator
- generic [ref=e74]:
- generic [ref=e75]: A01
- generic [ref=e76]: 填写采购订单
- generic [ref=e77]: 品类 / 商品 / 数量 / 联系方式
- generic [ref=e79]:
- generic [ref=e80]: A02
- generic [ref=e81]: 提交订单
- generic [ref=e82]: ↘
- generic [ref=e84]:
- generic [ref=e85]: A05
- generic [ref=e86]: 编辑订单状态
- generic [ref=e87]: 下拉框手工选择
- generic [ref=e89]:
- generic [ref=e90]: A06
- generic [ref=e91]: 继续更新状态
- generic [ref=e93]:
- generic [ref=e94]: A07
- generic [ref=e95]: 查看与归档
- generic [ref=e96]: 筛选 / 详情 / 编辑
- generic [ref=e97]:
- generic [ref=e98]:
- text: 系统
- generic [ref=e99]: System
- generic [ref=e101]:
- generic [ref=e102]: A03
- generic [ref=e103]: 必填校验
- generic [ref=e104]: 失败提示并返回表单
- generic [ref=e106]:
- generic [ref=e107]: A04
- generic [ref=e108]: 生成 PO 与预算总价
- generic [ref=e109]: 初始状态:待审核
- generic "原型状态顺序" [ref=e111]:
- generic [ref=e112]: 待审核
- generic [ref=e113]: 已确认
- generic [ref=e114]: 采购中
- generic [ref=e115]: 已完成
- generic [ref=e116]:
- generic [ref=e117]: CTRL-A01
- generic [ref=e118]: 原型没有独立审核、驳回或验收动作;四个状态由“编辑采购订单”页下拉框手工切换。
- region [ref=e119]:
- generic [ref=e120]:
- generic [ref=e121]:
- generic [ref=e122]: B
- generic [ref=e123]:
- heading "流程图 B:业务补全建议泳道图" [level=2] [ref=e124]
- paragraph [ref=e125]: 目标流程 · 客户需求至到货验收闭环
- generic [ref=e126]:
- generic [ref=e127]: 6 条泳道
- generic [ref=e128]: 9 个阶段
- generic [ref=e129]: 2 个异常回路
- generic "流程图 B,可横向滚动" [ref=e130]:
- generic [ref=e131]:
- generic [ref=e132]:
- generic [ref=e133]: S01
- generic [ref=e134]: 需求
- generic [ref=e135]:
- generic [ref=e136]: S02
- generic [ref=e137]: 初审
- generic [ref=e138]:
- generic [ref=e139]: S03
- generic [ref=e140]: 审核
- generic [ref=e141]:
- generic [ref=e142]: S04
- generic [ref=e143]: 确认
- generic [ref=e144]:
- generic [ref=e145]: S05
- generic [ref=e146]: 下单
- generic [ref=e147]:
- generic [ref=e148]: S06
- generic [ref=e149]: 发货
- generic [ref=e150]:
- generic [ref=e151]: S07
- generic [ref=e152]: 跟踪
- generic [ref=e153]:
- generic [ref=e154]: S08
- generic [ref=e155]: 验收
- generic [ref=e156]:
- generic [ref=e157]: S09
- generic [ref=e158]: 归档
- generic [ref=e159]:
- generic [ref=e160]:
- generic [ref=e161]:
- text: 客户
- generic [ref=e162]: Customer
- generic [ref=e164]:
- generic [ref=e165]: B01
- generic [ref=e166]: 提交采购需求
- generic [ref=e167]: 链接 / 规格 / 数量 / 预算
- generic [ref=e168]: ↘
- generic [ref=e170]:
- generic [ref=e171]: X01
- generic [ref=e172]: 补充或修正资料
- generic [ref=e173]: 资料退回后重新提交
- generic [ref=e174]:
- generic [ref=e175]:
- text: 客服
- generic [ref=e176]: Customer service
- generic [ref=e178]:
- generic [ref=e179]: B02
- generic [ref=e180]: 初审并创建采购单
- generic [ref=e181]: ↘
- generic [ref=e183]:
- generic [ref=e184]: X02
- generic [ref=e185]: 协同处理到货差异
- generic [ref=e186]: ↘
- generic [ref=e187]:
- generic [ref=e188]:
- text: 海外采购
- generic [ref=e189]: Overseas buyer
- generic [ref=e191]:
- generic [ref=e192]: B03
- generic [ref=e193]: 审核资料与预算
- generic [ref=e194]: 不通过:资料退回
- generic [ref=e196]:
- generic [ref=e197]: B04
- generic [ref=e198]: 确认采购方案
- generic [ref=e200]:
- generic [ref=e201]: B05
- generic [ref=e202]: 向供应商下单
- generic [ref=e203]: ↘
- generic [ref=e205]:
- generic [ref=e206]: B07
- generic [ref=e207]: 跟踪物流与异常
- generic [ref=e208]: ↘
- generic [ref=e210]:
- generic [ref=e211]: B09
- generic [ref=e212]: 确认费用并归档
- generic [ref=e213]:
- generic [ref=e214]:
- text: 供应商与物流
- generic [ref=e215]: Supplier & logistics
- generic [ref=e217]:
- generic [ref=e218]: B06
- generic [ref=e219]: 备货并发货
- generic [ref=e220]: 回传中美快递单号与 ETA
- generic [ref=e221]: ↘
- generic [ref=e223]:
- generic [ref=e224]: X03
- generic [ref=e225]: 补发 / 退款 / 换货
- generic [ref=e226]:
- generic [ref=e227]:
- text: 收货仓
- generic [ref=e228]: Receiving warehouse
- generic [ref=e230]:
- generic [ref=e231]: B08
- generic [ref=e232]: 到货验收
- generic [ref=e233]: 数量 / 规格 / 外观
- generic [ref=e234]: ↗
- generic [ref=e235]:
- generic [ref=e236]:
- text: 系统
- generic [ref=e237]: System
- generic [ref=e239]:
- generic [ref=e240]: SYS-01
- generic [ref=e241]: 生成 PO
- generic [ref=e242]: 状态:待审核
- generic [ref=e244]:
- generic [ref=e245]: SYS-02
- generic [ref=e246]: 状态:已确认
- generic [ref=e248]:
- generic [ref=e249]: SYS-03
- generic [ref=e250]: 状态:采购中
- generic [ref=e252]:
- generic [ref=e253]: SYS-04
- generic [ref=e254]: 状态:已完成
- generic [ref=e255]: 记录全过程日志
- generic "异常回路登记" [ref=e256]:
- generic [ref=e257]: EX-01
- generic [ref=e258]: 资料退回:B03 海外采购审核 → X01 客户补充 → B02 客服重新初审
- generic [ref=e259]: EX-02
- generic [ref=e260]: 到货差异:B08 收货仓验收 → X02 客服协同 → X03 供应商补发/退款/换货 → B07 重新跟踪
- generic [ref=e261]:
- generic [ref=e262]: CTRL-B01
- generic [ref=e263]: 资料退回与到货差异必须记录责任人、原因、时间和处理结果;异常关闭前不得进入已完成。
- generic [ref=e264]:
- article [ref=e265]:
- heading "A 图原型字段" [level=3] [ref=e266]
- list [ref=e267]:
- listitem [ref=e268]: 采购品类、商品名称、商品规格、商品链接、采购数量。
- listitem [ref=e269]: 预算单价、预算总价、收货地址、操作费。
- listitem [ref=e270]: 中国快递单号、美国快递单号、预计到达时间。
- listitem [ref=e271]: 联系人、联系电话、订单备注。
- article [ref=e272]:
- heading "B 图建议控制点" [level=3] [ref=e273]
- list [ref=e274]:
- listitem [ref=e275]: 审核不通过必须记录资料退回原因、审核人和时间。
- listitem [ref=e276]: 采购中必须关联供应商订单、采购凭证和物流轨迹。
- listitem [ref=e277]: 到货差异关闭前不得进入已完成。
- listitem [ref=e278]: 验收通过后确认实际费用并归档。
- generic [ref=e279]:
- generic [ref=e280]: 依据:0805 版货代 SaaS 平台原型 · 海外业务 / 采购订单
- generic [ref=e281]: A 为原型事实 · B 为业务补全建议 · 离线文档
@@ -0,0 +1,256 @@
- main [ref=f1e2]:
- generic [ref=f1e3]:
- generic [ref=f1e4]:
- heading "海外采购单业务流程" [level=1] [ref=f1e5]
- paragraph [ref=f1e6]: Baiyue Logistics · Process Control Sheet
- generic "文档信息" [ref=f1e7]:
- generic [ref=f1e8]:
- generic [ref=f1e9]: 原型依据
- generic [ref=f1e10]: 0805 版
- generic [ref=f1e11]:
- generic [ref=f1e12]: 文档修订
- generic [ref=f1e13]: REV. 01
- generic [ref=f1e14]:
- generic [ref=f1e15]: 图表范围
- generic [ref=f1e16]: A / B 双泳道
- generic [ref=f1e17]:
- generic [ref=f1e18]: 输出用途
- generic [ref=f1e19]: 评审 / 归档
- region "图例和图表索引" [ref=f1e20]:
- generic [ref=f1e21]:
- generic [ref=f1e22]: 正常流
- generic [ref=f1e24]: 退回 / 异常
- generic [ref=f1e26]: 完成态
- generic [ref=f1e28]: 业务补全建议
- generic [ref=f1e30]: 原型状态:待审核 → 已确认 → 采购中 → 已完成
- generic [ref=f1e32]:
- region [ref=f1e33]:
- generic [ref=f1e34]:
- generic [ref=f1e35]:
- generic [ref=f1e36]: A
- generic [ref=f1e37]:
- heading "流程图 A:原型事实泳道图" [level=2] [ref=f1e38]
- paragraph [ref=f1e39]: 系统现状 · 状态由编辑页手工切换
- generic [ref=f1e40]:
- generic [ref=f1e41]: 2 条泳道
- generic [ref=f1e42]: 7 个阶段
- generic "流程图 A,可横向滚动" [ref=f1e43]:
- generic [ref=f1e44]:
- generic [ref=f1e45]:
- generic [ref=f1e46]: S01
- generic [ref=f1e47]: 录入
- generic [ref=f1e48]:
- generic [ref=f1e49]: S02
- generic [ref=f1e50]: 提交
- generic [ref=f1e51]:
- generic [ref=f1e52]: S03
- generic [ref=f1e53]: 校验
- generic [ref=f1e54]:
- generic [ref=f1e55]: S04
- generic [ref=f1e56]: 建单
- generic [ref=f1e57]:
- generic [ref=f1e58]: S05
- generic [ref=f1e59]: 确认
- generic [ref=f1e60]:
- generic [ref=f1e61]: S06
- generic [ref=f1e62]: 执行
- generic [ref=f1e63]:
- generic [ref=f1e64]: S07
- generic [ref=f1e65]: 完成
- generic [ref=f1e66]:
- generic [ref=f1e67]:
- generic [ref=f1e68]:
- text: 后台操作员
- generic [ref=f1e69]: Admin operator
- generic [ref=f1e71]:
- generic [ref=f1e72]: A01
- generic [ref=f1e73]: 填写采购订单
- generic [ref=f1e74]: 品类 / 商品 / 数量 / 联系方式
- generic [ref=f1e76]:
- generic [ref=f1e77]: A02
- generic [ref=f1e78]: 提交订单
- generic [ref=f1e79]: ↘
- generic [ref=f1e81]:
- generic [ref=f1e82]: A05
- generic [ref=f1e83]: 编辑订单状态
- generic [ref=f1e84]: 下拉框手工选择
- generic [ref=f1e86]:
- generic [ref=f1e87]: A06
- generic [ref=f1e88]: 继续更新状态
- generic [ref=f1e90]:
- generic [ref=f1e91]: A07
- generic [ref=f1e92]: 查看与归档
- generic [ref=f1e93]: 筛选 / 详情 / 编辑
- generic [ref=f1e94]:
- generic [ref=f1e95]:
- text: 系统
- generic [ref=f1e96]: System
- generic [ref=f1e98]:
- generic [ref=f1e99]: A03
- generic [ref=f1e100]: 必填校验
- generic [ref=f1e101]: 失败提示并返回表单
- generic [ref=f1e103]:
- generic [ref=f1e104]: A04
- generic [ref=f1e105]: 生成 PO 与预算总价
- generic [ref=f1e106]: 初始状态:待审核
- generic "原型状态顺序" [ref=f1e108]:
- generic [ref=f1e109]: 待审核
- generic [ref=f1e110]: 已确认
- generic [ref=f1e111]: 采购中
- generic [ref=f1e112]: 已完成
- generic [ref=f1e113]:
- generic [ref=f1e114]: CTRL-A01
- generic [ref=f1e115]: 原型没有独立审核、驳回或验收动作;四个状态由“编辑采购订单”页下拉框手工切换。
- region [ref=f1e116]:
- generic [ref=f1e117]:
- generic [ref=f1e118]:
- generic [ref=f1e119]: B
- generic [ref=f1e120]:
- heading "流程图 B:业务补全建议泳道图" [level=2] [ref=f1e121]
- paragraph [ref=f1e122]: 目标流程 · 客户需求至到货验收闭环
- generic [ref=f1e123]:
- generic [ref=f1e124]: 6 条泳道
- generic [ref=f1e125]: 9 个阶段
- generic [ref=f1e126]: 2 个异常回路
- generic "流程图 B,可横向滚动" [ref=f1e127]:
- generic [ref=f1e128]:
- generic [ref=f1e129]:
- generic [ref=f1e130]: S01
- generic [ref=f1e131]: 需求
- generic [ref=f1e132]:
- generic [ref=f1e133]: S02
- generic [ref=f1e134]: 初审
- generic [ref=f1e135]:
- generic [ref=f1e136]: S03
- generic [ref=f1e137]: 审核
- generic [ref=f1e138]:
- generic [ref=f1e139]: S04
- generic [ref=f1e140]: 确认
- generic [ref=f1e141]:
- generic [ref=f1e142]: S05
- generic [ref=f1e143]: 下单
- generic [ref=f1e144]:
- generic [ref=f1e145]: S06
- generic [ref=f1e146]: 发货
- generic [ref=f1e147]:
- generic [ref=f1e148]: S07
- generic [ref=f1e149]: 跟踪
- generic [ref=f1e150]:
- generic [ref=f1e151]: S08
- generic [ref=f1e152]: 验收
- generic [ref=f1e153]:
- generic [ref=f1e154]: S09
- generic [ref=f1e155]: 归档
- generic [ref=f1e156]:
- generic [ref=f1e157]:
- generic [ref=f1e158]:
- text: 客户
- generic [ref=f1e159]: Customer
- generic [ref=f1e161]:
- generic [ref=f1e162]: B01
- generic [ref=f1e163]: 提交采购需求
- generic [ref=f1e164]: 链接 / 规格 / 数量 / 预算
- generic [ref=f1e165]: ↘
- generic [ref=f1e167]:
- generic [ref=f1e168]: X01
- generic [ref=f1e169]: 补充或修正资料
- generic [ref=f1e170]: 资料退回后重新提交
- generic [ref=f1e171]:
- generic [ref=f1e172]:
- text: 客服
- generic [ref=f1e173]: Customer service
- generic [ref=f1e175]:
- generic [ref=f1e176]: B02
- generic [ref=f1e177]: 初审并创建采购单
- generic [ref=f1e178]: ↘
- generic [ref=f1e180]:
- generic [ref=f1e181]: X02
- generic [ref=f1e182]: 协同处理到货差异
- generic [ref=f1e183]: ↘
- generic [ref=f1e184]:
- generic [ref=f1e185]:
- text: 海外采购
- generic [ref=f1e186]: Overseas buyer
- generic [ref=f1e188]:
- generic [ref=f1e189]: B03
- generic [ref=f1e190]: 审核资料与预算
- generic [ref=f1e191]: 不通过:资料退回
- generic [ref=f1e193]:
- generic [ref=f1e194]: B04
- generic [ref=f1e195]: 确认采购方案
- generic [ref=f1e197]:
- generic [ref=f1e198]: B05
- generic [ref=f1e199]: 向供应商下单
- generic [ref=f1e200]: ↘
- generic [ref=f1e202]:
- generic [ref=f1e203]: B07
- generic [ref=f1e204]: 跟踪物流与异常
- generic [ref=f1e205]: ↘
- generic [ref=f1e207]:
- generic [ref=f1e208]: B09
- generic [ref=f1e209]: 确认费用并归档
- generic [ref=f1e210]:
- generic [ref=f1e211]:
- text: 供应商与物流
- generic [ref=f1e212]: Supplier & logistics
- generic [ref=f1e214]:
- generic [ref=f1e215]: B06
- generic [ref=f1e216]: 备货并发货
- generic [ref=f1e217]: 回传中美快递单号与 ETA
- generic [ref=f1e218]: ↘
- generic [ref=f1e220]:
- generic [ref=f1e221]: X03
- generic [ref=f1e222]: 补发 / 退款 / 换货
- generic [ref=f1e223]:
- generic [ref=f1e224]:
- text: 收货仓
- generic [ref=f1e225]: Receiving warehouse
- generic [ref=f1e227]:
- generic [ref=f1e228]: B08
- generic [ref=f1e229]: 到货验收
- generic [ref=f1e230]: 数量 / 规格 / 外观
- generic [ref=f1e231]: ↗
- generic [ref=f1e232]:
- generic [ref=f1e233]:
- text: 系统
- generic [ref=f1e234]: System
- generic [ref=f1e236]:
- generic [ref=f1e237]: SYS-01
- generic [ref=f1e238]: 生成 PO
- generic [ref=f1e239]: 状态:待审核
- generic [ref=f1e241]:
- generic [ref=f1e242]: SYS-02
- generic [ref=f1e243]: 状态:已确认
- generic [ref=f1e245]:
- generic [ref=f1e246]: SYS-03
- generic [ref=f1e247]: 状态:采购中
- generic [ref=f1e249]:
- generic [ref=f1e250]: SYS-04
- generic [ref=f1e251]: 状态:已完成
- generic [ref=f1e252]: 记录全过程日志
- generic "异常回路登记" [ref=f1e253]:
- generic [ref=f1e254]: EX-01
- generic [ref=f1e255]: 资料退回:B03 海外采购审核 → X01 客户补充 → B02 客服重新初审
- generic [ref=f1e256]: EX-02
- generic [ref=f1e257]: 到货差异:B08 收货仓验收 → X02 客服协同 → X03 供应商补发/退款/换货 → B07 重新跟踪
- generic [ref=f1e258]:
- generic [ref=f1e259]: CTRL-B01
- generic [ref=f1e260]: 资料退回与到货差异必须记录责任人、原因、时间和处理结果;异常关闭前不得进入已完成。
- generic [ref=f1e261]:
- article [ref=f1e262]:
- heading "A 图原型字段" [level=3] [ref=f1e263]
- list [ref=f1e264]:
- listitem [ref=f1e265]: 采购品类、商品名称、商品规格、商品链接、采购数量。
- listitem [ref=f1e266]: 预算单价、预算总价、收货地址、操作费。
- listitem [ref=f1e267]: 中国快递单号、美国快递单号、预计到达时间。
- listitem [ref=f1e268]: 联系人、联系电话、订单备注。
- article [ref=f1e269]:
- heading "B 图建议控制点" [level=3] [ref=f1e270]
- list [ref=f1e271]:
- listitem [ref=f1e272]: 审核不通过必须记录资料退回原因、审核人和时间。
- listitem [ref=f1e273]: 采购中必须关联供应商订单、采购凭证和物流轨迹。
- listitem [ref=f1e274]: 到货差异关闭前不得进入已完成。
- listitem [ref=f1e275]: 验收通过后确认实际费用并归档。
- generic [ref=f1e276]:
- generic [ref=f1e277]: 依据:0805 版货代 SaaS 平台原型 · 海外业务 / 采购订单
- generic [ref=f1e278]: A 为原型事实 · B 为业务补全建议 · 离线文档
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More