优化
|
Before Width: | Height: | Size: 881 KiB |
|
Before Width: | Height: | Size: 549 KiB |
|
Before Width: | Height: | Size: 835 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 26 KiB |
@@ -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>
|
||||
|
Before Width: | Height: | Size: 764 KiB |
@@ -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>
|
||||
|
Before Width: | Height: | Size: 764 KiB |
|
Before Width: | Height: | Size: 26 KiB |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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 & LAST-MILE LOGISTICS PROCESS · 全程节点可追踪</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 881 KiB |
|
Before Width: | Height: | Size: 549 KiB |
|
Before Width: | Height: | Size: 835 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 492 KiB |
|
Before Width: | Height: | Size: 26 KiB |
@@ -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>
|
||||
|
Before Width: | Height: | Size: 764 KiB |
@@ -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>
|
||||
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 764 KiB |
@@ -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路由限制未能获取完整表头信息,已根据菜单结构推断标注。如需补充具体页面详情,请登录系统确认后更新。
|
||||
@@ -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/);
|
||||
});
|
||||
@@ -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. 退货入库、退货换标和退货出库已整合为客户端“退货换标”工作台,支持新增、查询、列表和详情;提交始终生成尾程订单,选择换标时额外生成后台换标单。
|
||||
@@ -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>
|
||||
|
Before Width: | Height: | Size: 764 KiB |
@@ -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 为业务补全建议 · 离线文档
|
||||
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 125 KiB |