Files
docs/悦/官网头程下单/workbench.html
T
2026-09-08 18:17:00 +08:00

9857 lines
701 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>虫虫出海 CHONGCHONG GLOBAL | 工作台</title>
<meta name="description" content="虫虫出海工作台 - 海外采购、美国本土卡车、美本土散板一键下单。">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%231565c0'/%3E%3Cpath d='M18 14h19c9 0 14 4 14 11 0 5-3 8-8 9 6 1 9 4 9 10 0 8-6 12-16 12H18V14zm10 9v8h8c4 0 6-1 6-4s-2-4-6-4h-8zm0 16v9h9c4 0 6-2 6-5s-2-4-7-4h-8z' fill='white'/%3E%3C/svg%3E">
<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 src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></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.25rem 1rem;
position: sticky; top: 3.5rem; align-self: flex-start;
max-height: calc(100vh - 3.5rem); overflow-y: auto;
}
.dash-right.dash-right-hidden { display: none !important; }
.dash-topup-btn {
display: inline-flex; align-items: center; gap: 0.45rem;
padding: 0.45rem 0.9rem; border-radius: 0.7rem;
font-size: 0.85rem; font-weight: 600; cursor: pointer;
color: #fff; background: #1565C0;
border: none; box-shadow: 0 4px 14px rgba(21,101,192,0.3);
transition: all 0.2s ease;
}
.dash-topup-btn:hover { background: #0f4fa1; transform: translateY(-1px); box-shadow: 0 6px 18px rgba(21,101,192,0.4); }
.dash-topup-btn i { font-size: 0.95rem; }
.topup-overlay {
position: fixed; inset: 0; background: rgba(15,23,42,0.55);
display: flex; align-items: center; justify-content: center; z-index: 100; padding: 1rem;
}
.topup-overlay.hidden { display: none; }
.topup-modal {
width: 100%; max-width: 520px; max-height: calc(100vh - 2rem); background: #fff; border-radius: 1rem;
box-shadow: 0 24px 60px rgba(0,0,0,0.25); overflow: hidden; animation: topupIn .25s ease;
display: flex; flex-direction: column;
}
@keyframes topupIn { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: none; } }
.topup-head { display: flex; align-items: center; justify-content: space-between; padding: 1.1rem 1.25rem; border-bottom: 1px solid #eef2f7; }
.topup-head h3 { font-size: 1.05rem; font-weight: 700; color: #0f172a; }
.topup-close { width: 2.75rem; height: 2.75rem; border-radius: 0.55rem; background: none; border: none; font-size: 1.4rem; line-height: 1; color: #64748b; cursor: pointer; }
.topup-close:hover { color: #475569; }
.topup-close:focus-visible, .topup-preset:focus-visible, .topup-upload:focus-visible, .topup-remove:focus-visible,
.topup-foot button:focus-visible { outline: 3px solid rgba(21,101,192,0.28); outline-offset: 2px; }
.topup-body { padding: 1.25rem; overflow-y: auto; }
.topup-stepper { display: grid; grid-template-columns: repeat(3, 1fr); margin-bottom: 1.35rem; }
.topup-step { position: relative; display: flex; flex-direction: column; align-items: center; gap: 0.4rem; color: #64748b; font-size: 0.75rem; text-align: center; }
.topup-step:not(:last-child)::after { content: ''; position: absolute; top: 0.9rem; left: calc(50% + 1rem); right: calc(-50% + 1rem); height: 2px; background: #e2e8f0; }
.topup-step.done:not(:last-child)::after { background: #007565; }
.topup-step-dot { position: relative; z-index: 1; width: 1.8rem; height: 1.8rem; display: inline-flex; align-items: center; justify-content: center; border: 2px solid #cbd5e1; border-radius: 50%; background: #fff; font-weight: 700; }
.topup-step.active { color: #1565C0; font-weight: 600; }
.topup-step.active .topup-step-dot { border-color: #1565C0; background: #1565C0; color: #fff; }
.topup-step.done { color: #006b5d; }
.topup-step.done .topup-step-dot { border-color: #007565; background: #007565; color: #fff; }
.topup-panel[hidden] { display: none; }
.topup-panel-title { margin: 0 0 0.35rem; color: #0f172a; font-size: 1rem; font-weight: 700; }
.topup-help { margin: 0 0 1rem; color: #64748b; font-size: 0.82rem; line-height: 1.55; }
.topup-amount-label { font-size: 0.85rem; color: #475569; margin-bottom: 0.5rem; }
.topup-amount-input {
display: flex; align-items: center; gap: 0.5rem;
border: 1.5px solid rgba(21,101,192,0.25); border-radius: 0.8rem;
padding: 0 0.9rem; height: 3rem; transition: border-color .2s, box-shadow .2s;
}
.topup-amount-input:focus-within { border-color: #1565C0; box-shadow: 0 0 0 4px rgba(21,101,192,0.1); }
.topup-amount-input.has-error { border-color: #dc2626; }
.topup-amount-input .cur { font-size: 1.1rem; font-weight: 700; color: #0f172a; }
.topup-amount-input input { flex: 1; min-width: 0; border: none; outline: none; font-size: 1.05rem; font-weight: 600; color: #0f172a; background: transparent; }
.topup-presets { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; margin-top: 0.8rem; }
.topup-preset { flex: 1; padding: 0.5rem; border-radius: 0.6rem; border: 1px solid #e2e8f0; background: #f8fafc; font-size: 0.85rem; color: #475569; cursor: pointer; transition: all .2s; }
.topup-preset:hover { border-color: #1565C0; color: #1565C0; }
.topup-preset.active { background: linear-gradient(135deg, rgba(21,101,192,0.1), rgba(0,191,165,0.1)); border-color: #1565C0; color: #1565C0; font-weight: 600; }
.topup-error { min-height: 1.25rem; margin-top: 0.4rem; color: #dc2626; font-size: 0.78rem; line-height: 1.4; }
.topup-payment-qr {
display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 1rem;
margin-bottom: 1rem; padding: 1rem; border-radius: 0.85rem; background: #f8fafc;
box-shadow: 0 5px 16px rgba(15,23,42,0.08);
}
.topup-payment-qr-heading { display: flex; align-items: flex-start; gap: 0.7rem; min-width: 0; }
.topup-payment-qr-icon {
width: 2.5rem; height: 2.5rem; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
border-radius: 0.65rem; background: rgba(21,101,192,0.1); color: #1565c0;
}
.topup-payment-qr h5 { margin: 0; color: #0f172a; font-size: 0.92rem; font-weight: 700; }
.topup-payment-qr p { margin: 0.35rem 0 0; color: #475569; font-size: 0.78rem; line-height: 1.55; }
.topup-payment-qr-visual { position: relative; width: 11.25rem; height: 11.25rem; flex: 0 0 auto; }
.topup-payment-qr-visual img { display: block; width: 100%; height: 100%; }
.topup-payment-qr-visual span {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); min-width: 5.5rem;
color: #fff; font-size: 0.78rem; font-weight: 700; text-align: center; white-space: nowrap;
}
.topup-upload {
width: 100%; min-height: 9.5rem; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.55rem;
padding: 1.25rem; border: 2px dashed #cbd5e1; border-radius: 0.8rem; background: #f8fafc; color: #475569;
cursor: pointer; text-align: center; transition: border-color .2s, background .2s, box-shadow .2s;
}
.topup-upload:hover, .topup-upload.dragging { border-color: #1565C0; background: rgba(21,101,192,0.05); box-shadow: 0 0 0 4px rgba(21,101,192,0.08); }
.topup-upload.has-error { border-color: #dc2626; }
.topup-upload i { color: #1565C0; font-size: 1.65rem; }
.topup-upload-title { color: #0f172a; font-size: 0.9rem; font-weight: 700; }
.topup-upload-hint { color: #64748b; font-size: 0.78rem; line-height: 1.5; }
.topup-proof-summary { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.85rem; padding: 0.8rem; border: 1px solid #dbe5ef; border-radius: 0.75rem; background: #fff; }
.topup-proof-summary[hidden] { display: none; }
.topup-proof-icon { width: 2.75rem; height: 2.75rem; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; border-radius: 0.6rem; background: rgba(0,117,101,0.1); color: #006b5d; }
.topup-proof-meta { min-width: 0; flex: 1; }
.topup-proof-name { overflow: hidden; color: #0f172a; font-size: 0.85rem; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.topup-proof-size { color: #64748b; font-size: 0.75rem; }
.topup-remove { width: 2.75rem; height: 2.75rem; flex: 0 0 auto; border: none; border-radius: 0.5rem; background: #f1f5f9; color: #475569; cursor: pointer; }
.topup-success { display: flex; flex-direction: column; align-items: center; padding: 0.5rem 0 1rem; text-align: center; }
.topup-success-icon { width: 4rem; height: 4rem; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 0.8rem; border-radius: 50%; background: rgba(0,117,101,0.12); color: #006b5d; font-size: 1.8rem; }
.topup-status { display: inline-flex; align-items: center; gap: 0.45rem; margin-top: 0.55rem; padding: 0.4rem 0.7rem; border-radius: 999px; background: rgba(21,101,192,0.08); color: #1565C0; font-size: 0.78rem; font-weight: 700; }
.topup-result { width: 100%; margin-top: 0.9rem; border: 1px solid #e2e8f0; border-radius: 0.75rem; overflow: hidden; }
.topup-result-row { display: grid; grid-template-columns: minmax(6rem, auto) 1fr; gap: 0.8rem; padding: 0.72rem 0.85rem; color: #64748b; font-size: 0.82rem; text-align: left; }
.topup-result-row + .topup-result-row { border-top: 1px solid #eef2f7; }
.topup-result-row strong { min-width: 0; overflow-wrap: anywhere; color: #0f172a; text-align: right; }
.topup-notice { width: 100%; display: flex; align-items: flex-start; gap: 0.55rem; margin-top: 0.85rem; padding: 0.75rem; border-radius: 0.7rem; background: rgba(21,101,192,0.06); color: #475569; font-size: 0.78rem; line-height: 1.5; text-align: left; }
.topup-notice i { margin-top: 0.12rem; color: #1565C0; }
.topup-sms { margin-top: 0.65rem; color: #006b5d; font-size: 0.78rem; font-weight: 600; }
.topup-foot { display: flex; gap: 0.75rem; padding: 0 1.25rem 1.25rem; }
.topup-foot button { flex: 1; min-height: 2.75rem; padding: 0.7rem; border-radius: 0.8rem; font-size: 0.9rem; font-weight: 600; cursor: pointer; border: none; transition: all .2s; }
.topup-cancel { background: #f1f5f9; color: #475569; }
.topup-cancel:hover { background: #e2e8f0; }
.topup-confirm { background: #1565C0; color: #fff; box-shadow: 0 4px 14px rgba(21,101,192,0.3); }
.topup-confirm:hover { background: #0f4fa1; transform: translateY(-1px); box-shadow: 0 6px 18px rgba(21,101,192,0.4); }
.topup-foot-group { display: contents; }
.topup-foot-group[hidden] { display: none; }
@media (max-width: 640px) {
.topup-overlay { align-items: flex-end; padding: 0; }
.topup-modal { max-width: none; max-height: calc(100dvh - 0.5rem); border-radius: 1rem 1rem 0 0; animation-name: topupSheetIn; }
@keyframes topupSheetIn { from { opacity: 0; transform: translateY(2rem); } to { opacity: 1; transform: none; } }
.topup-head, .topup-body { padding-left: 1rem; padding-right: 1rem; }
.topup-body { padding-top: 1rem; padding-bottom: 1rem; }
.topup-foot { padding: 0 1rem max(1rem, env(safe-area-inset-bottom)); }
.topup-step { font-size: 0.68rem; }
.topup-presets { grid-template-columns: repeat(2, 1fr); }
.topup-payment-qr { grid-template-columns: 1fr; justify-items: center; text-align: left; }
.topup-payment-qr-heading { width: 100%; }
.topup-payment-qr-visual { width: 9.75rem; height: 9.75rem; }
.topup-upload { min-height: 8rem; padding: 1rem; }
.topup-result-row { grid-template-columns: minmax(5.4rem, auto) 1fr; }
}
@media (prefers-reduced-motion: reduce) {
.topup-modal { animation: none; }
.dash-topup-btn, .topup-preset, .topup-upload, .topup-foot button { transition: none; }
.dash-topup-btn:hover, .topup-confirm:hover { transform: none; }
}
.dash-right-card {
background: #f8fafc; border: 1px solid #eef2f7;
border-radius: 0.85rem; padding: 1rem; margin-bottom: 0.85rem;
}
.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: 0.9rem; 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; }
}
/* ===== 折叠分组菜单(来自柏悦客户端,统一为 workbench 样式)===== */
.dash-nav-divider { height: 1px; background: rgba(255, 255, 255, 0.08); margin: 0.5rem 0.75rem; }
.dash-menu-group { display: flex; flex-direction: column; }
.dash-menu-group-header {
display: flex; align-items: center; gap: 0.75rem;
margin: 2px 10px;
padding: 0.62rem 0.85rem; border-radius: 0.7rem;
color: rgba(255, 255, 255, 0.6); font-size: 0.86rem; font-weight: 600;
cursor: pointer; transition: all 0.2s ease; user-select: none; white-space: nowrap;
}
.dash-menu-group-header:hover { background: rgba(255, 255, 255, 0.05); color: #fff; }
.dash-menu-group-header .g-icon { width: 1.2rem; text-align: center; font-size: 0.92rem; }
.dash-menu-group-header .g-title { flex: 1; }
.dash-menu-group-header .g-arrow { font-size: 0.62rem; transition: transform 0.3s ease; }
.dash-menu-group-header.collapsed .g-arrow { transform: rotate(-90deg); }
.dash-menu-sub {
display: flex; flex-direction: column; gap: 0.12rem; overflow: hidden;
max-height: 600px; opacity: 1; margin-top: 0.12rem;
transition: max-height 0.3s ease, opacity 0.25s ease, margin-top 0.2s ease;
}
.dash-menu-sub.collapsed { max-height: 0; opacity: 0; margin-top: 0; }
.dash-nav-link.sub { padding-left: 2.55rem; font-size: 0.88rem; font-weight: 500; }
.dash-nav-link.sub i { font-size: 0.8rem; opacity: 0.65; }
.dash-business-subgroup { padding: 0.35rem 0 0.3rem; border-bottom: 1px solid rgba(148, 163, 184, 0.14); }
.dash-business-subgroup:last-child { border-bottom: 0; }
.dash-business-label { display: flex; align-items: center; gap: 0.55rem; padding: 0.42rem 1rem; color: rgba(255,255,255,0.55); font-size: 0.76rem; font-weight: 600; }
.dash-business-label i { width: 1rem; text-align: center; }
.dash-business-subgroup .dash-nav-link.sub { padding-left: 2.85rem; font-size: 0.82rem; }
.sea-business-page { display: flex; flex-direction: column; gap: 0.9rem; }
.sea-business-page.hidden, .sea-business-form-panel[hidden] { display: none; }
.sea-page-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.sea-page-header h1 { margin: 0; color: #0f172a; font-size: 1.35rem; font-weight: 700; }
.sea-page-header p { margin: 0.3rem 0 0; color: #64748b; font-size: 0.85rem; }
.sea-order-filters { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.8rem; padding: 1rem; background: #fff; border-radius: 0.75rem; box-shadow: 0 4px 14px rgba(15,23,42,0.06); }
.sea-order-filters input, .sea-order-filters select { width: 100%; height: 2.4rem; padding: 0 0.75rem; border: 1px solid #dbe3ec; border-radius: 0.5rem; background: #fff; color: #334155; font-size: 0.84rem; }
.sea-filter-actions { display: flex; align-items: center; gap: 0.6rem; grid-column: 1 / -1; justify-content: flex-end; }
.sea-order-list-card { overflow: hidden; border: 1px solid var(--wb-border, #d7e2ea); border-radius: 12px; background: #fff; }
.sea-order-list-card .sea-order-table-wrap { overflow-x: auto; border-radius: 0; background: #fff; box-shadow: none; }
.sea-order-table { width: 100%; min-width: 760px; border-collapse: collapse; font-size: 0.82rem; }
.sea-order-table th, .sea-order-table td { padding: 0.8rem 0.9rem; text-align: left; border-bottom: 1px solid #edf1f5; white-space: nowrap; }
.sea-order-table th { color: #475569; background: #f8fafc; font-weight: 600; }
.sea-order-table tbody { background: #fff; }
.sea-order-table tbody tr:last-child td { border-bottom: 0; }
.sea-order-pagination { display: flex; min-height: 58px; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 16px; border-top: 1px solid var(--wb-border, #d7e2ea); background: #fff; color: var(--wb-muted, #667786); font-size: 13px; }
.sea-order-pagination-info, .sea-order-pagination-controls { display: flex; align-items: center; gap: 8px; }
.sea-order-pagination-info strong { color: var(--wb-ink, #142333); font-weight: 600; }
.sea-order-page-size { height: 36px; padding: 0 30px 0 12px; border: 1px solid var(--wb-border, #d7e2ea); border-radius: var(--wb-radius-control, 7px); background: #fff; color: var(--wb-ink, #142333); font-size: 13px; }
.sea-order-page-size:focus { border-color: var(--wb-action, #126ba8); box-shadow: var(--wb-focus, 0 0 0 3px rgba(18,107,168,0.14)); outline: none; }
.sea-order-pagination .page-btn { display: inline-flex; min-width: 36px; height: 36px; align-items: center; justify-content: center; padding: 0 10px; border: 1px solid var(--wb-border, #d7e2ea); border-radius: var(--wb-radius-control, 7px); background: #fff; color: var(--wb-ink, #142333); font-size: 13px; font-weight: 500; transition: border-color .2s ease, background .2s ease, color .2s ease; }
.sea-order-pagination .page-btn:hover:not(:disabled) { border-color: var(--wb-action, #126ba8); background: #edf3f7; color: var(--wb-action, #126ba8); }
.sea-order-pagination .page-btn.active { border-color: var(--wb-action, #126ba8); background: var(--wb-action, #126ba8); color: #fff; }
.sea-order-pagination .page-btn:disabled { cursor: not-allowed; opacity: .48; }
@media (max-width: 640px) {
.sea-order-pagination { align-items: flex-start; flex-direction: column; }
.sea-order-pagination-controls { width: 100%; justify-content: flex-end; }
}
.sea-business-page .btn-primary, .sea-business-form-panel .btn-primary, .sea-business-detail-page .btn-primary { background: linear-gradient(135deg, #667eea, #764ba2); box-shadow: 0 4px 15px rgba(102,126,234,0.35); }
.sea-business-page .btn-primary:hover, .sea-business-form-panel .btn-primary:hover, .sea-business-detail-page .btn-primary:hover { background: linear-gradient(135deg, #5a6fd8, #6b4295); box-shadow: 0 6px 20px rgba(102,126,234,0.45); }
.sea-business-page .sea-order-table th { background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); color: #667eea; border-bottom-color: rgba(102,126,234,0.15); }
.sea-business-page .sea-table-action { color: #667eea; }
.sea-business-page .sea-table-action:hover { color: #764ba2; }
.sea-order-empty { padding: 2.2rem 1rem !important; color: #94a3b8; text-align: center !important; }
.sea-table-actions { display: flex; align-items: center; gap: 0.75rem; }
.sea-table-action { color: #1565c0; font-weight: 600; }
.sea-table-action:hover { color: #0d47a1; text-decoration: underline; }
.sea-business-detail-page { display: flex; flex-direction: column; gap: 1rem; }
.sea-business-detail-page[hidden] { display: none; }
.sea-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
.sea-detail-section { background: #fff; border: 1px solid #e2e8f0; border-radius: 0.75rem; padding: 1.15rem; }
.sea-detail-section h3 { margin: 0 0 1rem; color: #0f172a; font-size: 1rem; font-weight: 700; }
.sea-detail-section dl { display: grid; grid-template-columns: 7rem minmax(0, 1fr); row-gap: 0.75rem; margin: 0; font-size: 0.85rem; }
.sea-detail-section dt { color: #64748b; }
.sea-detail-section dd { min-width: 0; margin: 0; color: #1e293b; font-weight: 500; overflow-wrap: anywhere; }
.truck-detail-header { display:flex; align-items:center; justify-content:space-between; gap:24px; padding:22px 24px; background:#fff; border:1px solid #e1e8ef; border-radius:12px 12px 0 0; }
.truck-detail-heading h1 { margin:0; color:#102a43; font-size:1.25rem; font-weight:700; }
.truck-detail-heading p { margin:7px 0 0; color:#6b8296; font-size:.8rem; letter-spacing:.02em; }
.truck-detail-actions { display:flex; align-items:center; gap:10px; flex-shrink:0; }
.truck-detail-status { display:inline-flex; align-items:center; padding:6px 12px; border-radius:999px; background:#fff5e6; color:#e88a16; font-size:.78rem; font-weight:700; }
.truck-detail-action { min-height:38px; padding:0 18px; border-radius:7px; font-size:.82rem; font-weight:600; transition:background .2s, border-color .2s, color .2s, transform .2s; }
.truck-detail-action:hover { transform:translateY(-1px); }
.truck-detail-action.primary { color:#fff; background:#2f6fed; border:1px solid #2f6fed; box-shadow:0 4px 12px rgba(47,111,237,.2); }
.truck-detail-action.primary:hover { background:#245fd5; border-color:#245fd5; }
.truck-detail-action.secondary { color:#506579; background:#fff; border:1px solid #cbd7e2; }
.truck-detail-action.secondary:hover { color:#245fd5; border-color:#8eb0ef; background:#f8fbff; }
.truck-detail-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; padding:16px 0 24px; }
.truck-detail-card { min-width:0; overflow:hidden; background:#fff; border:1px solid #e1e8ef; border-radius:12px; box-shadow:0 4px 14px rgba(30,59,86,.05); }
.truck-detail-card h2 { margin:0; padding:15px 16px; color:#183b63; background:#f7fafc; border-bottom:1px solid #e5ecf2; font-size:.92rem; font-weight:700; }
.truck-detail-fields { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); margin:0; padding:4px 16px 8px; }
.truck-detail-field { display:grid; grid-template-columns:6.3rem minmax(0,1fr); align-items:start; gap:10px; min-width:0; padding:12px 0; border-bottom:1px dashed #e5ecf2; font-size:.82rem; }
.truck-detail-field:nth-last-child(-n+2) { border-bottom:0; }
.truck-detail-field dt { color:#70869a; }
.truck-detail-field dd { min-width:0; margin:0; color:#153b68; font-weight:600; overflow-wrap:anywhere; }
.truck-detail-field dd.is-status { color:#e88a16; }
.pallet-detail-breadcrumb { display:flex; align-items:center; flex-wrap:wrap; gap:7px; margin:0 0 12px; color:#718399; font-size:.78rem; }
.pallet-detail-breadcrumb button { padding:0; border:0; background:transparent; color:#718399; cursor:pointer; }
.pallet-detail-breadcrumb button:hover { color:#2f6fed; }
.pallet-detail-breadcrumb strong { color:#183b63; font-weight:600; }
.pallet-detail-header { display:flex; align-items:center; justify-content:space-between; gap:24px; padding:22px 24px; background:#fff; border:1px solid #e1e8ef; border-radius:12px 12px 0 0; }
.pallet-detail-heading h1 { margin:0; color:#102a43; font-size:1.25rem; font-weight:700; }
.pallet-detail-heading p { margin:7px 0 0; color:#6b8296; font-size:.8rem; letter-spacing:.02em; }
.pallet-detail-actions { display:flex; align-items:center; gap:10px; flex-shrink:0; }
.pallet-detail-status { display:inline-flex; align-items:center; padding:6px 12px; border-radius:999px; background:#fff5e6; color:#e88a16; font-size:.78rem; font-weight:700; }
.pallet-detail-action { min-height:38px; padding:0 18px; border-radius:7px; font-size:.82rem; font-weight:600; transition:background .2s, border-color .2s, color .2s, transform .2s; }
.pallet-detail-action:hover { transform:translateY(-1px); }
.pallet-detail-action.primary { color:#fff; background:#2f6fed; border:1px solid #2f6fed; box-shadow:0 4px 12px rgba(47,111,237,.2); }
.pallet-detail-action.primary:hover { background:#245fd5; border-color:#245fd5; }
.pallet-detail-action.secondary { color:#506579; background:#fff; border:1px solid #cbd7e2; }
.pallet-detail-action.secondary:hover { color:#245fd5; border-color:#8eb0ef; background:#f8fbff; }
.pallet-detail-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; padding:16px 0 24px; }
.pallet-detail-card { min-width:0; overflow:hidden; background:#fff; border:1px solid #e1e8ef; border-radius:12px; box-shadow:0 4px 14px rgba(30,59,86,.05); }
.pallet-detail-card h2 { margin:0; padding:15px 16px; color:#183b63; background:#f7fafc; border-bottom:1px solid #e5ecf2; font-size:.92rem; font-weight:700; }
.pallet-detail-fields { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); margin:0; padding:4px 16px 8px; }
.pallet-detail-field { display:grid; grid-template-columns:6.3rem minmax(0,1fr); align-items:start; gap:10px; min-width:0; padding:12px 0; border-bottom:1px dashed #e5ecf2; font-size:.82rem; }
.pallet-detail-field:nth-last-child(-n+2) { border-bottom:0; }
.pallet-detail-field dt { color:#70869a; }
.pallet-detail-field dd { min-width:0; margin:0; color:#153b68; font-weight:600; overflow-wrap:anywhere; }
.pallet-detail-field dd.is-status { color:#e88a16; }
@media (max-width:780px) { .truck-detail-header, .pallet-detail-header { align-items:flex-start; flex-direction:column; gap:16px; padding:18px; } .truck-detail-actions, .pallet-detail-actions { width:100%; flex-wrap:wrap; } .truck-detail-actions .truck-detail-action, .pallet-detail-actions .pallet-detail-action { flex:1; } .truck-detail-grid, .pallet-detail-grid { grid-template-columns:1fr; gap:12px; } .truck-detail-fields, .pallet-detail-fields { grid-template-columns:1fr; padding-bottom:4px; } .truck-detail-field, .truck-detail-field:nth-last-child(-n+2), .pallet-detail-field, .pallet-detail-field:nth-last-child(-n+2) { border-bottom:1px dashed #e5ecf2; } .truck-detail-field:last-child, .pallet-detail-field:last-child { border-bottom:0; } }
.procurement-detail-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.procurement-detail-heading h1 { margin: 0; color: #1f2937; font-size: 1.25rem; font-weight: 700; }
.procurement-detail-heading p { margin: 0.35rem 0 0; color: #64748b; font-size: 0.82rem; }
.procurement-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
.procurement-detail-card { overflow: hidden; border: 1px solid #e2e8f0; border-radius: 0.5rem; background: #fff; }
.procurement-detail-card.is-product { grid-column: 1 / -1; }
.procurement-detail-card h3 { margin: 0; padding: 0.8rem 1rem; border-bottom: 1px solid #e2e8f0; background: #f8fafc; color: #1f2937; font-size: 0.9rem; font-weight: 700; }
.procurement-detail-card dl { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; padding: 0.25rem 1rem 0.75rem; column-gap: 1.75rem; }
.procurement-detail-card .detail-field { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); align-items: center; min-height: 2.65rem; border-bottom: 1px dashed #e2e8f0; font-size: 0.82rem; }
.procurement-detail-card .detail-field:last-child, .procurement-detail-card .detail-field:nth-last-child(2):nth-child(odd) { border-bottom-color: transparent; }
.procurement-detail-card dt { color: #64748b; }
.procurement-detail-card dd { min-width: 0; margin: 0; color: #1f2937; font-weight: 500; overflow-wrap: anywhere; }
.sea-progress-track { display: flex; align-items: flex-start; }
.sea-progress-step { position: relative; display: flex; flex: 1; flex-direction: column; align-items: center; gap: 0.45rem; color: #94a3b8; font-size: 0.78rem; text-align: center; }
.sea-progress-step:not(:last-child)::after { content: ''; position: absolute; top: 0.78rem; left: 58%; width: 84%; height: 2px; background: #e2e8f0; }
.sea-progress-step span { position: relative; z-index: 1; display: grid; width: 1.6rem; height: 1.6rem; place-items: center; border-radius: 50%; background: #e2e8f0; color: #64748b; font-weight: 700; }
.sea-progress-step.is-current { color: #1565c0; }
.sea-progress-step.is-current span { background: #1565c0; color: #fff; }
.sea-progress-step.is-done { color: #0f766e; }
.sea-progress-step.is-done span { background: #ccfbf1; color: #0f766e; }
.sea-edit-overlay { position: fixed; inset: 0; z-index: 1200; display: grid; place-items: center; padding: 1rem; background: rgba(15, 23, 42, 0.55); }
.sea-edit-overlay[hidden] { display: none; }
.sea-edit-dialog { display: flex; width: min(820px, 100%); max-height: min(88vh, 820px); flex-direction: column; overflow: hidden; border-radius: 0.75rem; background: #fff; box-shadow: 0 24px 60px rgba(15, 23, 42, 0.24); }
.sea-edit-header, .sea-edit-footer { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1rem 1.25rem; border-bottom: 1px solid #e2e8f0; }
.sea-edit-header h2 { margin: 0; color: #0f172a; font-size: 1.1rem; font-weight: 700; }
.sea-edit-close { display: grid; width: 2.25rem; height: 2.25rem; place-items: center; border-radius: 0.5rem; color: #64748b; }
.sea-edit-close:hover { background: #f1f5f9; color: #0f172a; }
.sea-edit-body { overflow-y: auto; padding: 1.25rem; }
.sea-edit-footer { justify-content: flex-end; border-top: 1px solid #e2e8f0; border-bottom: 0; }
@media (max-width: 780px) { .sea-page-header, .procurement-detail-header { align-items: flex-start; flex-direction: column; } .sea-order-filters { grid-template-columns: 1fr; } .sea-filter-actions { grid-column: 1; justify-content: flex-start; } .sea-detail-grid, .procurement-detail-grid { grid-template-columns: 1fr; } .sea-detail-section dl { grid-template-columns: 6rem minmax(0, 1fr); } .procurement-detail-card.is-product { grid-column: auto; } .procurement-detail-card dl { grid-template-columns: 1fr; } .procurement-detail-card .detail-field { grid-template-columns: 6.5rem minmax(0, 1fr); } .sea-edit-dialog { max-height: 94vh; } }
.dash-nav-link.logout { color: #fca5a5; background: rgba(239, 68, 68, 0.10); margin-top: 0.2rem; }
.dash-nav-link.logout:hover { background: rgba(239, 68, 68, 0.22); color: #fff; }
/* ===== 柏悦客户端组件样式(已统一作用域到 .client-shell)===== */
.client-shell * {
box-sizing: border-box; margin: 0; padding: 0; }
.client-shell html, .client-shell body {
overflow-x: hidden; max-width: 100vw; }
.client-shell body {
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; background: #f5f7fa; color: #262626; display: flex; min-height: 100vh; }
.client-shell /* ===== SIDEBAR ===== */
.sidebar {
width: 240px; background: rgba(255,255,255,0.95); backdrop-filter: blur(20px); border-right: 1px solid rgba(255,255,255,0.3);
display: flex; flex-direction: column;
position: fixed; left: 0; top: 64px; bottom: 0;
z-index: 100; box-shadow: 2px 0 20px rgba(0,0,0,.08);
transition: all .3s cubic-bezier(.4,0,.2,1);
}
.client-shell .sidebar.collapsed {
width: 0; transform: translateX(-100%); border-right: none; overflow: hidden; }
.client-shell .nav {
flex: 1; overflow-y: auto; padding: 16px 12px; }
.client-shell .nav-item {
display: flex; align-items: center; gap: 10px;
padding: 12px 16px; font-size: 13px; color: #595959;
cursor: pointer; transition: all .3s cubic-bezier(.4,0,.2,1);
position: relative; text-decoration: none; border-radius: 10px;
}
.client-shell .nav-item:hover {
background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); color: #667eea; transform: translateX(3px); }
.client-shell .nav-item.active {
background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; font-weight: 500; box-shadow: 0 4px 15px rgba(102,126,234,0.3); }
.client-shell .nav-item.active::before {
content: ''; position: absolute; left: -8px; top: 50%; transform: translateY(-50%);
width: 4px; height: 24px; background: linear-gradient(135deg, #667eea, #f093fb); border-radius: 0 4px 4px 0;
}
.client-shell .nav-item .nav-icon, .client-shell .nav-group-left .nav-icon {
width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.client-shell .nav-item .nav-icon svg, .client-shell .nav-group-left .nav-icon svg {
width: 16px; height: 16px; }
.client-shell .nav-divider {
height: 1px; background: linear-gradient(90deg, transparent, rgba(102,126,234,0.2), transparent); margin: 12px 8px; }
.client-shell .nav-group {
display: flex; flex-direction: column; }
.client-shell .nav-group-header {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 16px; font-size: 13px; color: #595959;
cursor: pointer; transition: all .3s cubic-bezier(.4,0,.2,1); user-select: none;
border-radius: 10px;
}
.client-shell .nav-group-header:hover {
background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); color: #667eea; }
.client-shell .nav-group-header .nav-group-left {
display: flex; align-items: center; gap: 10px; }
.client-shell .nav-group-header .nav-arrow {
font-size: 10px; color: #8c8c8c; transition: transform .3s; }
.client-shell .nav-group-header.expanded .nav-arrow {
transform: rotate(180deg); }
.client-shell .nav-group-header.active {
color: #667eea; font-weight: 600; }
.client-shell .nav-sub {
display: none; flex-direction: column; }
.client-shell .nav-sub.expanded {
display: flex; }
.client-shell .nav-sub-item {
display: flex; align-items: center; gap: 8px;
padding: 10px 16px 10px 40px; font-size: 13px; color: #8c8c8c;
cursor: pointer; text-decoration: none; transition: all .3s cubic-bezier(.4,0,.2,1);
border-radius: 8px;
}
.client-shell .nav-sub-item:hover {
color: #667eea; background: linear-gradient(135deg, rgba(102,126,234,0.05), rgba(118,75,162,0.05)); }
.client-shell .nav-sub-item.active {
color: #fff; font-weight: 600; background: linear-gradient(135deg, #667eea, #764ba2); }
.client-shell /* ===== TOPBAR ===== */
.topbar {
position: fixed; top: 0; left: 0; right: 0; height: 64px;
background: linear-gradient(135deg, rgba(102,126,234,0.95), rgba(118,75,162,0.95), rgba(240,147,251,0.95));
backdrop-filter: blur(20px); display: flex; align-items: center;
justify-content: space-between; padding: 0 28px; z-index: 99;
box-shadow: 0 4px 30px rgba(102,126,234,.35);
border-bottom: 1px solid rgba(255,255,255,.2);
}
.client-shell .topbar-left {
display: flex; align-items: center; gap: 18px; }
.client-shell .topbar-menu-icon {
width: 24px; height: 20px; display: flex; flex-direction: column;
justify-content: space-between; cursor: pointer; padding: 4px 0;
}
.client-shell .topbar-menu-icon span {
display: block; height: 2px; background: #fff; border-radius: 3px; transition: all .3s; }
.client-shell .topbar-menu-icon:hover span:first-child {
width: 14px; transform: translateX(2px); }
.client-shell .topbar-menu-icon:hover span:last-child {
width: 14px; transform: translateX(-2px); }
.client-shell .topbar-logo-small {
width: 36px; height: 36px; object-fit: contain; display: block; border-radius: 10px; background: rgba(255,255,255,0.9); box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.client-shell .topbar-brand-text {
font-size: 22px; font-weight: 700; color: #fff; letter-spacing: 2px; text-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.client-shell .topbar-right {
display: flex; align-items: center; gap: 18px; }
.client-shell .topbar-icon-btn {
width: 38px; height: 38px; display: flex; align-items: center; justify-content: center;
border-radius: 50%; cursor: pointer; color: #fff; font-size: 16px; transition: all .3s;
background: rgba(255,255,255,0.1);
}
.client-shell .topbar-icon-btn:hover {
background: rgba(255,255,255,0.25); transform: scale(1.1); }
.client-shell .topbar-avatar {
width: 34px; height: 34px; border-radius: 50%; background: linear-gradient(135deg, rgba(255,255,255,0.3), rgba(255,255,255,0.15));
display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: 700; color: #fff; cursor: pointer; border: 2px solid rgba(255,255,255,0.4);
transition: all .3s;
}
.client-shell .topbar-avatar:hover {
transform: scale(1.1); box-shadow: 0 4px 15px rgba(255,255,255,0.3); }
.client-shell .topbar-logout {
font-size: 13px; color: rgba(255,255,255,.9); cursor: pointer; transition: all .3s; padding: 6px 14px; border-radius: 8px; }
.client-shell .topbar-logout:hover {
color: #fff; background: rgba(255,255,255,0.15); }
.client-shell /* ===== MAIN CONTENT ===== */
.main {
margin-left: 240px; margin-top: 64px; flex: 1;
padding: 28px; min-height: calc(100vh - 64px);
transition: all .3s cubic-bezier(.4,0,.2,1);
max-width: calc(100vw - 0px); overflow-x: hidden;
}
.client-shell .main.expanded {
margin-left: 0; }
.client-shell /* Page content panels */
.page-content {
display: none; }
.client-shell .page-content.active {
display: block; }
.client-shell /* ===== SHARED: Page Header / Breadcrumb ===== */
.page-header {
display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 18px; }
.client-shell .page-title {
font-size: 20px; font-weight: 700; color: #1e293b; }
.client-shell .page-subtitle {
font-size: 13px; color: #71717a; margin-top: 6px; }
.client-shell .breadcrumb {
display: flex; align-items: center; gap: 8px; font-size: 13px; color: #71717a; margin-bottom: 18px; }
.client-shell .breadcrumb a {
color: #71717a; text-decoration: none; transition: all .2s; }
.client-shell .breadcrumb a:hover {
color: #667eea; }
.client-shell .link-primary {
color: #667eea; text-decoration: none; font-size: 13px; transition: all .2s; }
.client-shell .link-primary:hover {
text-decoration: underline; color: #764ba2; }
.client-shell /* Company info tabs */
.info-tabs {
display: flex; gap: 0; border: 1.5px solid rgba(102,126,234,0.2); border-radius: 14px; overflow: hidden; width: fit-content; margin-bottom: 20px; background: rgba(255,255,255,0.95); }
.client-shell .info-tab {
padding: 12px 32px; font-size: 13px; cursor: pointer; color: #595959; background: transparent; border: none; transition: all .3s cubic-bezier(.4,0,.2,1); border-right: 1px solid rgba(102,126,234,0.15); font-weight: 500; }
.client-shell .info-tab:last-child {
border-right: none; }
.client-shell .info-tab:hover {
color: #667eea; background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); }
.client-shell .info-tab.active {
background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; font-weight: 600; }
.client-shell .info-tab-content {
display: none; }
.client-shell .info-tab-content.active {
display: block; }
.client-shell .balance-card {
display: flex; align-items: center; justify-content: space-between;
background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08));
border: 1px solid rgba(102,126,234,0.22); border-radius: 14px;
padding: 18px 22px; margin-bottom: 18px; }
.client-shell .balance-label {
font-size: 13px; color: #64748b; margin-bottom: 4px; }
.client-shell .balance-amount {
font-size: 1.6rem; font-weight: 700; color: #4338ca; line-height: 1.1; }
.client-shell .balance-actions {
display: flex; align-items: center; gap: 10px; }
.client-shell .balance-detail-btn {
display: inline-flex; align-items: center; gap: 7px; padding: 8px 18px;
border: 1px solid rgba(102,126,234,0.38); border-radius: 8px; background: #fff;
color: #4f46e5; font-size: 13px; font-weight: 600; cursor: pointer; transition: all .2s ease; }
.client-shell .balance-detail-btn:hover {
border-color: #667eea; background: rgba(102,126,234,0.07); transform: translateY(-1px); }
.balance-detail-modal { width: 760px; }
.balance-detail-summary {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 18px; }
.balance-summary-item {
padding: 14px 16px; border: 1px solid #e8edf5; border-radius: 12px; background: #f8fafc; }
.balance-summary-label { color: #64748b; font-size: 12px; margin-bottom: 5px; }
.balance-summary-value { color: #0f172a; font-size: 19px; font-weight: 700; }
.balance-summary-value.income { color: #059669; }
.balance-summary-value.expense { color: #e11d48; }
.balance-detail-filter {
display: flex; align-items: center; justify-content: space-between; gap: 12px;
flex-wrap: wrap; margin-bottom: 14px; padding: 12px; border-radius: 12px; background: #f8fafc; }
.balance-quick-periods { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.balance-period-btn {
padding: 6px 11px; border: 1px solid #dbe3ef; border-radius: 7px; background: #fff;
color: #64748b; font-size: 12px; cursor: pointer; transition: all .2s ease; }
.balance-period-btn:hover, .balance-period-btn.active {
border-color: #667eea; background: rgba(102,126,234,.08); color: #4f46e5; }
.balance-date-range { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.balance-date-range input {
width: 142px; padding: 6px 9px; border: 1px solid #dbe3ef; border-radius: 7px;
background: #fff; color: #334155; font-size: 12px; outline: none; }
.balance-date-range input:focus { border-color: #667eea; box-shadow: 0 0 0 2px rgba(102,126,234,.1); }
.balance-filter-action {
padding: 7px 13px; border: 0; border-radius: 7px; background: #4f46e5;
color: #fff; font-size: 12px; font-weight: 600; cursor: pointer; }
.balance-filter-reset { background: #fff; border: 1px solid #dbe3ef; color: #64748b; }
.balance-detail-table-wrap { overflow-x: auto; border: 1px solid #e8edf5; border-radius: 12px; }
.balance-detail-table { width: 100%; border-collapse: collapse; min-width: 680px; font-size: 13px; }
.balance-detail-table th {
padding: 11px 14px; background: #f8fafc; color: #64748b; font-weight: 600; text-align: left; white-space: nowrap; }
.balance-detail-table td { padding: 13px 14px; border-top: 1px solid #eef2f7; color: #334155; white-space: nowrap; }
.balance-detail-table .amount-in { color: #059669; font-weight: 600; }
.balance-detail-table .amount-out { color: #e11d48; font-weight: 600; }
.balance-status {
display: inline-flex; padding: 3px 9px; border-radius: 999px; background: #ecfdf5; color: #059669; font-size: 11px; }
.balance-empty-row td { padding: 30px 14px; color: #94a3b8; text-align: center; }
.balance-detail-note { margin-top: 12px; color: #94a3b8; font-size: 12px; text-align: right; }
@media (max-width: 640px) {
.client-shell .balance-card { align-items: flex-start; gap: 16px; flex-direction: column; }
.client-shell .balance-actions { width: 100%; }
.client-shell .balance-actions button { flex: 1; justify-content: center; }
.balance-detail-summary { grid-template-columns: 1fr; }
}
.client-shell /* Company info cards */
.info-card {
background: rgba(255,255,255,0.95); backdrop-filter: blur(20px); border-radius: 18px; padding: 28px; box-shadow: 0 8px 32px rgba(0,0,0,.08); border: 1px solid rgba(255,255,255,0.4); }
.client-shell .info-section-title {
font-size: 15px; font-weight: 700; color: #667eea; margin-bottom: 18px; padding-bottom: 12px; border-bottom: 1px solid rgba(102,126,234,0.1); }
.client-shell .info-row {
display: flex; margin-bottom: 14px; }
.client-shell .info-label {
width: 100px; color: #8c8c8c; font-size: 13px; flex-shrink: 0; }
.client-shell .info-value {
flex: 1; color: #262626; font-size: 13px; }
.client-shell .info-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 16px 40px; }
.client-shell .info-doc-table {
width: 100%; border-collapse: collapse; font-size: 13px; }
.client-shell .info-doc-table thead th {
background: #fafafa; padding: 10px 12px; text-align: left; font-weight: 500; color: #8c8c8c; border-bottom: 1px solid #f0f0f0; }
.client-shell .info-doc-table tbody td {
padding: 12px; border-bottom: 1px solid #f5f5f5; color: #262626; }
.client-shell .info-doc-table tbody tr:last-child td {
border-bottom: none; }
.client-shell .doc-status {
color: #52c41a; font-size: 12px; }
.client-shell .doc-btn {
padding: 3px 12px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 12px; color: #595959; background: #fff; cursor: pointer; }
.client-shell .doc-btn:hover {
border-color: #1677ff; color: #1677ff; }
.client-shell .info-two-col {
display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.client-shell .breadcrumb a:hover {
color: #667eea; }
.client-shell .breadcrumb .sep {
color: #a1a1aa; }
.client-shell .breadcrumb .current {
color: #1e293b; font-weight: 500; }
.client-shell /* ===== SHARED: Card ===== */
.card {
background: rgba(255,255,255,0.95); backdrop-filter: blur(20px); border-radius: 20px;
box-shadow: 0 8px 32px rgba(0,0,0,.08), 0 2px 8px rgba(0,0,0,.04);
border: 1px solid rgba(255,255,255,0.4);
margin-bottom: 14px; overflow: hidden;
transition: all .4s cubic-bezier(.4,0,.2,1);
}
.client-shell .card:hover {
transform: translateY(-2px); box-shadow: 0 12px 40px rgba(102,126,234,.15); }
.client-shell .card-header {
display: flex; align-items: center; justify-content: space-between;
padding: 18px 20px 14px; border-bottom: 1px solid rgba(102,126,234,0.1);
}
.client-shell .card-title {
font-size: 18px; font-weight: 700; color: #1e293b; }
.client-shell .card-subtitle {
font-size: 13px; color: #8c8c8c; margin-top: 4px; }
.client-shell .card-body {
padding: 18px 20px; }
.client-shell /* ===== SHARED: Buttons ===== */
.btn {
display: inline-flex; align-items: center; gap: 8px;
padding: 10px 20px; border-radius: 12px; font-size: 13px;
cursor: pointer; border: none; transition: all .3s cubic-bezier(.4,0,.2,1); font-family: inherit;
font-weight: 500;
}
.client-shell .btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; box-shadow: 0 4px 15px rgba(102,126,234,0.35); }
.client-shell .btn-primary:hover {
transform: translateY(-2px); box-shadow: 0 6px 20px rgba(102,126,234,0.45); }
.client-shell .btn-default {
background: rgba(255,255,255,0.9); color: #595959; border: 1.5px solid rgba(102,126,234,0.2); }
.client-shell .btn-default:hover {
border-color: #667eea; color: #667eea; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(102,126,234,0.15); }
.client-shell .btn-danger {
background: rgba(255,255,255,0.9); color: #ff6b6b; border: 1.5px solid rgba(255,107,107,0.3); }
.client-shell .btn-danger:hover {
background: linear-gradient(135deg, rgba(255,107,107,0.05), rgba(255,77,79,0.05)); transform: translateY(-1px); }
.client-shell .btn-ghost {
background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); color: #667eea; border: 1px solid rgba(102,126,234,0.2); }
.client-shell .btn-ghost:hover {
background: linear-gradient(135deg, rgba(102,126,234,0.15), rgba(118,75,162,0.15)); transform: translateY(-1px); }
.client-shell .btn-sm {
padding: 6px 12px; font-size: 12px; border-radius: 8px; }
.client-shell .btn-purple {
background: linear-gradient(135deg, #764ba2, #f093fb); color: #fff; border: none; box-shadow: 0 4px 15px rgba(118,75,162,0.35); }
.client-shell .btn-purple:hover {
transform: translateY(-2px); box-shadow: 0 6px 20px rgba(118,75,162,0.45); }
.client-shell /* Flat toolbar buttons */
.btn-flat {
display: inline-flex; align-items: center; gap: 6px;
padding: 8px 16px; border-radius: 10px; font-size: 13px;
cursor: pointer; border: 1.5px solid rgba(102,126,234,0.2); transition: all .3s cubic-bezier(.4,0,.2,1);
font-family: inherit; background: rgba(255,255,255,0.9); color: #595959;
}
.client-shell .btn-flat:hover {
border-color: #667eea; color: #667eea; background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); transform: translateY(-1px); }
.client-shell .btn-flat-blue {
background: linear-gradient(135deg, #4f46e5, #7c3aed); color: #fff; border-color: transparent; box-shadow: 0 4px 16px rgba(79, 70, 229, 0.35); border-radius: 10px; padding: 10px 20px; font-weight: 500; }
.client-shell .btn-flat-blue:hover {
background: linear-gradient(135deg, #4338ca, #6d28d9); transform: translateY(-2px); box-shadow: 0 8px 22px rgba(79, 70, 229, 0.45); }
.client-shell .btn-flat-blue:active {
transform: translateY(0); box-shadow: 0 4px 12px rgba(79, 70, 229, 0.35); }
.client-shell .btn-flat-purple {
background: linear-gradient(135deg, #764ba2, #f093fb); color: #fff; border-color: transparent; box-shadow: 0 4px 15px rgba(118,75,162,0.35); }
.client-shell .btn-flat-purple:hover {
background: linear-gradient(135deg, #667eea, #764ba2); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(118,75,162,0.45); }
.client-shell .btn-flat-red {
color: #ff6b6b; border-color: rgba(255,107,107,0.3); background: rgba(255,255,255,0.9); }
.client-shell .btn-flat-red:hover {
background: linear-gradient(135deg, rgba(255,107,107,0.08), rgba(255,77,79,0.08)); border-color: #ff6b6b; transform: translateY(-1px); }
.client-shell .btn-flat .icon-svg {
width: 14px; height: 14px; flex-shrink: 0; }
.client-shell /* ===== PAGE: 头程下单 - Form ===== */
.form-group {
display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; min-height: 32px; }
.client-shell .form-group:last-child {
margin-bottom: 0; }
.client-shell .form-label {
font-size: 13px; color: #595959; font-weight: 400; min-width: 80px; text-align: right; padding-top: 5px; line-height: 22px; }
.client-shell .form-label .required {
color: #ff4d4f; margin-right: 4px; }
.client-shell .form-content {
flex: 1; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.client-shell .radio-group {
display: flex; flex-wrap: wrap; gap: 24px; align-items: center; }
.client-shell .radio-label {
display: inline-flex; align-items: center; gap: 10px; font-size: 13px; color: #262626; cursor: pointer; user-select: none; transition: all .2s; }
.client-shell .radio-label:hover {
color: #667eea; }
.client-shell .radio-label input[type=radio] {
width: 16px; height: 16px; accent-color: #667eea; cursor: pointer; margin: 0; }
.client-shell .radio-label.disabled {
color: #bfbfbf; cursor: not-allowed; }
.client-shell .radio-label.disabled input[type=radio] {
accent-color: #d9d9d9; cursor: not-allowed; }
.client-shell .form-input, .client-shell .form-select, .client-shell .form-textarea {
height: 40px; padding: 0 16px; border: 1.5px solid rgba(102,126,234,0.2); border-radius: 10px;
font-size: 13px; color: #262626; background: rgba(255,255,255,0.95); outline: none; transition: all .3s cubic-bezier(.4,0,.2,1); font-family: inherit;
}
.client-shell .form-input:focus, .client-shell .form-select:focus, .client-shell .form-textarea:focus {
border-color: #667eea; box-shadow: 0 0 0 4px rgba(102,126,234,0.1); }
.client-shell .form-input::placeholder, .client-shell .form-textarea::placeholder {
color: #bfbfbf; }
.client-shell /* 强制表单控件不撑宽页面 */
.main .form-input, .client-shell .main .form-select, .client-shell .main .form-textarea {
min-width: 0 !important; max-width: 100% !important; }
.client-shell .main .form-content {
flex: 1; min-width: 0; }
.client-shell .form-textarea {
width: 100%; max-width: 100%; height: 90px; padding: 12px 16px; resize: vertical; border-radius: 12px; }
.client-shell .input-wrap {
position: relative; display: flex; align-items: center; }
.client-shell .input-wrap .form-input {
padding-right: 32px; }
.client-shell .input-suffix {
position: absolute; right: 10px; color: #bfbfbf; font-size: 14px; pointer-events: none; }
.client-shell .select-wrap {
position: relative; display: inline-block; }
.client-shell .select-wrap .form-select {
min-width: 160px; padding-right: 32px; appearance: auto; }
.client-shell .date-range {
display: flex; align-items: center; gap: 8px; }
.client-shell .date-range .form-input {
width: 160px; }
.client-shell .date-sep {
color: #bfbfbf; font-size: 13px; white-space: nowrap; }
.client-shell /* Dest type tabs */
.dest-type-tabs {
display: flex; gap: 0; border: 1.5px solid rgba(102,126,234,0.2); border-radius: 14px; overflow: hidden; width: fit-content; background: rgba(255,255,255,0.95); }
.client-shell .dest-tab {
padding: 10px 28px; font-size: 13px; cursor: pointer; color: #595959; background: transparent; border: none; transition: all .3s cubic-bezier(.4,0,.2,1); border-right: 1px solid rgba(102,126,234,0.15); font-weight: 500; }
.client-shell .dest-tab:last-child {
border-right: none; }
.client-shell .dest-tab:hover {
color: #667eea; background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); }
.client-shell .dest-tab.active {
background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; font-weight: 600; }
.client-shell .dest-content {
display: none; }
.client-shell .dest-content.active {
display: block; }
.client-shell .dest-content-bulk {
display: none; }
.client-shell .dest-content-bulk.active {
display: block; }
.client-shell /* Goods table */
.table-toolbar {
display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.client-shell .table-actions {
display: flex; gap: 10px; }
.client-shell .table-wrap {
overflow-x: auto; border: 1px solid rgba(102,126,234,0.15); border-radius: 16px; background: rgba(255,255,255,0.95); }
.client-shell table.goods-table {
width: 100%; border-collapse: collapse; font-size: 12px; }
.client-shell .goods-table thead th {
background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); border-bottom: 1px solid rgba(102,126,234,0.15); padding: 14px 10px; text-align: left; font-weight: 600; color: #667eea; white-space: nowrap; position: sticky; top: 0; z-index: 1; }
.client-shell .goods-table thead th:first-child {
width: 48px; text-align: center; }
.client-shell .goods-table tbody tr {
border-bottom: 1px solid rgba(102,126,234,0.08); transition: all .3s cubic-bezier(.4,0,.2,1); }
.client-shell .goods-table tbody tr:last-child {
border-bottom: none; }
.client-shell .goods-table tbody tr:hover {
background: linear-gradient(135deg, rgba(102,126,234,0.05), rgba(118,75,162,0.05)); }
.client-shell .goods-table tbody td {
padding: 12px 10px; vertical-align: middle; }
.client-shell .goods-table tbody td:first-child {
text-align: center; color: #bfbfbf; font-size: 12px; }
.client-shell .td-input {
width: 100%; height: 36px; padding: 0 10px; border: 1.5px solid transparent; border-radius: 8px; font-size: 12px; color: #262626; background: transparent; outline: none; transition: all .3s cubic-bezier(.4,0,.2,1); font-family: inherit; }
.client-shell .td-input:focus, .client-shell .td-input:hover {
border-color: rgba(102,126,234,0.3); background: rgba(255,255,255,0.9); }
.client-shell .td-input::placeholder {
color: #bfbfbf; }
.client-shell .empty-row td {
padding: 28px 0; text-align: center; color: #bfbfbf; font-size: 13px; }
.client-shell .upload-link {
display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: #667eea; cursor: pointer; padding: 10px 22px; border: 2px dashed rgba(102,126,234,0.4); border-radius: 12px; background: linear-gradient(135deg, rgba(102,126,234,0.05), rgba(118,75,162,0.05)); transition: all .3s cubic-bezier(.4,0,.2,1); font-weight: 600; }
.client-shell .upload-link:hover {
background: linear-gradient(135deg, rgba(102,126,234,0.1), rgba(118,75,162,0.1)); border-color: #667eea; transform: translateY(-1px); }
.client-shell /* Upload zone (image upload) */
.upload-zone {
border: 2px dashed rgba(102,126,234,0.4); border-radius: 14px; padding: 32px 20px; text-align: center; cursor: pointer; background: linear-gradient(135deg, rgba(102,126,234,0.03), rgba(118,75,162,0.03)); transition: all .3s cubic-bezier(.4,0,.2,1); }
.client-shell .upload-zone:hover {
border-color: #667eea; background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); }
.client-shell .upload-zone.dragover {
border-color: #667eea; background: linear-gradient(135deg, rgba(102,126,234,0.12), rgba(118,75,162,0.12)); }
.client-shell .upload-zone-icon {
font-size: 36px; margin-bottom: 8px; }
.client-shell .upload-zone-text {
font-size: 14px; color: #262626; font-weight: 600; margin-bottom: 4px; }
.client-shell .upload-zone-hint {
font-size: 12px; color: #8c8c8c; }
.client-shell .upload-preview {
display: flex; flex-wrap: wrap; gap: 12px; margin-top: 14px; }
.client-shell .upload-preview-item {
position: relative; width: 110px; height: 110px; border-radius: 10px; overflow: hidden; border: 1px solid rgba(102,126,234,0.2); background: #fff; }
.client-shell .upload-preview-item img {
width: 100%; height: 100%; object-fit: cover; }
.client-shell .upload-preview-item .upload-remove {
position: absolute; top: 4px; right: 4px; width: 22px; height: 22px; background: rgba(0,0,0,0.6); color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; cursor: pointer; line-height: 1; }
.client-shell .upload-preview-item .upload-remove:hover {
background: #ff4d4f; }
.client-shell .upload-preview-item .upload-filename {
position: absolute; bottom: 0; left: 0; right: 0; padding: 4px 6px; background: rgba(0,0,0,0.55); color: #fff; font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.client-shell .row-upload-cell {
display: inline-flex; align-items: center; gap: 6px; cursor: pointer; padding: 4px 10px; border: 1px dashed #d9d9d9; border-radius: 6px; font-size: 12px; color: #667eea; background: #fafafa; }
.client-shell .row-upload-cell:hover {
border-color: #667eea; background: #f0f4ff; }
.client-shell .row-upload-cell input {
display: none; }
.client-shell .labeling-file-upload {
width: 142px; max-width: 142px; }
.client-shell .labeling-file-upload .row-upload-text {
min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.client-shell .row-img-cell {
width: 60px; height: 60px; border: 1px dashed #d9d9d9; border-radius: 6px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: #bfbfbf; font-size: 18px; background: #fafafa; position: relative; overflow: hidden; }
.client-shell .row-img-cell:hover {
border-color: #667eea; color: #667eea; }
.client-shell .row-img-cell img {
width: 100%; height: 100%; object-fit: cover; }
.client-shell .row-img-cell input {
display: none; }
.client-shell .download-link {
display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: #595959; text-decoration: none; padding: 10px 22px; border: 1.5px solid rgba(102,126,234,0.2); border-radius: 12px; background: rgba(255,255,255,0.95); transition: all .3s cubic-bezier(.4,0,.2,1); font-weight: 500; }
.client-shell .download-link:hover {
border-color: #667eea; color: #667eea; transform: translateY(-1px); }
.client-shell /* Action row buttons — outlined style matching screenshots */
.btn-outline-blue {
display: inline-flex; align-items: center; gap: 8px; padding: 10px 22px; border: 2px solid #667eea; border-radius: 12px; color: #667eea; background: rgba(255,255,255,0.9); font-size: 13px; cursor: pointer; transition: all .3s cubic-bezier(.4,0,.2,1); font-family: inherit; font-weight: 500; }
.client-shell .btn-outline-blue:hover {
background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 15px rgba(102,126,234,0.35); }
.client-shell .btn-outline-red {
display: inline-flex; align-items: center; gap: 8px; padding: 10px 22px; border: 2px solid #ff6b6b; border-radius: 12px; color: #ff6b6b; background: rgba(255,255,255,0.9); font-size: 13px; cursor: pointer; transition: all .3s cubic-bezier(.4,0,.2,1); font-family: inherit; font-weight: 500; }
.client-shell .btn-outline-red:hover {
background: linear-gradient(135deg, #ff6b6b, #ff8787); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 15px rgba(255,107,107,0.35); }
.client-shell .form-footer {
background: rgba(255,255,255,0.95); backdrop-filter: blur(20px); border-radius: 16px; padding: 20px 28px; display: flex; flex-direction: column; align-items: center; gap: 12px; box-shadow: 0 8px 32px rgba(0,0,0,.08); border: 1px solid rgba(255,255,255,0.4); position: sticky; bottom: 0; z-index: 50; }
.client-shell .footer-tips {
font-size: 12px; color: #8c8c8c; display: flex; align-items: center; gap: 8px; }
.client-shell .footer-tips .tip-icon {
color: #ffd93d; }
.client-shell .btn-submit {
background: linear-gradient(135deg, #667eea, #764ba2, #f093fb); color: #fff; border: none; padding: 12px 48px; border-radius: 14px; font-size: 15px; font-weight: 600; cursor: pointer; transition: all .3s cubic-bezier(.4,0,.2,1); box-shadow: 0 6px 20px rgba(102,126,234,.4); }
.client-shell .btn-submit:hover {
background: linear-gradient(135deg, #764ba2, #f093fb, #667eea); box-shadow: 0 8px 25px rgba(102,126,234,.5); transform: translateY(-2px); }
.client-shell .btn-submit:active {
transform: translateY(0); }
.client-shell /* ===== 费用预估 ===== */
.cost-estimate {
margin-top: 16px; }
.client-shell .cost-estimate .card-header {
border-bottom: none; padding-bottom: 0; }
.client-shell .cost-cards {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.client-shell .cost-card {
background: #fff; border-radius: 20px; padding: 28px 20px; text-align: center;
cursor: pointer; transition: all .35s cubic-bezier(.4,0,.2,1);
border: 2px solid rgba(102,126,234,.08);
position: relative;
box-shadow: 0 2px 12px rgba(102,126,234,.04);
}
.client-shell .cost-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 28px rgba(102,126,234,.15);
border-color: rgba(102,126,234,.25);
}
.client-shell .cost-card.active {
background: linear-gradient(135deg, rgba(102,126,234,.06), rgba(118,75,162,.06));
border-color: #667eea;
box-shadow: 0 6px 24px rgba(102,126,234,.22), inset 0 1px 0 rgba(255,255,255,.6);
}
.client-shell .cost-badge {
position: absolute; top: -10px; right: 18px;
padding: 4px 14px; border-radius: 12px; font-size: 11px; font-weight: 600; color: #fff;
line-height: 1.4;
}
.client-shell .cost-badge.fastest {
background: linear-gradient(135deg, #5c6bc0, #3949ab); }
.client-shell .cost-badge.recommend {
background: linear-gradient(135deg, #667eea, #764ba2); box-shadow: 0 3px 10px rgba(102,126,234,.35); }
.client-shell .cost-badge.economy {
background: linear-gradient(135deg, #78909c, #546e7a); }
.client-shell .cost-days {
font-size: 26px; font-weight: 700; color: #333; margin-bottom: 14px; letter-spacing: 1px; }
.client-shell .cost-price {
font-size: 34px; font-weight: 800; color: #667eea; margin-bottom: 10px; line-height: 1.2; }
.client-shell .cost-price span {
font-size: 20px; font-weight: 600; }
.client-shell .cost-desc {
font-size: 13px; color: #999; }
.client-shell input[type=checkbox] {
width: 16px; height: 16px; accent-color: #667eea; cursor: pointer; }
.client-shell .col-checkbox {
width: 36px; }
.client-shell /* ===== PAGE: 订单列表 - Filter ===== */
.filter-grid {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px 20px; align-items: center; }
.client-shell .filter-item {
display: flex; align-items: center; gap: 8px; }
.client-shell .filter-item.span2 {
grid-column: span 2; }
.client-shell .filter-label {
font-size: 13px; color: #595959; white-space: nowrap; flex-shrink: 0; min-width: 70px; text-align: right; }
.client-shell .filter-input, .client-shell .filter-select {
flex: 1; height: 40px; padding: 0 16px; border: 1.5px solid rgba(102,126,234,0.2); border-radius: 10px; font-size: 13px; color: #262626; background: rgba(255,255,255,0.95); outline: none; font-family: inherit; min-width: 0; transition: all .3s cubic-bezier(.4,0,.2,1); }
.client-shell .filter-input:focus, .client-shell .filter-select:focus {
border-color: #667eea; box-shadow: 0 0 0 4px rgba(102,126,234,0.1); }
.client-shell .filter-input::placeholder {
color: #bfbfbf; }
.client-shell .date-range-wrap {
display: flex; align-items: center; gap: 6px; flex: 1; }
.client-shell .date-range-wrap .filter-input {
flex: 1; }
.client-shell .filter-actions {
display: flex; align-items: center; gap: 12px; margin-top: 16px; padding-top: 16px; border-top: 1px solid rgba(79, 70, 229, 0.1); flex-wrap: wrap; }
.client-shell .filter-actions > [hidden] {
display: none; }
.client-shell /* Data table */
.table-wrap-simple {
overflow-x: auto; border: 1px solid rgba(102,126,234,0.15); border-radius: 16px; background: rgba(255,255,255,0.95); }
.client-shell .data-table {
width: 100%; border-collapse: collapse; font-size: 12px; }
.client-shell .data-table thead th {
background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); padding: 14px 14px; text-align: left; font-weight: 600; color: #667eea; border-bottom: 1px solid rgba(102,126,234,0.15); white-space: nowrap; }
.client-shell .data-table tbody td {
padding: 14px 14px; border-bottom: 1px solid rgba(102,126,234,0.08); color: #262626; white-space: nowrap; }
.client-shell .data-table tbody tr:hover {
background: linear-gradient(135deg, rgba(102,126,234,0.05), rgba(118,75,162,0.05)); }
.client-shell .data-table tbody tr:last-child td {
border-bottom: none; }
.client-shell .data-table .col-checkbox {
width: 40px; text-align: center; }
.client-shell .data-table .col-checkbox input {
cursor: pointer; }
.client-shell .empty-state {
text-align: center; padding: 60px 20px; color: #bfbfbf; }
.client-shell .empty-state .empty-icon {
font-size: 48px; margin-bottom: 12px; }
.client-shell .empty-state .empty-text {
font-size: 14px; }
.client-shell /* Quick Order Table */
.quick-table-wrap {
border: 1px solid rgba(102,126,234,0.15); border-radius: 16px; overflow: hidden; background: rgba(255,255,255,0.95); }
.client-shell .quick-table {
width: 100%; border-collapse: collapse; font-size: 13px; }
.client-shell .quick-table thead th {
background: linear-gradient(135deg, rgba(102,126,234,0.08), rgba(118,75,162,0.08)); padding: 14px 16px; text-align: left; font-weight: 600; color: #667eea; border-bottom: 1px solid rgba(102,126,234,0.15); }
.client-shell .quick-table tbody td {
padding: 16px 16px; border-bottom: 1px solid rgba(102,126,234,0.08); vertical-align: top; }
.client-shell .quick-table tbody tr:last-child td {
border-bottom: none; }
.client-shell .quick-table .select-wrap .form-select {
min-width: 120px; }
.client-shell .quick-row-actions {
display: flex; align-items: center; gap: 10px; white-space: nowrap; }
.client-shell .quick-row-actions a {
flex: 0 0 auto; }
.client-shell .product-line {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.client-shell .product-line:last-child {
margin-bottom: 0; }
.client-shell /* Tags */
.tag {
display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 8px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.client-shell .tag-blue {
background: linear-gradient(135deg, rgba(102,126,234,0.15), rgba(118,75,162,0.15)); color: #667eea; border: 1px solid rgba(102,126,234,0.3); }
.client-shell .tag-success {
background: linear-gradient(135deg, rgba(82,196,26,0.12), rgba(116,227,101,0.12)); color: #52c41a; border: 1px solid rgba(82,196,26,0.3); }
.client-shell .tag-warning {
background: linear-gradient(135deg, rgba(255,217,61,0.15), rgba(250,173,20,0.15)); color: #faad14; border: 1px solid rgba(250,173,20,0.3); }
.client-shell .tag-error {
background: linear-gradient(135deg, rgba(255,107,107,0.12), rgba(255,77,79,0.12)); color: #ff6b6b; border: 1px solid rgba(255,107,107,0.3); }
.client-shell .tag-info {
background: linear-gradient(135deg, rgba(102,126,234,0.15), rgba(118,75,162,0.15)); color: #667eea; border: 1px solid rgba(102,126,234,0.3); }
.client-shell .tag-default {
background: rgba(0,0,0,0.05); color: #8c8c8c; border: 1px solid rgba(0,0,0,0.1); }
.client-shell /* Pagination */
.pagination-bar {
display: flex; align-items: center; justify-content: space-between; padding: 16px 28px; border-top: 1px solid rgba(102,126,234,0.1); font-size: 13px; color: #8c8c8c; }
.client-shell .pagination-info {
display: flex; align-items: center; gap: 10px; }
.client-shell .pagination-ctrl {
display: flex; align-items: center; gap: 6px; }
.client-shell .page-btn {
min-width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border: 1.5px solid rgba(102,126,234,0.2); background: rgba(255,255,255,0.95); border-radius: 10px; font-size: 13px; color: #595959; cursor: pointer; transition: all .3s cubic-bezier(.4,0,.2,1); padding: 0 10px; font-weight: 500; }
.client-shell .page-btn:hover {
border-color: #667eea; color: #667eea; transform: translateY(-1px); }
.client-shell .page-btn.active {
background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; border-color: transparent; box-shadow: 0 4px 15px rgba(102,126,234,0.35); }
.client-shell .page-btn:disabled {
opacity: .5; cursor: not-allowed; border-color: rgba(0,0,0,0.1); color: #bfbfbf; }
.client-shell .page-size-select {
height: 36px; padding: 0 12px; border: 1.5px solid rgba(102,126,234,0.2); border-radius: 10px; font-size: 13px; background: rgba(255,255,255,0.95); outline: none; transition: all .3s cubic-bezier(.4,0,.2,1); }
.client-shell .page-size-select:focus {
border-color: #667eea; box-shadow: 0 0 0 4px rgba(102,126,234,0.1); }
.client-shell /* Scrollbar */
::-webkit-scrollbar {
width: 6px; height: 6px; }
.client-shell ::-webkit-scrollbar-track {
background: transparent; }
.client-shell ::-webkit-scrollbar-thumb {
background: #d9d9d9; border-radius: 3px; }
.client-shell ::-webkit-scrollbar-thumb:hover {
background: #bfbfbf; }
.client-shell /* ===== Modal ===== */
.modal-overlay {
display: none; position: fixed; inset: 0; z-index: 1000;
background: rgba(0,0,0,.45); backdrop-filter: blur(4px);
align-items: center; justify-content: center;
}
.client-shell .modal-overlay.show {
display: flex; }
.client-shell .modal-box {
background: #fff; border-radius: 16px; width: 600px; max-width: 90vw;
max-height: 85vh; overflow: hidden; display: flex; flex-direction: column;
box-shadow: 0 20px 60px rgba(0,0,0,.25);
animation: modalIn .3s cubic-bezier(.4,0,.2,1);
}
@keyframes modalIn {
from { opacity: 0; transform: scale(.92) translateY(20px); } to { opacity: 1; transform: scale(1) translateY(0); } }
.client-shell .modal-header {
display: flex; align-items: center; justify-content: space-between;
padding: 20px 28px; border-bottom: 1px solid #f0f0f0;
}
.client-shell .modal-title {
font-size: 16px; font-weight: 600; color: #262626; }
.client-shell .modal-close {
width: 32px; height: 32px; border: none; background: #f5f5f5; border-radius: 8px;
cursor: pointer; font-size: 18px; color: #8c8c8c; transition: all .2s;
display: flex; align-items: center; justify-content: center;
}
.client-shell .modal-close:hover {
background: #fff1f0; color: #ff4d4f; }
.client-shell .modal-body {
flex: 1; overflow-y: auto; padding: 28px; }
.client-shell .doc-preview {
text-align: center; }
.client-shell .doc-preview img {
max-width: 100%; border-radius: 8px; border: 1px solid #f0f0f0; }
.client-shell .doc-info {
margin-top: 20px; }
.client-shell .doc-info-row {
display: flex; margin-bottom: 12px; font-size: 13px; }
.client-shell .doc-info-label {
width: 100px; color: #8c8c8c; flex-shrink: 0; }
.client-shell .doc-info-value {
color: #262626; flex: 1; }
.client-shell .modal-footer {
display: flex; justify-content: flex-end; gap: 12px;
padding: 16px 28px; border-top: 1px solid #f0f0f0;
}
.client-shell .modal-box.modal-wide {
width: 920px; max-width: 92vw; }
.client-shell .inbound-actions { display:flex; align-items:center; gap:10px; }
.client-shell .inbound-action { padding:4px 0; border:0; background:transparent; color:var(--wb-action,#126ba8); font-size:12px; font-weight:600; cursor:pointer; }
.client-shell .inbound-action:hover { text-decoration:underline; }
.client-shell .inbound-detail-subtitle { color:var(--wb-muted,#667786); font-size:13px; }
.client-shell .inbound-detail-section { margin-bottom:16px; }
.client-shell .inbound-detail-tabs-shell { margin-bottom:16px; overflow:hidden; }
.client-shell .inbound-detail-tabs { display:flex; gap:0; overflow-x:auto; border-bottom:1px solid var(--wb-border,#d7e2ea); background:#fbfdfe; -webkit-overflow-scrolling:touch; scrollbar-width:thin; }
.client-shell .inbound-detail-tab { position:relative; flex:0 0 auto; min-height:46px; padding:0 20px; border:0; background:transparent; color:#667786; font:600 13px/1.2 inherit; cursor:pointer; white-space:nowrap; }
.client-shell .inbound-detail-tab::after { content:""; position:absolute; right:18px; bottom:-1px; left:18px; height:3px; border-radius:3px 3px 0 0; background:transparent; }
.client-shell .inbound-detail-tab:hover { color:#126ba8; background:#f2f8fb; }
.client-shell .inbound-detail-tab.active { color:#126ba8; }
.client-shell .inbound-detail-tab.active::after { background:#126ba8; }
.client-shell .inbound-detail-panel { display:none; }
.client-shell .inbound-detail-panel.active { display:block; }
.client-shell .business-detail-tabs-shell, .sea-business-detail-page .business-detail-tabs-shell { margin-bottom:16px; overflow:hidden; }
.client-shell .business-detail-tabs, .sea-business-detail-page .business-detail-tabs { display:flex; gap:0; max-width:100%; overflow-x:auto; overflow-y:hidden; border-bottom:1px solid var(--wb-border,#d7e2ea); background:#fbfdfe; -webkit-overflow-scrolling:touch; overscroll-behavior-x:contain; scrollbar-width:thin; }
.client-shell .business-detail-tabs .business-detail-tab, .sea-business-detail-page .business-detail-tabs .business-detail-tab { position:relative; flex:0 0 auto; min-height:46px; padding:0 20px; border:0; background:transparent; color:var(--wb-muted,#667786); font:600 13px/1.2 inherit; cursor:pointer; white-space:nowrap; }
.client-shell .business-detail-tab::after, .sea-business-detail-page .business-detail-tab::after { content:""; position:absolute; right:18px; bottom:-1px; left:18px; height:3px; border-radius:3px 3px 0 0; background:transparent; }
.client-shell .business-detail-tab:hover, .sea-business-detail-page .business-detail-tab:hover { color:var(--wb-action,#126ba8); background:#f2f8fb; }
.client-shell .business-detail-tab.active, .sea-business-detail-page .business-detail-tab.active { color:var(--wb-action,#126ba8); }
.client-shell .business-detail-tab.active::after, .sea-business-detail-page .business-detail-tab.active::after { background:var(--wb-action,#126ba8); }
.client-shell .business-detail-tab:focus-visible, .sea-business-detail-page .business-detail-tab:focus-visible { z-index:1; outline:2px solid var(--wb-action,#126ba8); outline-offset:-3px; }
.client-shell .business-detail-panels, .sea-business-detail-page .business-detail-panels { min-width:0; }
.client-shell .business-detail-panel, .sea-business-detail-page .business-detail-panel { display:none; min-width:0; }
.client-shell .business-detail-panel.active, .sea-business-detail-page .business-detail-panel.active { display:block; }
.client-shell .business-detail-panel > .card, .sea-business-detail-page .business-detail-panel > .card,
.client-shell .business-detail-panel > .sea-detail-section, .sea-business-detail-page .business-detail-panel > .sea-detail-section { margin-bottom:0; }
.client-shell .business-detail-panel .storage-transfer-summary-value, .sea-business-detail-page .business-detail-panel .storage-transfer-summary-value,
.client-shell .business-detail-panel .return-relabel-relation-grid strong, .sea-business-detail-page .business-detail-panel .return-relabel-relation-grid strong { overflow-wrap:anywhere; }
.client-shell .inbound-detail-title { display:flex; align-items:center; gap:10px; margin-bottom:16px; color:var(--wb-ink,#142333); font-size:15px; font-weight:700; }
.client-shell .inbound-push-label { display:inline-flex; align-items:center; padding:3px 8px; border-radius:999px; background:#fff5dd; color:#a76300; font-size:11px; font-weight:700; }
.client-shell .inbound-detail-confirm { margin-left:auto; padding:7px 12px; font-size:12px; }
.client-shell .inbound-detail-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px 24px; }
.client-shell .inbound-detail-field { display:grid; grid-template-columns:100px minmax(0,1fr); gap:10px; min-height:28px; align-items:center; font-size:13px; }
.client-shell .inbound-detail-field dt { color:var(--wb-muted,#667786); }
.client-shell .inbound-detail-field dd { margin:0; color:var(--wb-ink,#142333); font-weight:600; overflow-wrap:anywhere; }
.client-shell .inbound-order-info-columns { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px 28px; }
.client-shell .inbound-order-info-column { display:grid; gap:10px; min-width:0; }
.client-shell .inbound-order-info-column .inbound-detail-field { grid-template-columns:minmax(112px,auto) minmax(0,1fr); min-height:28px; }
.client-shell .inbound-node-scroll { overflow-x:auto; max-width:100%; -webkit-overflow-scrolling:touch; scrollbar-width:thin; }
.client-shell .inbound-node-track { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:0; min-width:760px; padding:14px 8px 4px; }
.client-shell .inbound-node { position:relative; display:flex; flex-direction:column; align-items:flex-start; gap:6px; color:#8392a0; text-align:left; font-size:12px; }
.client-shell .inbound-node::after { content:""; position:absolute; top:13px; left:18px; width:calc(100% - 18px); height:3px; background:#d8e1e8; }
.client-shell .inbound-node:last-child::after { display:none; }
.client-shell .inbound-node span { position:relative; z-index:1; display:grid; width:28px; height:28px; place-items:center; border:1px solid #c7d2dc; border-radius:50%; background:#fff; color:#82909d; font-size:16px; font-weight:700; }
.client-shell .inbound-node b { color:inherit; font-size:14px; font-weight:700; white-space:nowrap; }
.client-shell .inbound-node small { color:#9aa9bb; font-size:13px; white-space:nowrap; }
.client-shell .inbound-node.is-done,.client-shell .inbound-node.is-current { color:#159447; }
.client-shell .inbound-node.is-done span,.client-shell .inbound-node.is-current span { border-color:#20a957; background:#20a957; color:#fff; }
.client-shell .inbound-node.is-done::after { background:#20a957; }
.client-shell .inbound-measure-summary { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:10px; margin-bottom:14px; padding:14px; border:1px solid var(--wb-border,#d7e2ea); border-radius:8px; background:#f8fafb; }
.client-shell .inbound-measure-stat span { display:block; color:var(--wb-muted,#667786); font-size:11px; margin-bottom:5px; }
.client-shell .inbound-measure-stat strong { color:var(--wb-ink,#142333); font-size:14px; }
.client-shell .inbound-measure-table-wrap { overflow-x:auto; border:1px solid var(--wb-border,#d7e2ea); border-radius:8px; }
.client-shell .inbound-measure-table { width:100%; min-width:1040px; border-collapse:collapse; font-size:12px; }
.client-shell .inbound-measure-table th { padding:10px 9px; background:#edf3f7; color:var(--wb-action,#126ba8); text-align:left; white-space:nowrap; }
.client-shell .inbound-measure-table td { padding:10px 9px; border-top:1px solid #edf1f4; color:#33475b; white-space:nowrap; }
.client-shell .inbound-product-heading { justify-content:space-between; }
.client-shell .inbound-product-count { color:var(--wb-muted,#667786); font-size:12px; font-weight:500; }
.client-shell .inbound-product-table-wrap { max-width:100%; overflow-x:auto; border:1px solid var(--wb-border,#d7e2ea); border-radius:8px; -webkit-overflow-scrolling:touch; }
.client-shell .inbound-product-table { width:100%; min-width:2300px; border-collapse:collapse; font-size:11px; }
.client-shell .inbound-product-table th { padding:10px 9px; background:#edf3f7; color:var(--wb-action,#126ba8); text-align:left; white-space:nowrap; }
.client-shell .inbound-product-table td { max-width:180px; padding:10px 9px; border-top:1px solid #edf1f4; color:#33475b; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.client-shell .inbound-product-table tbody tr:hover td { background:#f8fbfd; }
.client-shell .inbound-product-table th:first-child,.client-shell .inbound-product-table td:first-child { position:sticky; left:0; z-index:1; background:#fff; }
.client-shell .inbound-product-table th:first-child { z-index:2; background:#edf3f7; }
.client-shell .inbound-product-image { display:block; width:40px; height:40px; border-radius:6px; object-fit:cover; }
.client-shell .inbound-product-empty { padding:24px 12px !important; color:var(--wb-muted,#667786) !important; text-align:center; }
.client-shell .inbound-fee-list { display:grid; gap:8px; max-width:640px; }
.client-shell .inbound-fee-row { display:flex; justify-content:space-between; gap:20px; padding:10px 0; border-bottom:1px dashed var(--wb-border,#d7e2ea); font-size:13px; }
.client-shell .inbound-fee-row:last-child { border-bottom:0; padding-bottom:0; }
.client-shell .inbound-fee-row span { color:var(--wb-muted,#667786); }.client-shell .inbound-fee-row strong { color:var(--wb-ink,#142333); }
.client-shell .inbound-customs-toolbar { display:flex; align-items:center; justify-content:space-between; gap:14px; margin-bottom:14px; }
.client-shell .inbound-customs-hint { color:var(--wb-muted,#667786); font-size:12px; line-height:1.5; }
.client-shell .inbound-customs-actions { display:flex; flex-wrap:wrap; gap:10px; justify-content:flex-end; }
.client-shell .inbound-customs-upload { display:inline-flex; align-items:center; gap:7px; padding:8px 13px; border:1px solid #126ba8; border-radius:7px; background:#126ba8; color:#fff; font-size:12px; font-weight:700; cursor:pointer; }
.client-shell .inbound-customs-upload:hover { background:#0f5d91; }
.client-shell .inbound-customs-download { padding:8px 13px; border:1px solid #c5d5df; border-radius:7px; background:#fff; color:#126ba8; font-size:12px; font-weight:700; cursor:pointer; }
.client-shell .inbound-customs-download:hover { border-color:#126ba8; background:#f2f8fb; }
.client-shell .inbound-customs-files { display:grid; gap:8px; }
.client-shell .inbound-customs-file { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:11px 12px; border:1px solid #dfe8ee; border-radius:7px; background:#fbfdfe; }
.client-shell .inbound-customs-file-main { display:flex; align-items:center; min-width:0; gap:9px; }
.client-shell .inbound-customs-file-main i { color:#126ba8; }
.client-shell .inbound-customs-file-name { min-width:0; overflow:hidden; color:#243746; font-size:13px; font-weight:600; text-overflow:ellipsis; white-space:nowrap; }
.client-shell .inbound-customs-file-meta { flex:0 0 auto; color:#8a9aaa; font-size:11px; white-space:nowrap; }
.client-shell .inbound-customs-empty { padding:18px 12px; border:1px dashed #c9d7e1; border-radius:7px; color:#718293; background:#fbfdfe; font-size:12px; text-align:center; }
.client-shell .inbound-container-node-list { display:grid; gap:0; border:1px solid var(--wb-border,#d7e2ea); border-radius:8px; overflow:hidden; }
.client-shell .inbound-container-node { display:grid; grid-template-columns:28px minmax(160px,1fr) minmax(120px,auto); align-items:center; gap:14px; min-height:62px; padding:12px 16px; background:#fff; }
.client-shell .inbound-container-node + .inbound-container-node { border-top:1px solid #edf1f4; }
.client-shell .inbound-container-node-marker { position:relative; display:grid; width:24px; height:24px; place-items:center; border:1px solid #c7d2dc; border-radius:50%; background:#fff; color:#82909d; font-size:12px; font-weight:700; }
.client-shell .inbound-container-node-marker::after { content:""; position:absolute; top:24px; left:11px; width:1px; height:38px; background:#d8e1e8; }
.client-shell .inbound-container-node:last-child .inbound-container-node-marker::after { display:none; }
.client-shell .inbound-container-node-name { color:var(--wb-ink,#142333); font-size:13px; font-weight:700; }
.client-shell .inbound-container-node-status { display:inline-flex; align-items:center; justify-content:center; justify-self:end; min-width:58px; padding:4px 9px; border-radius:999px; font-size:11px; font-weight:700; }
.client-shell .inbound-container-node-time { color:#9aa9bb; font-size:12px; white-space:nowrap; }
.client-shell .inbound-container-node.is-done .inbound-container-node-marker { border-color:#20a957; background:#20a957; color:#fff; }
.client-shell .inbound-container-node.is-done .inbound-container-node-status { background:#eaf8ef; color:#159447; }
.client-shell .inbound-container-node.is-current { background:#f7fbfd; }
.client-shell .inbound-container-node.is-current .inbound-container-node-marker { border-color:#126ba8; background:#126ba8; color:#fff; box-shadow:0 0 0 4px rgba(18,107,168,.12); }
.client-shell .inbound-container-node.is-current .inbound-container-node-status { background:#e7f3fa; color:#126ba8; }
.client-shell .inbound-container-node.is-pending .inbound-container-node-status { background:#f1f4f6; color:#718293; }
@media (max-width:640px) { .client-shell .inbound-container-node { grid-template-columns:28px minmax(0,1fr) auto; gap:8px 10px; padding:12px; } .client-shell .inbound-container-node-status { min-width:52px; padding-right:7px; padding-left:7px; } }
.client-shell .inbound-destination-filter { align-items:center; }
.client-shell .inbound-destination-filter .filter-select { min-width:0; }
.client-shell .inbound-destination-filter .inbound-filter-query { flex:0 0 auto; min-width:78px; min-height:36px; padding:0 12px; white-space:nowrap; }
.inbound-batch-modal { max-width:600px; border-radius:10px; }
.inbound-batch-modal .topup-body { display:grid; gap:16px; padding:20px; overflow-y:auto; }
.inbound-batch-intro { margin:0; color:#526274; font-size:13px; line-height:1.65; }
.inbound-batch-template { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:14px 16px; border:1px solid #d7e2ea; border-radius:8px; background:#f7fafc; }
.inbound-batch-template-copy { display:grid; gap:4px; min-width:0; }
.inbound-batch-template-copy strong { color:#203548; font-size:13px; }
.inbound-batch-template-copy span { color:#718293; font-size:12px; line-height:1.5; }
.inbound-batch-download { display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto; gap:7px; min-height:38px; padding:0 14px; border:1px solid #9fc4d8; border-radius:6px; background:#eef6fa; color:#0f668f; font-size:13px; font-weight:700; cursor:pointer; }
.inbound-batch-download:hover { border-color:#0f668f; background:#e3f1f7; }
.inbound-batch-upload { display:grid; min-height:150px; padding:24px 18px; place-items:center; border:1px dashed #9eb7c7; border-radius:8px; background:#fbfdfe; color:#36556d; text-align:center; cursor:pointer; }
.inbound-batch-upload:hover,
.inbound-batch-upload.is-dragover { border-color:#0f668f; background:#eef6fa; }
.inbound-batch-upload i { margin-bottom:10px; color:#126ba8; font-size:28px; }
.inbound-batch-upload strong { display:block; margin-bottom:4px; color:#203548; font-size:14px; }
.inbound-batch-upload span { display:block; color:#718293; font-size:12px; line-height:1.5; }
.inbound-batch-file { display:none; align-items:center; justify-content:space-between; gap:12px; padding:12px 14px; border:1px solid #cfe1eb; border-radius:8px; background:#f2f8fb; }
.inbound-batch-file.is-visible { display:flex; }
.inbound-batch-file-main { display:flex; align-items:center; min-width:0; gap:10px; }
.inbound-batch-file-main i { color:#16805b; font-size:20px; }
.inbound-batch-file-name { display:block; min-width:0; overflow:hidden; color:#203548; font-size:13px; font-weight:700; text-overflow:ellipsis; white-space:nowrap; }
.inbound-batch-file-summary { display:block; margin-top:3px; color:#607789; font-size:12px; }
.inbound-batch-remove { display:grid; flex:0 0 auto; width:32px; height:32px; place-items:center; border:0; border-radius:6px; background:transparent; color:#718293; cursor:pointer; }
.inbound-batch-remove:hover { background:#e4edf2; color:#bf3f4b; }
.inbound-batch-message { min-height:20px; margin:0; color:#bf3f4b; font-size:12px; line-height:1.5; }
.inbound-batch-modal .topup-foot { padding:0 20px 20px; }
.inbound-batch-modal .topup-foot button { min-height:42px; border-radius:6px; }
@media (max-width:640px) {
.client-shell .inbound-destination-filter { grid-column:1 / -1; }
.inbound-batch-template { align-items:stretch; flex-direction:column; }
.inbound-batch-download { width:100%; }
.inbound-batch-upload { min-height:138px; }
}
.client-shell .inbound-measurement-modal {
max-width:520px;
max-height:calc(100vh - 3rem);
border-radius:10px;
}
.client-shell .inbound-measurement-modal .topup-head {
min-height:80px;
padding:0 20px;
}
.client-shell .inbound-measurement-modal .topup-head h3 {
font-size:16px;
letter-spacing:0;
}
.client-shell .inbound-measurement-modal .topup-close {
width:32px;
height:32px;
font-size:22px;
}
.client-shell .inbound-measurement-modal .topup-body {
padding:20px;
}
.client-shell .inbound-measurement-notice {
display:flex;
align-items:flex-start;
gap:7px;
margin:0 0 4px;
padding:0;
border-radius:0;
background:transparent;
color:#334155;
font-size:14px;
line-height:1.5;
}
.client-shell .inbound-measurement-notice i {
flex:0 0 auto;
margin-top:3px;
color:#1f2937;
}
.client-shell .inbound-measurement-modal .inbound-measure-summary {
display:block;
margin:0 0 10px;
padding:0;
border:0;
background:transparent;
}
.client-shell .inbound-measurement-modal .inbound-measure-stat {
display:inline;
color:#334155;
font-size:14px;
line-height:1.7;
}
.client-shell .inbound-measurement-modal .inbound-measure-stat span {
display:inline;
margin:0;
color:#334155;
font-size:14px;
}
.client-shell .inbound-measurement-modal .inbound-measure-stat span::after { content:" "; }
.client-shell .inbound-measurement-modal .inbound-measure-stat strong {
color:#263548;
font-size:14px;
}
.client-shell .inbound-measurement-modal .inbound-measure-stat + .inbound-measure-stat { display:block; }
.client-shell .inbound-measurement-modal .inbound-measure-table-wrap {
overflow-x:auto;
border:0;
border-radius:0;
}
.client-shell .inbound-measurement-modal .inbound-measure-table {
width:100%;
min-width:600px;
table-layout:fixed;
font-size:12px;
}
.client-shell .inbound-measurement-modal .inbound-measure-table th {
padding:5px 3px;
background:transparent;
color:#263548;
text-align:center;
white-space:normal;
line-height:1.35;
font-weight:700;
}
.client-shell .inbound-measurement-modal .inbound-measure-table td {
padding:7px 3px;
border-top:0;
color:#334155;
text-align:center;
white-space:normal;
overflow-wrap:anywhere;
line-height:1.45;
}
.client-shell .inbound-measurement-modal .topup-foot {
gap:12px;
padding:0 20px 20px;
}
.client-shell .inbound-measurement-modal .topup-foot button {
min-height:44px;
border-radius:6px;
font-size:14px;
}
.client-shell .inbound-measurement-modal .topup-cancel {
background:#f1f5f9;
color:#526274;
}
.client-shell .inbound-measurement-modal .topup-confirm {
background:#1677b5;
box-shadow:0 4px 12px rgba(22,119,181,.24);
}
@media (max-width:640px) {
.client-shell .inbound-measurement-modal {
max-height:calc(100dvh - .5rem);
}
.client-shell .inbound-measurement-modal .topup-head { min-height:64px; }
.client-shell .inbound-measurement-modal .topup-body { padding:16px; }
.client-shell .inbound-measurement-modal .topup-foot { padding:0 16px max(16px, env(safe-area-inset-bottom)); }
}
@media (max-width:780px) { .client-shell .inbound-detail-grid, .client-shell .inbound-order-info-columns { grid-template-columns:1fr; }.client-shell .inbound-measure-summary { grid-template-columns:repeat(2,minmax(0,1fr)); }.client-shell .inbound-node-scroll { margin-right:-4px; }.client-shell .inbound-detail-title { flex-wrap:wrap; }.client-shell .inbound-customs-toolbar { align-items:flex-start; flex-direction:column; }.client-shell .inbound-customs-actions { justify-content:flex-start; } }
.client-shell .modal-section-title {
font-size: 14px; font-weight: 600; color: #262626; margin-bottom: 12px; padding-left: 10px; border-left: 3px solid #667eea; }
.client-shell .page-header-actions {
display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.client-shell .page-header-actions .page-title {
margin: 0; }
.client-shell .page-action-group {
display: flex; gap: 10px; }
.client-shell .status-badge {
display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 6px; font-size: 12px; font-weight: 500; }
.client-shell .status-pending {
background: #fff7e6; color: #fa8c16; }
.client-shell .status-processing {
background: #e6f7ff; color: #1677ff; }
.client-shell .status-done {
background: #f6ffed; color: #52c41a; }
.client-shell .status-rejected {
background: #fff1f0; color: #ff4d4f; }
.client-shell .link-action {
color: #1677ff; font-size: 12px; cursor: pointer; margin-right: 8px; }
.client-shell .link-action:hover {
color: #0056b3; text-decoration: underline; }
.client-shell .form-section {
margin-bottom: 14px; }
.client-shell .form-section:last-child {
margin-bottom: 0; }
.client-shell .product-table-wrap {
overflow-x: auto; border: 1px solid #f0f0f0; border-radius: 10px; margin-bottom: 12px; }
.client-shell .product-table {
width: 100%; border-collapse: collapse; font-size: 12px; }
.client-shell .product-table thead th {
background: #f8f8ff; padding: 12px; text-align: left; font-weight: 600; color: #667eea; border-bottom: 1px solid #e8e8ff; }
.client-shell .product-table tbody td {
padding: 10px 12px; border-bottom: 1px solid #f0f0f0; }
.client-shell .product-table tbody tr:last-child td {
border-bottom: none; }
.client-shell .product-table .cell-input {
width: 100%; min-width: 100px; height: 34px; padding: 0 10px; border: 1px solid #d9d9d9; border-radius: 6px; font-size: 12px; outline: none; }
.client-shell .product-table .cell-input:focus {
border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,0.1); }
.client-shell .text-error {
color: #ff4d4f; font-size: 12px; margin-top: 4px; }
.client-shell .btn-text {
color: #1677ff; font-size: 13px; cursor: pointer; background: none; border: none; display: inline-flex; align-items: center; gap: 4px; }
.client-shell .btn-text:hover {
color: #0056b3; }
.client-shell .btn-ghost {
background: #fff; border: 1px solid #d9d9d9; color: #595959; padding: 8px 18px; border-radius: 10px; font-size: 13px; cursor: pointer; transition: all .2s; }
.client-shell .btn-ghost:hover {
border-color: #667eea; color: #667eea; }
/* ===== 仓储转运单 ===== */
.client-shell .storage-transfer-filter-grid,
.client-shell .storage-transfer-form-grid,
.client-shell .storage-transfer-summary-grid {
display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px 20px; }
.client-shell .storage-transfer-filter-item,
.client-shell .storage-transfer-field {
display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.client-shell .storage-transfer-filter-label,
.client-shell .storage-transfer-field-label {
color: #475569; font-size: 12px; font-weight: 600; }
.client-shell .storage-transfer-required {
color: #ff4d4f; margin-right: 4px; }
.client-shell .storage-transfer-input,
.client-shell .storage-transfer-select,
.client-shell .storage-transfer-textarea {
width: 100%; border: 1px solid #dbe3ef; border-radius: 8px; background: #fff;
color: #1e293b; font-size: 13px; outline: none; transition: border-color .2s, box-shadow .2s; }
.client-shell .storage-transfer-input,
.client-shell .storage-transfer-select {
height: 38px; padding: 0 11px; }
.client-shell .storage-transfer-textarea {
min-height: 84px; padding: 10px 11px; resize: vertical; }
.client-shell .storage-transfer-input:focus,
.client-shell .storage-transfer-select:focus,
.client-shell .storage-transfer-textarea:focus {
border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,.1); }
.client-shell .storage-transfer-span-3 {
grid-column: 1 / -1; }
.client-shell .storage-transfer-filter-actions,
.client-shell .storage-transfer-form-actions {
display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.client-shell .storage-transfer-section {
margin-bottom: 14px; }
.client-shell .storage-transfer-section-title {
color: #1e293b; font-size: 14px; font-weight: 700; margin-bottom: 16px;
padding-bottom: 10px; border-bottom: 1px solid #eef2f7; }
.client-shell .storage-transfer-settings-card {
overflow: hidden; border-color: #dce6ee; box-shadow: none; }
.client-shell .storage-transfer-settings-header {
display: flex; align-items: center; justify-content: space-between; gap: 16px;
padding: 15px 18px; border-bottom: 1px solid #e8eef3; background: #fff; }
.client-shell .storage-transfer-settings-title {
color: #1e293b; font-size: 14px; font-weight: 700; }
.client-shell .storage-transfer-settings-status {
color: #64748b; font-size: 12px; font-weight: 600; }
.client-shell .storage-transfer-settings-status.is-on { color: #146ea8; }
.client-shell .storage-transfer-settings-body { padding: 18px; }
.client-shell .storage-transfer-settings-toggle {
display: inline-flex; align-items: center; gap: 8px; color: #1e293b;
font-size: 13px; font-weight: 650; cursor: pointer; }
.client-shell .storage-transfer-settings-toggle input {
width: 16px; height: 16px; margin: 0; accent-color: #1677ff; cursor: pointer; }
.client-shell .storage-transfer-settings-fields { margin-top: 18px; }
.client-shell .storage-transfer-requirement-grid {
display: grid; grid-template-columns: minmax(0, 520px) minmax(180px, 240px); gap: 12px; align-items: end; }
.client-shell .storage-transfer-template-field {
display: flex; flex-direction: column; align-items: flex-start; gap: 7px; min-width: 0; }
.client-shell .storage-transfer-template-field .btn { min-height: 38px; }
.client-shell .storage-transfer-upload-grid {
display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 14px; }
.client-shell .storage-transfer-upload-card {
display: flex; flex-direction: column; gap: 8px; min-width: 0; padding: 14px;
border: 1px dashed #b8cad7; border-radius: 8px; background: #fff;
transition: border-color .2s, background-color .2s; }
.client-shell .storage-transfer-upload-card.is-disabled {
border-style: solid; border-color: #e2e8f0; background: #f8fafc; }
.client-shell .storage-transfer-upload-card.is-ready {
border-style: solid; border-color: #9dd2bb; background: #f3fbf7; }
.client-shell .storage-transfer-upload-card label {
color: #1e293b; font-size: 12px; font-weight: 700; }
.client-shell .storage-transfer-upload-card input[type="file"] {
width: 100%; color: #64748b; font-size: 12px; }
.client-shell .storage-transfer-upload-card input[type="file"]::file-selector-button {
min-height: 30px; margin-right: 10px; padding: 0 12px; border: 1px solid #cbd5e1;
border-radius: 6px; background: #fff; color: #334155; cursor: pointer; }
.client-shell .storage-transfer-upload-card input[type="file"]:focus-visible {
outline: 2px solid #1677ff; outline-offset: 3px; }
.client-shell .storage-transfer-upload-card input[type="file"]:disabled::file-selector-button {
cursor: not-allowed; color: #94a3b8; background: #f1f5f9; }
.client-shell .storage-transfer-associated-grid {
display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; margin-top: 12px; }
.client-shell .storage-transfer-associated-field {
display: flex; flex-direction: column; gap: 8px; min-width: 0; padding: 14px;
border: 1px solid #dbe3ef; border-radius: 8px; background: #fff; }
.client-shell .storage-transfer-associated-field label {
color: #1e293b; font-size: 12px; font-weight: 700; }
.client-shell .storage-transfer-associated-field input[type="file"] {
width: 100%; color: #64748b; font-size: 12px; }
.client-shell .storage-transfer-associated-field input[type="file"]::file-selector-button {
min-height: 30px; margin-right: 10px; padding: 0 12px; border: 1px solid #cbd5e1;
border-radius: 6px; background: #fff; color: #334155; cursor: pointer; }
.client-shell .storage-transfer-associated-field input[type="file"]:focus-visible {
outline: 2px solid #1677ff; outline-offset: 3px; }
.client-shell .storage-transfer-associated-field textarea {
width: 100%; min-height: 64px; padding: 9px 10px; border: 1px solid #dbe3ef;
border-radius: 7px; color: #1e293b; font-size: 13px; resize: vertical; outline: none; }
.client-shell .storage-transfer-associated-field textarea:focus {
border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,.1); }
.client-shell .storage-transfer-associated-field.is-disabled {
border-color: #e2e8f0; background: #f8fafc; }
.client-shell .storage-transfer-associated-field.is-ready {
border-color: #9dd2bb; background: #f3fbf7; }
.client-shell .storage-transfer-associated-field :disabled { cursor: not-allowed; color: #94a3b8; }
.client-shell .storage-transfer-relabel-hint {
margin-top: 12px; padding: 10px 12px; border-radius: 7px;
background: #edf6fb; color: #2b5770; font-size: 12px; line-height: 1.5; }
.client-shell .storage-transfer-settings-empty {
margin-top: 14px; padding: 12px 14px; border: 1px dashed #c9d7e1;
border-radius: 7px; background: #f8fafc; color: #60758a; font-size: 12px; }
.client-shell .storage-transfer-relabel-copy {
margin-top: 12px; color: #64748b; font-size: 12px; line-height: 1.6; }
.client-shell .storage-transfer-date-range {
display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 7px; }
.client-shell .storage-transfer-file-name {
display: block; max-width: 130px; margin-top: 5px; color: #64748b; font-size: 11px;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.client-shell .storage-transfer-error {
min-height: 16px; margin-top: 4px; color: #ff4d4f; font-size: 12px; line-height: 1.35; }
.client-shell .storage-transfer-input[aria-invalid="true"],
.client-shell .storage-transfer-select[aria-invalid="true"],
.client-shell .storage-transfer-cargo-input[aria-invalid="true"] {
border-color: #ff4d4f; }
.client-shell .storage-transfer-cargo-input {
width: 100%; min-width: 108px; height: 34px; padding: 0 9px; border: 1px solid #d9d9d9;
border-radius: 6px; color: #1e293b; font-size: 12px; outline: none; }
.client-shell .storage-transfer-cargo-input:focus {
border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,.1); }
.client-shell .storage-transfer-cargo-error {
min-height: 15px; margin-top: 3px; color: #ff4d4f; font-size: 11px; }
.client-shell .storage-transfer-file-control {
display: inline-flex; align-items: center; min-height: 32px; color: #1677ff; cursor: pointer; }
.client-shell .storage-transfer-file-control input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.client-shell .storage-transfer-cargo-table { min-width: 920px; }
.client-shell .storage-transfer-list-card .table-wrap-simple { overflow-x: auto; }
.client-shell .storage-transfer-list-card .data-table { min-width: 1500px; }
.client-shell .storage-transfer-actions { display: inline-flex; align-items: center; gap: 12px; white-space: nowrap; }
.client-shell .storage-transfer-submit[disabled] { cursor: not-allowed; opacity: .65; }
.client-shell .return-relabel-actions { display: inline-flex; align-items: center; gap: 12px; white-space: nowrap; }
/* Restore the create-page styles that were removed when list/detail styling was unified. */
.client-shell #page-return-relabel-create .return-relabel-form-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px 20px;
}
.client-shell #page-return-relabel-create .return-relabel-form-grid label {
display: block;
margin-bottom: 7px;
color: #475569;
font-size: 12px;
font-weight: 600;
}
.client-shell #page-return-relabel-create :is(.return-relabel-input, .return-relabel-select, .return-relabel-textarea) {
width: 100%;
height: 38px;
padding: 0 11px;
border: 1px solid #dbe3ef;
border-radius: 8px;
background: #fff;
color: #1e293b;
font-size: 13px;
outline: none;
}
.client-shell #page-return-relabel-create .return-relabel-textarea {
height: 84px;
padding: 10px;
resize: vertical;
}
.client-shell #page-return-relabel-create .return-relabel-form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 16px;
}
.client-shell #page-return-relabel-create .return-relabel-table-wrap { overflow-x: auto; }
.client-shell #page-return-relabel-create #return-relabel-sku-table { min-width: 1120px; }
.client-shell #page-return-relabel-create #return-relabel-sku-table th:nth-child(1) { min-width: 150px; }
.client-shell #page-return-relabel-create #return-relabel-sku-table th:nth-child(2) { min-width: 190px; }
.client-shell #page-return-relabel-create .return-relabel-section { margin-bottom: 14px; }
.client-shell #page-return-relabel-create .return-relabel-section-title {
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 1px solid #eef2f7;
color: #1e293b;
font-size: 14px;
font-weight: 700;
}
.client-shell #page-return-relabel-create .return-relabel-file-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.client-shell #page-return-relabel-create .return-relabel-error {
min-height: 18px;
margin-top: 10px;
color: #ff4d4f;
font-size: 12px;
}
.client-shell #page-return-relabel-create .return-relabel-span-2 { grid-column: span 2; }
.client-shell #page-return-relabel-create .return-relabel-settings-panel {
overflow: hidden;
border: 1px solid #dce6ee;
border-radius: 8px;
background: #fbfdfe;
}
.client-shell #page-return-relabel-create .return-relabel-settings-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
border-bottom: 1px solid #e8eef3;
}
.client-shell #page-return-relabel-create .return-relabel-settings-header .return-relabel-section-title {
margin: 0;
padding: 0;
border: 0;
}
.client-shell #page-return-relabel-create .return-relabel-settings-status {
color: #60758a;
font-size: 12px;
}
.client-shell #page-return-relabel-create .return-relabel-settings-status.is-on {
color: #146ea8;
font-weight: 700;
}
.client-shell #page-return-relabel-create .return-relabel-settings-body { padding: 16px; }
.client-shell #page-return-relabel-create .return-relabel-settings-toggle {
display: flex;
align-items: center;
gap: 8px;
color: #1e293b;
font-weight: 650;
}
.client-shell #page-return-relabel-create .return-relabel-settings-empty {
margin-top: 14px;
padding: 12px 14px;
border: 1px dashed #c9d7e1;
border-radius: 6px;
background: #fff;
color: #60758a;
font-size: 12px;
}
.client-shell #page-return-relabel-create .return-relabel-settings-fields { margin-top: 16px; }
.client-shell #page-return-relabel-create .return-relabel-upload-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
margin-top: 14px;
}
.client-shell #page-return-relabel-create .return-relabel-upload-card {
display: flex;
flex-direction: column;
gap: 7px;
padding: 14px;
border: 1px dashed #b8cad7;
border-radius: 6px;
background: #fff;
}
.client-shell #page-return-relabel-create .return-relabel-upload-card.is-ready {
border-style: solid;
border-color: #9dd2bb;
background: #f3fbf7;
}
.client-shell #page-return-relabel-create .return-relabel-upload-card label {
margin: 0;
color: #1e293b;
font-size: 12px;
font-weight: 700;
}
.client-shell #page-return-relabel-create .return-relabel-upload-card input[type="file"] {
width: 100%;
font-size: 12px;
}
.client-shell #page-return-relabel-create .return-relabel-file-hint {
margin-top: 12px;
padding: 10px 12px;
border-radius: 6px;
background: #edf6fb;
color: #2b5770;
font-size: 12px;
line-height: 1.5;
}
.client-shell .return-relabel-associated-template {
display: flex; align-items: flex-end; gap: 10px; min-height: 38px;
}
.client-shell .return-relabel-associated-template-label,
.client-shell .return-relabel-associated-field > label {
color: #1e293b; font-size: 12px; font-weight: 700;
}
.client-shell .return-relabel-associated-template-label { margin-bottom: 8px; }
.client-shell .return-relabel-associated-fields {
display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 12px;
}
.client-shell .return-relabel-associated-field { display: flex; min-width: 0; flex-direction: column; gap: 7px; }
.client-shell .return-relabel-associated-field input[type="file"] { width: 100%; color: #64748b; font-size: 12px; }
.client-shell .return-relabel-associated-field input[type="file"]::file-selector-button { min-height: 30px; margin-right: 10px; padding: 0 12px; border: 1px solid #cbd5e1; border-radius: 6px; background: #fff; color: #334155; cursor: pointer; }
.client-shell .return-relabel-associated-field textarea { min-height: 38px; padding: 9px 11px; border: 1px solid #dbe3ef; border-radius: 8px; background: #fff; color: #1e293b; font-size: 13px; resize: vertical; }
.client-shell .return-relabel-associated-current-file { min-height: 18px; color: #64748b; font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
.client-shell .return-relabel-associated-current-file strong { color: #334155; font-weight: 650; }
@media (max-width: 760px) {
.client-shell #page-return-relabel-create .return-relabel-form-grid,
.client-shell #page-return-relabel-create .return-relabel-file-grid,
.client-shell #page-return-relabel-create .return-relabel-upload-grid,
.client-shell .return-relabel-associated-fields { grid-template-columns: 1fr; }
.client-shell #page-return-relabel-create .return-relabel-span-2 { grid-column: auto; }
.client-shell #page-return-relabel-create .return-relabel-form-actions {
flex-wrap: wrap;
justify-content: flex-start;
}
}
.client-shell .return-relabel-edit-modal-box { width: min(1080px, calc(100vw - 32px)); max-width: none; max-height: min(92vh, 920px); }
.client-shell .return-relabel-edit-modal-box form { display: flex; min-height: 0; flex: 1; flex-direction: column; }
.client-shell .return-relabel-edit-modal-box .modal-body { padding: 22px 28px; }
.client-shell .return-relabel-edit-section + .return-relabel-edit-section { margin-top: 24px; padding-top: 22px; border-top: 1px solid #eef2f7; }
.client-shell .return-relabel-edit-modal-box .return-relabel-section-title { margin-bottom: 16px; color: #1e293b; font-size: 14px; font-weight: 700; }
.client-shell .return-relabel-edit-modal-box .return-relabel-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 20px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-form-grid > div { display: flex; min-width: 0; flex-direction: column; }
.client-shell .return-relabel-edit-modal-box .return-relabel-span-2 { grid-column: span 2; }
.client-shell .return-relabel-edit-modal-box :is(.return-relabel-input, .return-relabel-select, .return-relabel-textarea) { width: 100%; border: 1px solid #dbe3ef; border-radius: 8px; background: #fff; color: #1e293b; font-size: 13px; outline: none; transition: border-color .2s, box-shadow .2s; }
.client-shell .return-relabel-edit-modal-box :is(.return-relabel-input, .return-relabel-select) { height: 38px; padding: 0 11px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-textarea { min-height: 84px; padding: 10px 11px; resize: vertical; }
.client-shell .return-relabel-edit-modal-box :is(.return-relabel-input, .return-relabel-select, .return-relabel-textarea):focus { border-color: #1677ff; box-shadow: 0 0 0 3px rgba(22,119,255,.1); }
.client-shell .return-relabel-edit-modal-box .product-table { min-width: 780px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-header .return-relabel-section-title { margin-bottom: 0; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-status { color: #64748b; font-size: 12px; font-weight: 600; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-status.is-on { color: #146ea8; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-body { padding-top: 16px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-toggle { display: inline-flex; align-items: center; gap: 8px; color: #1e293b; font-size: 13px; font-weight: 650; cursor: pointer; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-toggle input { width: 16px; height: 16px; margin: 0; accent-color: #1677ff; cursor: pointer; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-fields { margin-top: 18px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 14px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-card { display: flex; min-width: 0; flex-direction: column; gap: 8px; padding: 14px; border: 1px dashed #b8cad7; border-radius: 8px; background: #fff; transition: border-color .2s, background-color .2s; }
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-card.is-disabled { border-style: solid; border-color: #e2e8f0; background: #f8fafc; }
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-card.is-ready { border-style: solid; border-color: #9dd2bb; background: #f3fbf7; }
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-card label { color: #1e293b; font-size: 12px; font-weight: 700; }
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-card input[type="file"] { width: 100%; color: #64748b; font-size: 12px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-card input[type="file"]::file-selector-button { min-height: 30px; margin-right: 10px; padding: 0 12px; border: 1px solid #cbd5e1; border-radius: 6px; background: #fff; color: #334155; cursor: pointer; }
.client-shell .return-relabel-edit-modal-box .return-relabel-file-hint { margin-top: 12px; padding: 10px 12px; border-radius: 7px; background: #edf6fb; color: #2b5770; font-size: 12px; line-height: 1.5; }
.client-shell .return-relabel-edit-modal-box .return-relabel-settings-empty { margin-top: 14px; padding: 12px 14px; border: 1px dashed #c9d7e1; border-radius: 7px; background: #f8fafc; color: #60758a; font-size: 12px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-error { min-height: 16px; margin-top: 16px; color: #dc2626; font-size: 12px; line-height: 1.4; }
.client-shell .return-relabel-edit-current-file { min-height: 18px; margin-top: 7px; color: #64748b; font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
.client-shell .return-relabel-edit-current-file strong { color: #334155; font-weight: 650; }
.client-shell .return-relabel-edit-submit[disabled] { cursor: not-allowed; opacity: .65; }
.client-shell .storage-transfer-edit-modal-box { width: min(980px, calc(100vw - 32px)); max-width: none; max-height: min(92vh, 900px); }
.client-shell .storage-transfer-edit-modal-box form { display: flex; min-height: 0; flex: 1; flex-direction: column; }
.client-shell .storage-transfer-edit-modal-box .modal-body { padding: 22px 28px; }
.client-shell .storage-transfer-edit-section + .storage-transfer-edit-section { margin-top: 22px; }
.client-shell .storage-transfer-edit-current-file { min-height: 18px; color: #64748b; font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
.client-shell .storage-transfer-edit-current-file strong { color: #334155; font-weight: 650; }
.client-shell .storage-transfer-summary-item {
padding: 14px 16px; border: 1px solid #e8edf5; border-radius: 8px; background: #f8fafc; }
.client-shell .storage-transfer-summary-label {
color: #64748b; font-size: 12px; margin-bottom: 5px; }
.client-shell .storage-transfer-summary-value {
color: #1e293b; font-size: 13px; font-weight: 600; }
.client-shell .storage-transfer-relation-grid {
display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.client-shell .storage-transfer-relation-item {
padding: 14px 16px; border: 1px solid #dbeafe; border-radius: 8px; background: #eff6ff; }
.client-shell .storage-transfer-pagination {
display: flex; align-items: center; justify-content: space-between; min-height: 56px;
padding: 10px 16px; border-top: 1px solid #f0f0f0; }
@media (max-width: 760px) {
.client-shell .return-relabel-edit-modal-box { width: calc(100vw - 20px); max-height: calc(100vh - 20px); }
.client-shell .return-relabel-edit-modal-box .modal-body { padding: 18px 16px; }
.client-shell .return-relabel-edit-modal-box .return-relabel-form-grid,
.client-shell .return-relabel-edit-modal-box .return-relabel-upload-grid { grid-template-columns: minmax(0, 1fr); }
.client-shell .return-relabel-edit-modal-box .return-relabel-span-2 { grid-column: auto; }
.client-shell .storage-transfer-filter-grid,
.client-shell .storage-transfer-form-grid,
.client-shell .storage-transfer-summary-grid,
.client-shell .storage-transfer-relation-grid {
grid-template-columns: minmax(0, 1fr); }
.client-shell .storage-transfer-span-3 {
grid-column: auto; }
.client-shell .storage-transfer-filter-actions,
.client-shell .storage-transfer-form-actions {
flex-wrap: wrap; }
.client-shell .storage-transfer-requirement-grid,
.client-shell .storage-transfer-upload-grid,
.client-shell .storage-transfer-associated-grid {
grid-template-columns: minmax(0, 1fr); }
}
.client-shell .cargo-disposal-filter-grid,
.client-shell .cargo-disposal-summary-grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:16px 20px; }
.client-shell .cargo-disposal-filter-item,
.client-shell .cargo-disposal-field { display:flex; flex-direction:column; gap:7px; min-width:0; }
.client-shell .cargo-disposal-label { color:#475569; font-size:12px; font-weight:600; }
.client-shell .cargo-disposal-label .required,
.client-shell .cargo-disposal-table th .required { color:#ff4d4f; margin-right:4px; }
.client-shell .cargo-disposal-input,
.client-shell .cargo-disposal-select { width:100%; height:38px; padding:0 11px; border:1px solid #dbe3ef; border-radius:8px; background:#fff; color:#1e293b; font-size:13px; outline:none; }
.client-shell .cargo-disposal-input:focus,.client-shell .cargo-disposal-select:focus { border-color:#1677ff; box-shadow:0 0 0 3px rgba(22,119,255,.1); }
.client-shell .cargo-disposal-actions { display:flex; justify-content:flex-end; gap:10px; margin-top:18px; }
.client-shell .cargo-disposal-inbound-card { border:1px solid #dbe3ef; border-radius:10px; background:#fff; margin-bottom:12px; overflow:hidden; }
.client-shell .cargo-disposal-inbound-card.is-selected { border-color:#1677ff; box-shadow:0 0 0 2px rgba(22,119,255,.08); }
.client-shell .cargo-disposal-inbound-summary { display:flex; align-items:center; justify-content:space-between; gap:14px; padding:14px 16px; cursor:pointer; }
.client-shell .cargo-disposal-inbound-meta { color:#64748b; font-size:12px; margin-top:4px; }
.client-shell .cargo-disposal-inbound-search { max-width:440px; margin:14px 0 16px; }
.client-shell .cargo-disposal-inbound-search .cargo-disposal-label { display:block; margin-bottom:7px; }
.client-shell .cargo-disposal-inbound-body { display:none; padding:0 16px 16px; border-top:1px solid #eef2f7; }
.client-shell .cargo-disposal-inbound-card.expanded .cargo-disposal-inbound-body { display:block; }
.client-shell .cargo-disposal-table { width:100%; min-width:680px; border-collapse:collapse; font-size:12px; }
.client-shell .cargo-disposal-table th { background:#f8fafc; color:#64748b; padding:10px; text-align:left; }
.client-shell .cargo-disposal-table td { padding:10px; border-top:1px solid #f0f0f0; }
.client-shell .cargo-disposal-table-wrap { overflow-x:auto; margin-top:14px; }
.client-shell .cargo-disposal-quantity { width:92px; height:34px; padding:0 8px; border:1px solid #d9d9d9; border-radius:6px; }
.client-shell .cargo-disposal-fee-bar { display:flex; justify-content:space-between; align-items:center; gap:12px; padding:14px 16px; margin-top:14px; border-radius:8px; background:#e7f4f1; color:#225b56; }
.client-shell .cargo-disposal-fee-amount { color:#087d73; font-size:18px; font-weight:700; }
.client-shell .cargo-disposal-row-actions { display:flex; align-items:center; gap:12px; white-space:nowrap; }
.client-shell .cargo-disposal-edit-modal-box { width:min(920px,calc(100vw - 32px)); max-width:none; max-height:min(92vh,860px); }
.client-shell .cargo-disposal-edit-modal-box form { display:flex; min-height:0; flex:1; flex-direction:column; }
.client-shell .cargo-disposal-edit-modal-box .modal-body { padding:22px 28px; }
.client-shell .cargo-disposal-edit-modal-box .cargo-disposal-inbound-search { margin-top:0; }
.client-shell .cargo-disposal-relation-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:14px; }
.client-shell .cargo-disposal-relation-item { padding:14px 16px; border:1px solid #dbeafe; border-radius:8px; background:#eff6ff; }
@media (max-width:760px) { .client-shell .cargo-disposal-filter-grid,.client-shell .cargo-disposal-summary-grid,.client-shell .cargo-disposal-relation-grid { grid-template-columns:minmax(0,1fr); } .client-shell .cargo-disposal-inbound-summary { align-items:flex-start; } .client-shell .cargo-disposal-actions { flex-wrap:wrap; } .client-shell .cargo-disposal-edit-modal-box .modal-body { padding:18px 16px; } }
/* ===== 客户端异常订单 ===== */
.client-shell .client-exception-table-wrap { overflow-x:auto; }
.client-shell .client-exception-table { min-width:1680px; }
.client-shell .client-exception-no { color:#334155; font-weight:700; }
.client-shell .client-exception-relations { display:grid; gap:5px; min-width:154px; }
.client-shell .client-exception-relation { display:flex; align-items:center; gap:7px; }
.client-shell .client-exception-relation-label { min-width:28px; color:#64748b; font-size:11px; }
.client-shell .client-exception-relation-no { color:#334155; font-weight:600; }
.client-shell .client-exception-type { font-weight:600; }
.client-shell .client-exception-type.is-rejected { color:#dc2626; }
.client-shell .client-exception-type.is-returned { color:#d97706; }
.client-shell .client-exception-reason { display:block; max-width:220px; overflow:hidden; text-overflow:ellipsis; }
.client-shell .client-exception-detail-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); }
.client-shell .client-exception-detail-section { padding:22px 24px; border-bottom:1px solid #eef2f7; }
.client-shell .client-exception-detail-section:nth-child(odd) { border-right:1px solid #eef2f7; }
.client-shell .client-exception-detail-section:nth-last-child(-n+2) { border-bottom:0; }
.client-shell .client-exception-detail-title { margin-bottom:16px; color:#1e293b; font-size:14px; font-weight:700; }
.client-shell .client-exception-detail-fields { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px 22px; }
.client-shell .client-exception-detail-field { min-width:0; }
.client-shell .client-exception-detail-label { margin-bottom:5px; color:#64748b; font-size:12px; }
.client-shell .client-exception-detail-value { color:#1e293b; font-size:13px; font-weight:600; line-height:1.55; overflow-wrap:anywhere; }
.client-shell .client-exception-detail-value.is-reason { font-weight:500; }
@media (max-width:780px) {
.client-shell .exception-order-filter-grid { grid-template-columns:minmax(0,1fr); }
.client-shell .exception-order-filter-grid .span2 { grid-column:auto; }
.client-shell .exception-order-filter-grid .filter-item { align-items:flex-start; flex-direction:column; gap:7px; }
.client-shell .exception-order-filter-grid .filter-label { min-width:0; text-align:left; }
.client-shell .exception-order-filter-grid .filter-input,
.client-shell .exception-order-filter-grid .filter-select { width:100%; flex:none; }
.client-shell .client-exception-detail-grid { grid-template-columns:minmax(0,1fr); }
.client-shell .client-exception-detail-section,
.client-shell .client-exception-detail-section:nth-child(odd),
.client-shell .client-exception-detail-section:nth-last-child(-n+2) { border-right:0; border-bottom:1px solid #eef2f7; }
.client-shell .client-exception-detail-section:last-child { border-bottom:0; }
.client-shell .client-exception-detail-fields { grid-template-columns:minmax(0,1fr); }
}
/* ===== Workbench visual polish ===== */
:root {
--wb-navy: #0b2743;
--wb-navy-hover: #123b5d;
--wb-action: #126ba8;
--wb-action-hover: #0f5c91;
--wb-success: #1f9d6a;
--wb-ink: #142333;
--wb-muted: #667786;
--wb-canvas: #f3f6f8;
--wb-surface: #ffffff;
--wb-border: #dde5ec;
--wb-border-strong: #cbd7e1;
--wb-radius-control: 6px;
--wb-radius-panel: 8px;
--wb-radius-modal: 10px;
--wb-shadow-panel: 0 8px 24px rgba(11, 39, 67, 0.08);
--wb-shadow-modal: 0 24px 64px rgba(11, 39, 67, 0.18);
--wb-focus: 0 0 0 3px rgba(18, 107, 168, 0.16);
}
body {
color: var(--wb-ink);
background: var(--wb-canvas);
letter-spacing: 0;
}
.dash-sidebar {
width: 250px;
background: var(--wb-navy);
box-shadow: 8px 0 24px rgba(11, 39, 67, 0.08);
}
.dash-sidebar-brand {
min-height: 88px;
padding: 18px 16px;
border-bottom-color: rgba(255, 255, 255, 0.1);
}
.dash-nav-link {
min-height: 40px;
margin: 2px 10px;
border-radius: var(--wb-radius-control);
color: rgba(255, 255, 255, 0.72);
font-size: 14px;
}
.dash-nav-link:hover,
.dash-nav-link.active {
color: #fff;
background: rgba(18, 107, 168, 0.46);
}
.dash-topbar {
min-height: 64px;
padding: 0;
border-bottom: 1px solid var(--wb-border);
background: rgba(255, 255, 255, 0.96);
box-shadow: none;
backdrop-filter: blur(12px);
}
.dash-topbar > div {
width: 100%;
min-height: 64px;
padding-right: 32px !important;
padding-left: 32px !important;
}
.dash-main { background: var(--wb-canvas); }
.dash-main > .flex > main { padding: 28px 32px 40px; }
.dash-card {
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: var(--wb-shadow-panel);
}
.dash-card:hover {
transform: translateY(-2px);
border-color: #c8d8e4;
box-shadow: 0 12px 28px rgba(11, 39, 67, 0.1);
}
.dash-card > div:first-child {
border-radius: var(--wb-radius-control);
box-shadow: none;
}
.dash-card:nth-child(1) > div:first-child { background: var(--wb-action); }
.dash-card:nth-child(2) > div:first-child { background: var(--wb-success); }
.dash-card:nth-child(3) > div:first-child { background: #167f8d; }
.dash-view h1,
.client-shell .page-title,
.client-shell .sea-page-header h1 {
color: var(--wb-ink);
font-weight: 700;
letter-spacing: 0;
}
.client-shell .page-title { font-size: 24px; }
.client-shell .page-subtitle,
.client-shell .card-subtitle,
.client-shell .breadcrumb { color: var(--wb-muted); }
.client-shell .card,
.client-shell .sea-order-filters,
.client-shell .sea-order-table-wrap,
.client-shell .filter-panel {
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: var(--wb-shadow-panel);
}
.client-shell .card:hover { transform: none; }
.client-shell .card-header { padding: 18px 20px; border-bottom-color: var(--wb-border); }
.client-shell .card-body { padding: 20px; }
.client-shell .data-table { border-color: var(--wb-border); }
.client-shell .modal-box { border-radius: var(--wb-radius-modal); }
.client-shell .btn,
.client-shell button,
.topup-button {
border-radius: var(--wb-radius-control);
letter-spacing: 0;
}
.client-shell .btn,
.client-shell button:not(.modal-close),
.topup-button {
min-height: 36px;
line-height: 1.25;
}
.client-shell .btn-primary,
.client-shell .btn-flat-blue,
.client-shell .btn-purple,
.dash-topup-btn {
border-color: var(--wb-action);
background: var(--wb-action);
color: #fff;
box-shadow: none;
}
.client-shell .btn-primary:hover,
.client-shell .btn-flat-blue:hover,
.client-shell .btn-purple:hover,
.dash-topup-btn:hover { background: var(--wb-action-hover); }
.client-shell .form-input,
.client-shell .form-select,
.client-shell .form-textarea,
.client-shell input,
.client-shell select,
.client-shell textarea {
border-color: var(--wb-border-strong);
border-radius: var(--wb-radius-control);
color: var(--wb-ink);
background: #fff;
}
.client-shell :is(input, select, textarea, button, a):focus-visible,
.topup-modal :is(input, button):focus-visible {
outline: 2px solid var(--wb-action);
outline-offset: 2px;
box-shadow: var(--wb-focus);
}
.client-shell .sea-page-header,
.client-shell .page-header-actions {
align-items: center;
margin-bottom: 20px;
}
.client-shell .sea-page-header h1 { font-size: 24px; }
.client-shell .sea-order-filters,
.client-shell .filter-panel,
.client-shell .exception-order-filter-grid {
gap: 12px;
padding: 16px;
}
.client-shell :is(.data-table, .product-table, .goods-table, .quick-table, .sea-order-table) {
color: var(--wb-ink);
font-size: 13px;
}
.client-shell :is(.data-table, .product-table, .goods-table, .quick-table, .sea-order-table) thead th {
padding: 12px 14px;
border-bottom: 1px solid var(--wb-border);
background: #edf3f7;
color: var(--wb-action);
font-size: 12px;
font-weight: 700;
white-space: nowrap;
}
.client-shell :is(.data-table, .product-table, .goods-table, .quick-table, .sea-order-table) tbody td {
padding: 13px 14px;
border-bottom-color: #e8edf1;
vertical-align: middle;
}
.client-shell :is(.data-table, .product-table, .goods-table, .quick-table, .sea-order-table) tbody tr:hover {
background: #f7fafc;
}
.client-shell :is(.table-wrap, .table-container, .sea-order-table-wrap, .client-exception-table-wrap, .return-relabel-table-wrap, .cargo-disposal-table-wrap) {
position: relative;
border-radius: var(--wb-radius-panel);
scrollbar-color: #b9c7d2 transparent;
scrollbar-width: thin;
overscroll-behavior-x: contain;
-webkit-overflow-scrolling: touch;
}
.client-shell :is(.table-wrap, .table-container, .sea-order-table-wrap, .client-exception-table-wrap, .return-relabel-table-wrap, .cargo-disposal-table-wrap)::after,
.client-shell .profile-address-table-wrap::after,
.client-shell .quick-table-wrap::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 14px;
pointer-events: none;
box-shadow: inset -10px 0 14px -16px rgba(11, 39, 67, 0.65);
}
.client-shell :is(.status-badge, .doc-status) {
border-radius: 999px;
font-weight: 700;
}
.client-shell :is(.form-section, .return-relabel-section, .storage-transfer-section) {
margin-bottom: 16px;
}
.client-shell :is(.form-label, .return-relabel-form-grid label, .storage-transfer-label, .cargo-disposal-label) {
margin-bottom: 7px;
color: #32475a;
font-size: 13px;
font-weight: 650;
}
.client-shell :is(.storage-transfer-summary-item, .return-relabel-summary-grid > div, .cargo-disposal-relation-item) {
border: 0;
border-bottom: 1px solid var(--wb-border);
border-radius: 0;
background: transparent;
box-shadow: none;
}
.client-shell .client-exception-table { min-width: 1580px; }
.client-shell .modal-box,
.sea-edit-dialog,
.topup-modal {
border: 1px solid rgba(221, 229, 236, 0.9);
border-radius: var(--wb-radius-modal);
background: #fff;
box-shadow: var(--wb-shadow-modal);
}
.client-shell .modal-header,
.client-shell .modal-footer,
.sea-edit-header,
.topup-head,
.topup-foot {
border-color: var(--wb-border);
}
.client-shell .modal-title,
.sea-edit-title,
.topup-title {
color: var(--wb-ink);
font-weight: 700;
letter-spacing: 0;
}
.topup-foot button { border-radius: var(--wb-radius-control); }
.topup-confirm,
.topup-step.active .topup-step-dot {
border-color: var(--wb-action);
background: var(--wb-action);
}
.topup-confirm:hover { background: var(--wb-action-hover); }
.topup-step.done .topup-step-dot,
.topup-step.done:not(:last-child)::after { border-color: var(--wb-success); background: var(--wb-success); }
.topup-success-icon {
background: rgba(31, 157, 106, 0.12);
color: var(--wb-success);
}
/* Normalize legacy purple accents without touching business structure. */
.sea-business-page .btn-primary,
.sea-business-form-panel .btn-primary,
.sea-business-detail-page .btn-primary,
.client-shell .btn-flat-purple,
.client-shell .btn-submit {
border-color: var(--wb-action);
border-radius: var(--wb-radius-control);
background: var(--wb-action);
color: #fff;
box-shadow: none;
}
.sea-business-page .btn-primary:hover,
.sea-business-form-panel .btn-primary:hover,
.sea-business-detail-page .btn-primary:hover,
.client-shell .btn-flat-purple:hover,
.client-shell .btn-submit:hover {
background: var(--wb-action-hover);
box-shadow: none;
transform: none;
}
.sea-business-page .sea-order-table th {
border-bottom-color: var(--wb-border);
background: #edf3f7;
color: var(--wb-action);
}
.sea-business-page .sea-order-table thead th {
padding: 12px 14px;
border-bottom: 1px solid var(--wb-border);
background: #edf3f7;
color: var(--wb-action);
font-size: 12px;
font-weight: 700;
}
.client-shell .return-relabel-pagination > div {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: nowrap;
margin-left: auto;
}
.sea-business-page .sea-table-action,
.client-shell :is(.breadcrumb a:hover, .link-primary, .info-section-title, .cost-price, .btn-text, .link-action) {
color: var(--wb-action);
}
.client-shell :is(.info-tab, .dest-tab):hover {
background: #edf3f7;
color: var(--wb-action);
}
.client-shell :is(.info-tab, .dest-tab).active,
.client-shell .page-btn.active {
border-color: var(--wb-action);
background: var(--wb-action);
color: #fff;
box-shadow: none;
}
.client-shell :is(.btn-default, .btn-ghost, .btn-flat, .download-link, .btn-outline-blue, .page-btn):hover {
border-color: var(--wb-action);
background: #edf3f7;
color: var(--wb-action);
box-shadow: none;
transform: none;
}
.client-shell .btn-outline-blue {
border-color: var(--wb-action);
border-radius: var(--wb-radius-control);
color: var(--wb-action);
}
.client-shell :is(.radio-label input[type=radio], input[type=checkbox]) {
accent-color: var(--wb-action);
}
.client-shell :is(.form-input, .form-select, .form-textarea, .filter-input, .filter-select, .page-size-select, .cell-input, .storage-transfer-input, .storage-transfer-select, .storage-transfer-textarea, .storage-transfer-cargo-input, .cargo-disposal-input, .cargo-disposal-select):focus {
border-color: var(--wb-action);
box-shadow: var(--wb-focus);
}
.client-shell :is(.upload-link, .upload-zone, .row-upload-cell, .row-img-cell) {
border-color: #b8ccda;
border-radius: var(--wb-radius-panel);
background: #f7fafc;
color: var(--wb-action);
}
.client-shell :is(.upload-link, .upload-zone, .row-upload-cell, .row-img-cell):hover,
.client-shell .upload-zone.dragover {
border-color: var(--wb-action);
background: #edf3f7;
color: var(--wb-action);
transform: none;
}
.client-shell :is(.tag-blue, .tag-info) {
border-color: rgba(18, 107, 168, 0.24);
background: rgba(18, 107, 168, 0.1);
color: var(--wb-action);
}
.client-shell .cost-badge.recommend { background: var(--wb-action); box-shadow: none; }
.client-shell .modal-section-title {
padding-bottom: 10px;
padding-left: 0;
border-bottom: 1px solid var(--wb-border);
border-left: 0;
color: var(--wb-ink);
}
.client-shell .balance-card {
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: var(--wb-shadow-panel);
}
.client-shell .balance-amount { color: var(--wb-action); }
.client-shell .balance-detail-btn {
border-color: var(--wb-border-strong);
border-radius: var(--wb-radius-control);
background: #fff;
color: var(--wb-action);
box-shadow: none;
}
.client-shell .balance-detail-btn:hover {
border-color: var(--wb-action);
background: #edf3f7;
color: var(--wb-action);
box-shadow: none;
transform: none;
}
.client-shell .info-card {
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: var(--wb-shadow-panel);
backdrop-filter: none;
}
.client-shell .form-footer {
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: var(--wb-shadow-panel);
backdrop-filter: none;
}
.client-shell .cost-card {
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: none;
}
.client-shell .cost-card:hover {
border-color: var(--wb-action);
box-shadow: none;
transform: none;
}
.client-shell .cost-card.active {
border-color: var(--wb-action);
background: #f4f8fb;
box-shadow: none;
}
.client-shell .btn-ghost {
border: 1px solid var(--wb-border-strong);
border-radius: var(--wb-radius-control);
background: #fff;
color: var(--wb-action);
box-shadow: none;
}
.client-shell :is(.btn-primary, .btn-flat-blue, .btn-purple, .btn-flat-purple, .btn-submit):hover,
.dash-topup-btn:hover {
box-shadow: none;
transform: none;
}
.dash-topbar :is(button, a):focus-visible,
.dash-sidebar :is(button, a):focus-visible {
outline: 2px solid var(--wb-action);
outline-offset: 2px;
box-shadow: var(--wb-focus);
}
.dash-topbar-actions { display: flex; align-items: center; gap: 10px; }
.dash-message-btn {
position: relative;
display: inline-flex;
min-height: 36px;
align-items: center;
gap: 7px;
padding: 0 12px;
border: 1px solid var(--wb-border-strong);
border-radius: var(--wb-radius-control);
background: #fff;
color: var(--wb-action);
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: border-color 160ms ease-out, background-color 160ms ease-out, color 160ms ease-out;
}
.dash-message-btn:hover { border-color: var(--wb-action); background: #edf3f7; }
.dash-message-btn i { font-size: 14px; }
.dash-message-badge {
display: inline-flex;
min-width: 19px;
height: 19px;
align-items: center;
justify-content: center;
padding: 0 5px;
border-radius: 999px;
background: #c93643;
color: #fff;
font-size: 11px;
font-weight: 800;
line-height: 1;
}
.dash-message-badge[hidden] { display: none; }
.client-shell :is(input, select, textarea, button):disabled,
.topup-modal :is(input, button):disabled {
border-color: var(--wb-border);
background: #edf2f5;
color: var(--wb-muted);
box-shadow: none;
cursor: not-allowed;
opacity: 0.72;
}
.client-shell .info-tabs {
border-color: var(--wb-border);
border-radius: var(--wb-radius-control);
background: var(--wb-surface);
}
.client-shell .dest-type-tabs {
border-color: var(--wb-border);
border-radius: var(--wb-radius-control);
background: var(--wb-surface);
}
#balanceDetailsModal .balance-summary-item {
border-color: var(--wb-border);
border-radius: var(--wb-radius-panel);
background: #f8fafb;
box-shadow: none;
}
#balanceDetailsModal .balance-detail-filter {
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: #f8fafb;
box-shadow: none;
}
#balanceDetailsModal .balance-detail-table-wrap {
border-color: var(--wb-border);
border-radius: var(--wb-radius-panel);
background: #f8fafb;
box-shadow: none;
}
#balanceDetailsModal .balance-period-btn {
border-color: var(--wb-border-strong);
border-radius: var(--wb-radius-control);
background: #fff;
color: var(--wb-muted);
box-shadow: none;
}
#balanceDetailsModal .balance-period-btn.active {
border-color: var(--wb-action);
background: #edf3f7;
color: var(--wb-action);
box-shadow: none;
}
#balanceDetailsModal .balance-period-btn:hover {
border-color: var(--wb-action);
background: #edf3f7;
color: var(--wb-action);
box-shadow: none;
transform: none;
}
#balanceDetailsModal .balance-date-range input {
border-color: var(--wb-border-strong);
border-radius: var(--wb-radius-control);
color: var(--wb-ink);
}
#balanceDetailsModal .balance-date-range input:focus {
border-color: var(--wb-action);
box-shadow: var(--wb-focus);
}
#balanceDetailsModal .balance-filter-action {
border-radius: var(--wb-radius-control);
background: var(--wb-action);
color: #fff;
box-shadow: none;
}
#balanceDetailsModal .balance-filter-action:hover {
background: var(--wb-action-hover);
box-shadow: none;
transform: none;
}
#balanceDetailsModal .balance-filter-reset {
border: 1px solid var(--wb-border-strong);
background: #fff;
color: var(--wb-action);
}
#balanceDetailsModal .balance-detail-table th {
background: #edf3f7;
color: var(--wb-action);
}
.client-shell .profile-address-panel {
overflow: hidden;
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: var(--wb-shadow-panel);
}
.client-shell .profile-address-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 18px 20px;
border-bottom: 1px solid var(--wb-border);
}
.client-shell .profile-address-header .info-section-title { margin-bottom: 3px; }
.client-shell .profile-address-header .btn { display: inline-flex; align-items: center; gap: 7px; }
.client-shell .profile-address-subtitle { color: var(--wb-muted); font-size: 12px; }
.client-shell .profile-address-table-wrap {
max-width: 100%;
position: relative;
overflow-x: auto;
background: #fff;
overscroll-behavior-x: contain;
-webkit-overflow-scrolling: touch;
}
.client-shell .profile-address-table {
width: 100%;
min-width: 1220px;
border-collapse: collapse;
color: var(--wb-ink);
font-size: 13px;
}
.client-shell .profile-address-table th {
padding: 12px 14px;
border-bottom: 1px solid var(--wb-border);
background: #edf3f7;
color: var(--wb-action);
font-size: 12px;
font-weight: 700;
text-align: left;
white-space: nowrap;
}
.client-shell .profile-address-table td {
padding: 13px 14px;
border-bottom: 1px solid #edf1f4;
color: #33475b;
vertical-align: middle;
white-space: nowrap;
}
.client-shell .profile-address-table tbody tr:last-child td { border-bottom: 0; }
.client-shell .profile-address-table tbody tr:hover { background: #f8fafb; }
.client-shell .profile-address-actions { display: flex; align-items: center; gap: 12px; }
.client-shell .profile-address-action {
padding: 2px 0;
border: 0;
background: transparent;
color: var(--wb-action);
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.client-shell .profile-address-action:hover { text-decoration: underline; }
.client-shell .profile-address-action.is-delete { color: #c43b46; }
.client-shell .profile-address-status {
display: inline-flex;
min-height: 24px;
align-items: center;
padding: 3px 9px;
border-radius: 999px;
background: #e8f6ef;
color: #187a52;
font-size: 12px;
font-weight: 700;
}
.client-shell .profile-address-status.is-disabled { background: #eef1f4; color: #647382; }
.client-shell .profile-address-empty { height: 120px; color: var(--wb-muted) !important; text-align: center; }
.client-shell .profile-address-pagination {
display: flex;
min-height: 58px;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 10px 16px;
border-top: 1px solid var(--wb-border);
color: var(--wb-muted);
font-size: 13px;
}
.client-shell .profile-address-pagination strong { color: var(--wb-ink); }
.client-shell .profile-address-page-controls { display: flex; align-items: center; gap: 7px; }
.client-shell .profile-address-page-controls button {
min-width: 64px;
height: 34px;
padding: 0 10px;
border: 1px solid var(--wb-border-strong);
border-radius: var(--wb-radius-control);
background: #fff;
color: var(--wb-action);
font-size: 12px;
cursor: pointer;
}
.client-shell .profile-address-page-controls button:hover:not(:disabled) { border-color: var(--wb-action); background: #edf3f7; }
.client-shell .profile-address-page-controls span { min-width: 50px; color: var(--wb-ink); text-align: center; }
.client-shell .profile-address-modal-box {
width: min(720px, calc(100vw - 32px));
max-width: none;
border-radius: var(--wb-radius-modal);
box-shadow: var(--wb-shadow-modal);
}
.client-shell .profile-address-modal-box form { display: flex; min-height: 0; flex: 1; flex-direction: column; }
.client-shell .profile-address-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 16px; }
.client-shell .profile-address-field { min-width: 0; }
.client-shell .profile-address-field-wide { grid-column: 1 / -1; }
.client-shell .profile-address-field label {
display: block;
margin-bottom: 7px;
color: var(--wb-ink);
font-size: 13px;
font-weight: 600;
}
.client-shell .profile-address-field .form-input { width: 100%; min-width: 0; }
.client-shell .profile-address-form-error { min-height: 20px; margin-top: 12px; color: #b4232d; font-size: 12px; }
.client-shell .profile-address-delete-box { width: min(440px, calc(100vw - 32px)); }
.client-shell .profile-address-delete-copy { margin: 0; color: var(--wb-ink); font-size: 14px; }
.client-shell .profile-address-delete-hint { margin: 8px 0 0; color: var(--wb-muted); font-size: 12px; }
.client-shell .profile-address-delete-confirm {
border: 1px solid #b4232d;
background: #b4232d;
color: #fff;
}
.client-shell .profile-address-delete-confirm:hover { border-color: #941f28; background: #941f28; }
.client-shell .profile-message-panel {
overflow: hidden;
border: 1px solid var(--wb-border);
border-radius: var(--wb-radius-panel);
background: var(--wb-surface);
box-shadow: var(--wb-shadow-panel);
}
.client-shell .profile-message-header {
display: flex;
min-height: 76px;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px 20px;
border-bottom: 1px solid var(--wb-border);
}
.client-shell .profile-message-title-row { display: flex; align-items: center; gap: 10px; }
.client-shell .profile-message-title-row .info-section-title { margin-bottom: 0; }
.client-shell .profile-message-subtitle { margin-top: 4px; color: var(--wb-muted); font-size: 12px; }
.client-shell .profile-message-count,
.client-shell .profile-message-tab-count {
display: inline-flex;
min-width: 22px;
height: 22px;
align-items: center;
justify-content: center;
padding: 0 6px;
border-radius: 999px;
background: #e8f1f7;
color: var(--wb-action);
font-size: 11px;
font-weight: 800;
}
.client-shell .info-tab.active .profile-message-tab-count { background: rgba(255,255,255,0.2); color: #fff; }
.client-shell .profile-message-tab-count[hidden] { display: none; }
.client-shell .profile-message-list { min-height: 168px; }
.client-shell .profile-message-item {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
border-bottom: 1px solid #edf1f4;
background: #fff;
}
.client-shell .profile-message-item:last-child { border-bottom: 0; }
.client-shell .profile-message-item.is-unread { background: #f7fbfd; }
.client-shell .profile-message-toggle {
display: grid;
grid-template-columns: 40px minmax(0, 1fr);
gap: 12px;
align-items: start;
padding: 16px 20px;
border: 0;
background: transparent;
color: inherit;
text-align: left;
cursor: pointer;
}
.client-shell .profile-message-toggle:hover .profile-message-title { color: var(--wb-action); }
.client-shell .profile-message-toggle:focus-visible { outline: 2px solid var(--wb-action); outline-offset: -3px; }
.client-shell .profile-message-icon {
display: inline-flex;
width: 40px;
height: 40px;
align-items: center;
justify-content: center;
border-radius: 10px;
background: #e8f1f7;
color: var(--wb-action);
font-size: 15px;
}
.client-shell .profile-message-copy { min-width: 0; }
.client-shell .profile-message-heading { display: flex; min-width: 0; align-items: center; gap: 8px; }
.client-shell .profile-message-dot {
width: 7px;
height: 7px;
flex: 0 0 7px;
border-radius: 50%;
background: #c93643;
}
.client-shell .profile-message-title { overflow: hidden; color: var(--wb-ink); font-size: 14px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
.client-shell .profile-message-preview { margin-top: 5px; overflow: hidden; color: var(--wb-muted); font-size: 13px; line-height: 1.55; text-overflow: ellipsis; white-space: nowrap; }
.client-shell .profile-message-detail {
grid-column: 1 / -1;
margin: 0 20px 16px 72px;
padding: 12px 14px;
border-radius: var(--wb-radius-control);
background: #edf3f7;
color: #334b5f;
font-size: 13px;
line-height: 1.7;
}
.client-shell .profile-message-meta { display: flex; align-items: center; gap: 12px; padding: 16px 20px 16px 0; color: var(--wb-muted); font-size: 12px; white-space: nowrap; }
.client-shell .profile-message-state { color: #607482; font-weight: 700; }
.client-shell .profile-message-item.is-unread .profile-message-state { color: #c93643; }
.client-shell .profile-message-empty { display: grid; min-height: 168px; place-items: center; color: var(--wb-muted); font-size: 13px; }
/* First-leg order: six continuous modules */
.client-shell #page-order {
--fl-accent: #116fa5;
--fl-ink: #183247;
--fl-muted: #5f7384;
--fl-border: #d5e0e7;
--fl-soft: #f5f8fa;
--fl-surface: #ffffff;
--fl-border-strong: #b8cad5;
--fl-ink-soft: #36556d;
--fl-table-head: #edf3f6;
--fl-accent-soft: #eef6fa;
--fl-accent-hover: #0c5c8a;
--fl-danger: #bf3f4b;
--fl-danger-soft: #fff5f6;
--fl-teal: #1d777c;
--fl-radius-panel: 8px;
--fl-radius-control: 6px;
color: var(--fl-ink);
scroll-behavior: smooth;
}
.client-shell #page-order .form-footer { position: static; }
.client-shell .first-leg-address-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
align-items: start;
}
.client-shell #first-leg-origin { height: 380px; overflow: hidden; }
.client-shell #first-leg-origin .card-body { height: calc(100% - 61px); overflow-y: auto; }
.client-shell #first-leg-destination { height: 380px; overflow: hidden; }
.client-shell #first-leg-destination .card-body { height: calc(100% - 61px); overflow-y: auto; }
.client-shell .first-leg-module .form-group { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 12px; }
.client-shell .pickup-time-address-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.client-shell .first-leg-module .form-label { min-width: 0; }
.client-shell .first-leg-module :is(.form-input, .form-select, .form-textarea) { max-width: 100%; }
.client-shell .first-leg-address-grid .dest-type-tabs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); width: 100%; max-width: 100%; overflow: visible; }
.client-shell #first-leg-origin #pickup-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.client-shell #first-leg-destination #dest-tabs { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.client-shell .first-leg-address-grid .dest-tab { min-width: 0; padding-inline: 12px; }
.client-shell :is(#first-leg-origin, #first-leg-destination) .form-content > :is(.form-input, .form-select, .form-textarea, .input-wrap, .select-wrap) { width: 100%; min-width: 0 !important; }
.client-shell :is(#first-leg-origin, #first-leg-destination) :is(.input-wrap, .select-wrap) > :is(.form-input, .form-select) { width: 100%; min-width: 0 !important; }
.client-shell .first-leg-private-location-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.client-shell .first-leg-destination-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.client-shell .pickup-driver-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.client-shell .pickup-driver-row .form-group { grid-template-columns: 96px minmax(0, 1fr); gap: 12px; }
.client-shell #first-leg-destination .form-group { grid-template-columns: 88px minmax(0, 1fr); gap: 8px; }
.client-shell .first-leg-header-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; }
.client-shell .first-leg-table-wrap {
max-width: 100%;
overflow-x: auto;
overscroll-behavior-x: contain;
}
.client-shell #product-table { min-width: 2200px; }
.client-shell #box-table { min-width: 1180px; }
.client-shell .row-img-cell { display: inline-flex; align-items: center; justify-content: center; width: 40px; min-height: 32px; border: 1px dashed var(--wb-border-strong); border-radius: var(--wb-radius-control); color: var(--wb-action); cursor: pointer; }
.client-shell .row-img-cell input { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; }
.client-shell .empty-row td { padding: 36px 16px !important; color: var(--wb-muted); text-align: center; }
.client-shell .empty-icon { margin-bottom: 8px; color: #8fa3b4; font-size: 28px; }
.client-shell .first-leg-hints { display: flex; gap: 18px; margin-top: 12px; color: var(--wb-muted); font-size: 12px; }
.client-shell .first-leg-requirements-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: start; gap: 18px 24px; }
.client-shell .first-leg-requirements-grid .form-group { grid-template-columns: 96px minmax(0, 1fr); align-items: center; gap: 12px; margin-bottom: 0; }
.client-shell .first-leg-requirements-grid .form-label { text-align: right; padding-top: 0; }
.client-shell .first-leg-requirements-grid .form-content,
.client-shell .first-leg-requirements-grid .select-wrap,
.client-shell .first-leg-requirements-grid .form-select { width: 100%; min-width: 0; }
.client-shell .first-leg-requirements-grid .form-content { min-height: 40px; }
.client-shell .first-leg-requirements-grid :is(.form-input, .form-select) { width: 100%; min-width: 0; }
.client-shell .first-leg-requirements-grid .date-range { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; min-width: 0; gap: 4px; }
.client-shell .first-leg-summary {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 0;
margin: 0;
padding: 18px 20px;
}
.client-shell .first-leg-summary > div { display: flex; align-items: flex-start; justify-content: center; flex-direction: column; gap: 6px; min-width: 0; padding: 2px 18px; border-right: 1px solid var(--fl-border); }
.client-shell .first-leg-summary > div:last-child { border-right: 0; }
.client-shell .first-leg-summary span { color: var(--wb-muted); font-size: 12px; }
.client-shell .first-leg-summary strong { color: var(--wb-ink); font-size: 18px; font-weight: 700; }
.client-shell .first-leg-summary b { font: inherit; }
.client-shell .td-input.is-invalid,
.client-shell .box-sku.is-invalid,
.client-shell .form-input.is-invalid,
.client-shell .form-select.is-invalid,
.client-shell .form-textarea.is-invalid { border-color: #d14343; background: #fff7f7; }
/* First-leg order: scoped visual polish */
.client-shell #page-order .page-header { margin-bottom: 16px; }
.client-shell #page-order .page-title { color: var(--fl-ink); font-size: 26px; line-height: 1.3; }
.client-shell #page-order .page-subtitle { margin-top: 5px; color: var(--fl-muted); font-size: 13px; }
.client-shell #page-order .first-leg-address-grid { gap: 18px; }
.client-shell #page-order .first-leg-module {
overflow: hidden;
border: 1px solid var(--fl-border);
border-radius: var(--fl-radius-panel);
background: var(--fl-surface);
box-shadow: none;
}
.client-shell #page-order .first-leg-module .card-header {
min-height: 56px;
padding: 14px 20px;
border-bottom: 1px solid var(--fl-border);
background: #fbfcfd;
}
.client-shell #page-order .first-leg-module .card-title { color: var(--fl-ink); font-size: 16px; font-weight: 700; line-height: 1.4; }
.client-shell #page-order .first-leg-module .card-body { padding: 20px; }
.client-shell #page-order .first-leg-module .form-label { color: var(--fl-ink-soft); font-size: 13px; font-weight: 600; }
.client-shell #page-order .first-leg-module .form-label .required { color: var(--fl-danger); }
.client-shell #page-order .first-leg-module :is(.form-input, .form-select, .form-textarea) {
border: 1px solid var(--fl-border-strong);
border-radius: var(--fl-radius-control);
background: var(--fl-surface);
color: var(--fl-ink);
box-shadow: none;
transition: border-color 160ms ease, background-color 160ms ease;
}
.client-shell #page-order .first-leg-module :is(.form-input, .form-select, .form-textarea)::placeholder { color: #667b8d; }
.client-shell #page-order .first-leg-module :is(.form-input, .form-select, .form-textarea):hover { border-color: #91adbd; }
.client-shell #page-order .first-leg-module :is(.form-input, .form-select, .form-textarea):focus {
border-color: var(--fl-accent);
outline: 3px solid #d8eaf4;
outline-offset: 1px;
box-shadow: none;
}
.client-shell #page-order .radio-label { color: var(--fl-ink-soft); font-weight: 500; }
.client-shell #page-order .radio-label:hover { color: var(--fl-accent); }
.client-shell #page-order .radio-label input[type=radio] { accent-color: var(--fl-accent); }
.client-shell #page-order .dest-type-tabs {
gap: 2px;
padding: 3px;
border: 1px solid var(--fl-border);
border-radius: var(--fl-radius-control);
background: var(--fl-soft);
}
.client-shell #page-order .dest-tab {
min-height: 32px;
border: 0;
border-radius: 4px;
background: transparent;
color: var(--fl-muted);
font-weight: 600;
box-shadow: none;
transition: background-color 160ms ease, color 160ms ease;
}
.client-shell #page-order .dest-tab:hover { background: var(--fl-surface); color: var(--fl-accent); }
.client-shell #page-order .dest-tab.active { background: var(--fl-accent); color: #fff; box-shadow: none; }
.client-shell #page-order .first-leg-header-actions > :is(.upload-link, .download-link, .btn-flat) {
min-height: 36px;
padding: 0 12px;
border: 1px solid var(--fl-border-strong);
border-radius: var(--fl-radius-control);
background: var(--fl-surface);
color: var(--fl-ink-soft);
font-size: 12px;
font-weight: 600;
box-shadow: none;
transform: none;
transition: border-color 160ms ease, background-color 160ms ease, color 160ms ease;
}
.client-shell #page-order .first-leg-header-actions > .upload-link { border-color: #9fc4d8; background: var(--fl-accent-soft); color: var(--fl-accent); }
.client-shell #page-order .first-leg-header-actions > :is(.upload-link, .download-link, .btn-flat):hover { border-color: var(--fl-accent); background: var(--fl-accent-soft); color: var(--fl-accent); transform: none; }
.client-shell #page-order .first-leg-header-actions > .btn-flat-red { border-color: #e4b5bb; background: var(--fl-surface); color: var(--fl-danger); }
.client-shell #page-order .first-leg-header-actions > .btn-flat-red:hover { border-color: var(--fl-danger); background: var(--fl-danger-soft); color: var(--fl-danger); }
.client-shell #page-order :is(button, a, .upload-link):focus-visible {
outline: 3px solid #d8eaf4;
outline-offset: 2px;
}
.client-shell #page-order .first-leg-table-wrap { border: 1px solid var(--fl-border); border-radius: var(--fl-radius-control); background: var(--fl-surface); box-shadow: none; }
.client-shell #page-order .goods-table thead th { height: 44px; padding: 0 12px; border-bottom: 1px solid var(--fl-border); background: var(--fl-table-head); color: var(--fl-ink-soft); font-size: 12px; font-weight: 700; letter-spacing: 0; white-space: nowrap; }
.client-shell #page-order .goods-table th .required { color: var(--fl-danger); margin-right: 4px; }
.client-shell #page-order .goods-table tbody td { padding: 10px 12px; border-bottom-color: var(--fl-border); color: var(--fl-ink-soft); }
.client-shell #page-order .empty-row td { background: #fbfcfd; color: var(--fl-muted); }
.client-shell #page-order .empty-icon { color: #7f9aac; font-size: 26px; }
.client-shell #page-order .first-leg-hints { color: var(--fl-muted); line-height: 1.6; }
.client-shell #page-order .first-leg-requirements-grid { gap: 18px 24px; }
.client-shell #page-order .first-leg-cost-overview { display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, 0.42fr); overflow: hidden; border: 1px solid var(--fl-border); border-radius: var(--fl-radius-panel); background: var(--fl-soft); }
.client-shell #page-order .first-leg-summary { background: transparent; }
.client-shell #page-order .first-leg-summary span { color: var(--fl-muted); }
.client-shell #page-order .first-leg-summary strong { color: var(--fl-ink); font-size: 20px; }
.client-shell #page-order .first-leg-quote { display: flex; align-items: flex-start; justify-content: center; flex-direction: column; gap: 6px; padding: 18px 24px; border-left: 1px solid var(--fl-border); background: var(--fl-accent-soft); text-align: left; }
.client-shell #page-order .first-leg-quote-label { color: var(--fl-ink-soft); font-size: 13px; font-weight: 600; }
.client-shell #page-order .first-leg-quote-price { display: flex; align-items: baseline; gap: 4px; color: var(--fl-accent); font-size: 34px; font-weight: 800; line-height: 1.05; }
.client-shell #page-order .first-leg-quote-price span { font-size: 19px; font-weight: 700; }
.client-shell #page-order .first-leg-quote-price b { font: inherit; }
.client-shell #page-order .first-leg-quote-note { color: var(--fl-muted); font-size: 12px; line-height: 1.5; }
.client-shell #page-order .form-footer { padding: 16px 20px; border: 1px solid var(--fl-border); border-radius: var(--fl-radius-panel); background: var(--fl-surface); box-shadow: none; }
.client-shell #page-order .footer-tips { color: var(--fl-muted); }
.client-shell #page-order .form-footer .btn-primary { border-color: var(--fl-accent); background: var(--fl-accent); box-shadow: none; transform: none; }
.client-shell #page-order .form-footer .btn-primary:hover { border-color: var(--fl-accent-hover); background: var(--fl-accent-hover); box-shadow: none; transform: none; }
.client-shell #page-order .form-footer .btn-default { border-color: var(--fl-border-strong); background: var(--fl-surface); color: var(--fl-ink-soft); }
@media (max-width: 1200px) {
.dash-main > .flex > main { padding: 24px; }
.client-shell .sea-order-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 901px) and (max-width: 1199px) {
.client-shell #first-leg-origin { height: 420px; }
.client-shell #first-leg-destination { height: 420px; }
.client-shell #first-leg-destination .first-leg-destination-pair .form-group { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.client-shell .pickup-driver-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
}
@media (max-width: 900px) {
.dash-sidebar {
width: min(280px, calc(100vw - 48px));
box-shadow: 18px 0 48px rgba(11, 39, 67, 0.24);
}
.dash-topbar {
min-height: 56px;
padding: 0;
}
.dash-topbar > div {
min-height: 56px;
padding-right: 16px !important;
padding-left: 16px !important;
}
.dash-main > .flex > main { padding: 20px 16px 32px; }
.client-shell .page-title { font-size: 22px; }
.client-shell .first-leg-address-grid,
.client-shell #page-order .first-leg-cost-overview { grid-template-columns: minmax(0, 1fr); }
.client-shell .first-leg-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.client-shell .first-leg-requirements-grid { grid-template-columns: minmax(0, 1fr); }
.client-shell #first-leg-origin { height: auto; overflow: visible; }
.client-shell #first-leg-origin .card-body { height: auto; overflow-y: visible; }
.client-shell #first-leg-destination { height: auto; overflow: visible; }
.client-shell #first-leg-destination .card-body { height: auto; overflow-y: visible; }
.client-shell .first-leg-module .form-group { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.client-shell .pickup-time-address-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.client-shell .first-leg-module .form-label { padding-top: 0; text-align: left; }
.client-shell #page-order #pickup-tabs {
display: grid;
grid-template-columns: 1.35fr 1fr 1fr;
width: 100%;
overflow: visible;
}
.client-shell #page-order #pickup-tabs .dest-tab {
min-width: 0;
padding-inline: 5px;
white-space: nowrap;
}
.client-shell .first-leg-requirements-grid .form-group { grid-template-columns: 96px minmax(0, 1fr); gap: 12px; }
.client-shell .first-leg-requirements-grid .form-label { text-align: right; }
.client-shell #first-leg-destination .form-group { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.client-shell .first-leg-private-location-row .form-label { text-align: left; }
.client-shell .first-leg-destination-pair { grid-template-columns: minmax(0, 1fr); }
.client-shell .first-leg-destination-pair .form-group { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.client-shell #page-order .first-leg-quote { align-items: flex-start; border-top: 1px solid var(--fl-border); border-left: 0; text-align: left; }
.client-shell .first-leg-summary > div:nth-child(2) { border-right: 0; }
.client-shell .first-leg-summary > div:nth-child(-n+2) { padding-bottom: 14px; border-bottom: 1px solid var(--fl-border); }
.client-shell .first-leg-summary > div:nth-child(n+3) { padding-top: 14px; }
.client-shell .first-leg-module :is(.form-input, .form-select, .form-textarea) { width: 100%; min-width: 0 !important; }
.client-shell .first-leg-hints { align-items: flex-start; flex-direction: column; gap: 6px; }
.client-shell .first-leg-summary > div { min-height: 32px; }
.client-shell #page-order .table-toolbar { align-items: stretch; flex-direction: column; }
.client-shell #page-order .table-actions { flex-wrap: wrap; }
.client-shell #page-order :is(#first-leg-products, #first-leg-boxes) .card-header { align-items: flex-start; flex-wrap: wrap; }
.client-shell #page-order .first-leg-header-actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
width: 100%;
gap: 8px;
}
.client-shell #page-order .first-leg-header-actions > * {
width: 100%;
justify-content: center;
}
.client-shell .sea-order-filters,
.client-shell .filter-panel,
.client-shell .exception-order-filter-grid,
.client-shell .form-grid,
.client-shell .return-relabel-form-grid,
.client-shell .storage-transfer-summary-grid {
grid-template-columns: minmax(0, 1fr);
}
.client-shell .filter-grid {
grid-template-columns: minmax(0, 1fr);
}
.client-shell .info-grid {
grid-template-columns: minmax(0, 1fr);
}
.client-shell .info-two-col {
grid-template-columns: minmax(0, 1fr);
}
.client-shell .filter-grid .filter-item,
.client-shell .info-grid .info-row {
align-items: flex-start;
flex-direction: column;
}
.client-shell .info-grid .info-label,
.client-shell .info-grid .info-value {
width: 100%;
}
.client-shell .info-value .form-input {
width: 100% !important;
min-width: 0 !important;
max-width: 100%;
}
.client-shell .quick-table-wrap {
max-width: 100%;
position: relative;
overflow-x: auto;
overflow-y: hidden;
overscroll-behavior-x: contain;
-webkit-overflow-scrolling: touch;
}
.client-shell :is(.data-table, .product-table, .goods-table, .quick-table, .sea-order-table) {
min-width: max-content;
}
.client-shell .modal-box,
.sea-edit-dialog,
.topup-modal {
width: calc(100vw - 24px);
max-height: calc(100vh - 24px);
}
.client-shell .modal-footer,
.topup-foot {
gap: 10px;
flex-wrap: wrap;
}
.client-shell .modal-footer > *,
.topup-foot > * {
min-width: 96px;
}
.client-shell .profile-address-header { align-items: stretch; flex-direction: column; }
.client-shell .profile-address-header .btn { width: 100%; justify-content: center; }
.client-shell .profile-address-form-grid { grid-template-columns: minmax(0, 1fr); }
.client-shell .profile-address-field-wide { grid-column: auto; }
.client-shell .profile-address-modal-box { width: calc(100vw - 20px); max-height: calc(100vh - 20px); }
.client-shell .profile-address-pagination { align-items: flex-start; flex-direction: column; }
.client-shell .profile-message-header { align-items: stretch; flex-direction: column; }
.client-shell .profile-message-header .btn { width: 100%; justify-content: center; }
.client-shell .profile-message-item { grid-template-columns: minmax(0, 1fr); }
.client-shell .profile-message-toggle { padding: 14px 16px 8px; }
.client-shell .profile-message-meta { justify-content: space-between; padding: 0 16px 14px 68px; }
.client-shell .profile-message-detail { margin: 0 16px 14px 68px; }
}
@media (max-width: 620px) {
.dash-topbar-actions { gap: 7px; }
.dash-topup-btn,
.dash-message-btn { width: 36px; height: 36px; min-height: 36px; justify-content: center; padding: 0; }
.dash-topup-btn span,
.dash-message-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.dash-message-badge { position: absolute; top: -6px; right: -6px; border: 2px solid #fff; }
.client-shell .info-tabs { width: 100%; max-width: 100%; overflow-x: auto; }
.client-shell .info-tab { flex: 0 0 auto; padding: 11px 18px; white-space: nowrap; }
.client-shell .profile-message-item { overflow: hidden; }
.client-shell .profile-message-toggle { width: 100%; min-width: 0; }
.client-shell .profile-message-copy,
.client-shell .profile-message-heading { width: 100%; min-width: 0; }
.client-shell .profile-message-preview { display: -webkit-box; overflow: hidden; white-space: normal; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
}
</style>
<style>
.labeling-detail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 20px; }
.labeling-detail-card { min-width: 0; margin: 0; }
.labeling-detail-card .info-section-title { margin: 0 0 14px; padding-bottom: 10px; border-bottom: 1px solid #eef2f6; color: #1f3347; font-size: 14px; font-weight: 700; }
.labeling-detail-status { display: inline-flex; align-items: center; min-height: 30px; padding: 0 12px; border-radius: 999px; background: #fff4df; color: #d97800; font-size: 12px; font-weight: 700; }
.labeling-detail-status.is-processing { background: #e6f7ff; color: #1677ff; }
.labeling-detail-status.is-done { background: #f0f9eb; color: #52a31b; }
.labeling-detail-card dl { margin: 0; }
.labeling-detail-field { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 12px; align-items: center; min-height: 42px; padding: 0 16px; border-bottom: 1px solid #edf0f4; font-size: 13px; }
.labeling-detail-field:last-child { border-bottom: 0; }
.labeling-detail-field dt { color: #718092; }
.labeling-detail-field dd { margin: 0; color: #1d2d3e; font-weight: 500; overflow-wrap: anywhere; }
.labeling-detail-field dd.is-emphasis { font-weight: 700; }
.labeling-detail-file { color: #1766ad !important; }
.labeling-detail-list-action { color: #1766ad; font-size: 12px; cursor: pointer; }
.client-shell .labeling-edit-modal-box { width: min(1180px, calc(100vw - 32px)); max-width: none; max-height: min(92vh, 920px); }
.client-shell .labeling-edit-modal-box form { display: flex; min-height: 0; flex: 1; flex-direction: column; }
.client-shell .labeling-edit-modal-box .modal-body { overflow-y: auto; padding: 22px 28px; }
.client-shell .labeling-edit-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 24px; }
.client-shell .labeling-edit-form-grid .form-group { margin: 0; }
.client-shell .labeling-edit-form-grid .form-label { white-space: nowrap; }
.client-shell .labeling-edit-form-grid .form-input { width: 100%; max-width: 360px; }
.client-shell .labeling-edit-form-grid .labeling-edit-field-wide { grid-column: 1 / -1; }
.client-shell .labeling-line-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; }
.client-shell .labeling-edit-table-wrap { overflow-x: auto; margin-top: 6px; }
.client-shell #labeling-table { min-width: 1740px; }
.client-shell .labeling-edit-table { min-width: 1680px; }
@media (max-width: 780px) {
.client-shell .labeling-edit-modal-box { width: calc(100vw - 20px); max-height: calc(100vh - 20px); }
.client-shell .labeling-edit-modal-box .modal-body { padding: 16px; }
.client-shell .labeling-edit-form-grid { grid-template-columns: 1fr; gap: 14px; }
.client-shell .labeling-edit-form-grid .labeling-edit-field-wide { grid-column: auto; }
}
.return-relabel-detail-order-card { grid-column: 1 / -1; }
.return-relabel-detail-order-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 20px; }
.storage-transfer-detail-header .labeling-detail-status,
.storage-transfer-detail-header .btn { white-space: nowrap; }
@media (max-width: 760px) {
.labeling-detail-grid { grid-template-columns: minmax(0, 1fr); gap: 12px; }
.labeling-detail-field { grid-template-columns: 104px minmax(0, 1fr); padding: 0 12px; }
.return-relabel-detail-order-card { grid-column: auto; }
.return-relabel-detail-order-grid { grid-template-columns: minmax(0, 1fr); }
.storage-transfer-detail-header { align-items: stretch; flex-direction: column; gap: 12px; }
.storage-transfer-detail-header .page-action-group { justify-content: space-between; width: 100%; }
.cargo-disposal-detail-header { align-items: flex-start !important; flex-direction: column; gap: 14px; }
.cargo-disposal-detail-actions { width: 100%; align-items: center; justify-content: space-between; }
.cargo-disposal-detail-actions .labeling-detail-status,
.cargo-disposal-detail-actions .btn { white-space: nowrap; }
}
</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">
<!-- 业务总览(工作台首页) -->
<a class="dash-nav-link active" data-view="Home" onclick="dashNavigate('Home', this)">
<i class="fas fa-gauge-high"></i><span data-i18n="m_overview">业务总览</span>
</a>
<!-- 头程物流 -->
<div class="dash-menu-group">
<div class="dash-menu-group-header collapsed" onclick="toggleDashGroup(this)">
<span class="g-icon"><i class="fas fa-ship"></i></span>
<span class="g-title" data-i18n="m_g_head">头程物流</span>
<span class="g-arrow"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="dash-menu-sub collapsed">
<a class="dash-nav-link sub hidden" data-gicon="fa-file-invoice" data-gtitle="m_head_order" data-gsub="m_head_order_sub" onclick="dashNavigate('order', this)"><i class="fas fa-file-invoice"></i><span data-i18n="m_head_order">头程下单</span></a>
<a class="dash-nav-link sub hidden" data-gicon="fa-file-import" data-gtitle="m_head_bulk" data-gsub="m_head_bulk_sub" onclick="dashNavigate('bulk', this)"><i class="fas fa-file-import"></i><span data-i18n="m_head_bulk">一键导入</span></a>
<a class="dash-nav-link sub" data-gicon="fa-bolt" data-gtitle="m_head_quick" data-gsub="m_head_quick_sub" onclick="dashNavigate('quick', this)"><i class="fas fa-bolt"></i><span data-i18n="m_head_quick">快速下单</span></a>
<a class="dash-nav-link sub" data-gicon="fa-boxes-stacked" data-gtitle="m_inbound" data-gsub="m_inbound_sub" onclick="dashNavigate('inbound', this)"><i class="fas fa-boxes-stacked"></i><span data-i18n="m_inbound">订单列表</span></a>
</div>
</div>
<!-- 出海下单 -->
<div class="dash-menu-group">
<div class="dash-menu-group-header collapsed" onclick="toggleDashGroup(this)">
<span class="g-icon"><i class="fas fa-plane-departure"></i></span>
<span class="g-title" data-i18n="m_g_sea">出海下单</span>
<span class="g-arrow"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="dash-menu-sub collapsed">
<a class="dash-nav-link sub" href="#" data-view="Procurement" data-business="Procurement" onclick="openSeaBusiness('Procurement','list',this);return false;"><i class="fas fa-shopping-cart"></i><span>海外采购</span></a>
<a class="dash-nav-link sub" href="#" data-view="Truck" data-business="Truck" onclick="openSeaBusiness('Truck','list',this);return false;"><i class="fas fa-truck-moving"></i><span>美国本土卡</span></a>
<a class="dash-nav-link sub" href="#" data-view="Pallet" data-business="Pallet" onclick="openSeaBusiness('Pallet','list',this);return false;"><i class="fas fa-pallet"></i><span>美本土散板</span></a>
</div>
</div>
<!-- 海外仓 -->
<div class="dash-menu-group">
<div class="dash-menu-group-header collapsed" onclick="toggleDashGroup(this)">
<span class="g-icon"><i class="fas fa-warehouse"></i></span>
<span class="g-title" data-i18n="m_g_overseas">海外仓</span>
<span class="g-arrow"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="dash-menu-sub collapsed">
<a class="dash-nav-link sub" data-gicon="fa-tags" data-gtitle="m_ov_label" data-gsub="m_ov_label_sub" onclick="dashNavigate('labeling-list', this)"><i class="fas fa-tags"></i><span data-i18n="m_ov_label">贴标申请</span></a>
<a class="dash-nav-link sub" data-gicon="fa-truck-fast" onclick="dashNavigate('storage-transfer-list', this)"><i class="fas fa-truck-fast"></i><span>仓储转运</span></a>
<a class="dash-nav-link sub" data-gicon="fa-box-open" onclick="dashNavigate('cargo-disposal-list', this)"><i class="fas fa-box-open"></i><span>货物废弃</span></a>
<a class="dash-nav-link sub" data-gicon="fa-retweet" onclick="dashNavigate('return-relabel-list', this)"><i class="fas fa-retweet"></i><span>退货换标</span></a>
</div>
</div>
<!-- 客户服务 -->
<div class="dash-menu-group">
<div class="dash-menu-group-header collapsed" onclick="toggleDashGroup(this)">
<span class="g-icon"><i class="fas fa-clipboard-list"></i></span>
<span class="g-title" data-i18n="m_g_service">客户服务</span>
<span class="g-arrow"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="dash-menu-sub collapsed">
<a class="dash-nav-link sub hidden" data-gicon="fa-truck-ramp-box" data-gtitle="m_delivery" data-gsub="m_delivery_sub" onclick="dashNavigate('delivery', this)"><i class="fas fa-truck-ramp-box"></i><span data-i18n="m_delivery">派送订单列表</span></a>
<a class="dash-nav-link sub" data-gicon="fa-triangle-exclamation" onclick="dashNavigate('exception-orders', this)"><i class="fas fa-triangle-exclamation"></i><span>异常订单</span></a>
</div>
</div>
<!-- 财务结算 -->
<div class="dash-menu-group">
<div class="dash-menu-group-header collapsed" onclick="toggleDashGroup(this)">
<span class="g-icon"><i class="fas fa-file-invoice-dollar"></i></span>
<span class="g-title" data-i18n="m_g_fin">财务结算</span>
<span class="g-arrow"><i class="fas fa-chevron-down"></i></span>
</div>
<div class="dash-menu-sub collapsed">
<a class="dash-nav-link sub" data-gicon="fa-money-bill-wave" data-gtitle="m_bizfee" data-gsub="m_bizfee_sub" onclick="dashNavigate('biz-fee', this)"><i class="fas fa-money-bill-wave"></i><span data-i18n="m_bizfee">应付账单</span></a>
</div>
</div>
<div class="dash-nav-divider"></div>
<!-- 设置类 -->
<a class="dash-nav-link" data-gicon="fa-building" data-gtitle="m_company" data-gsub="m_company_sub" onclick="dashNavigate('company', this)">
<i class="fas fa-building"></i><span data-i18n="m_company">公司信息</span>
</a>
<a class="dash-nav-link" data-gicon="fa-user" data-gtitle="m_profile" data-gsub="m_profile_sub" onclick="dashNavigate('profile', this)">
<i class="fas fa-user"></i><span data-i18n="m_profile">我的信息</span>
</a>
<a class="dash-nav-link logout" onclick="doLogout()">
<i class="fas fa-sign-out-alt"></i><span data-i18n="dash_logout">退出登录</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>
</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-5 lg:px-6 h-14 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>
</div>
<div class="dash-topbar-actions">
<button type="button" class="dash-message-btn" id="dash-message-button" onclick="openProfileMessages()" aria-label="我的消息">
<i class="fas fa-bell" aria-hidden="true"></i><span class="dash-message-label">我的消息</span><span class="dash-message-badge" id="dash-message-badge" hidden aria-hidden="true">0</span>
</button>
<span class="text-sm text-gray-500" id="dashUserName">Hi, 客户</span>
</div>
</div>
</header>
<div class="flex flex-1 min-h-0">
<main class="flex-1 px-4 sm:px-5 lg:px-6 py-5 min-w-0">
<!-- 视图1:选择业务 -->
<div id="dashViewHome" class="dash-view">
<div class="mb-6">
<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-4">
<div class="dash-card p-6" 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-6" 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-6" 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">
<section class="sea-business-page" id="sea-procurement-page" data-business="Procurement">
<div class="sea-page-header"><div><h1>海外采购</h1><p>查询并管理海外采购头程订单</p></div><button class="btn-primary text-white font-bold px-6 py-3 rounded-xl" type="button" onclick="openSeaBusiness('Procurement','new',this)">新增下单</button></div>
<div class="sea-order-filters"><input data-filter="orderNo" placeholder="订单号"><input data-filter="customer" placeholder="客户/联系人"><select data-filter="status"><option value="">全部状态</option><option>待确认</option><option>处理中</option><option>已完成</option></select><div class="sea-filter-actions"><button class="btn-primary text-white font-semibold px-5 py-2 rounded-lg" type="button" onclick="querySeaOrders('Procurement')">查询</button><button class="px-5 py-2 rounded-lg border border-gray-200 text-gray-600" type="button" onclick="resetSeaOrders('Procurement')">重置</button></div></div>
<div class="sea-order-list-card"><div class="sea-order-table-wrap"><table class="sea-order-table"><thead><tr><th>序号</th><th>订单号</th><th>采购品类</th><th>商品规格</th><th>采购数量</th><th>预算单价</th><th>状态</th><th>操作</th></tr></thead><tbody id="sea-procurement-orders-body"></tbody></table></div><div class="sea-order-pagination" data-sea-pagination="Procurement"><div class="sea-order-pagination-info"><span>共 <strong id="sea-procurement-total">0</strong> 条</span><select class="sea-order-page-size" id="sea-procurement-page-size" aria-label="每页条数" onchange="changeSeaOrderPageSize('Procurement', this.value)"><option value="10">10 条/页</option><option value="20">20 条/页</option><option value="50">50 条/页</option><option value="100">100 条/页</option></select></div><div class="sea-order-pagination-controls"><button class="page-btn" id="sea-procurement-prev" type="button" onclick="changeSeaOrderPage('Procurement', -1)">上一页</button><button class="page-btn active" id="sea-procurement-page-number" type="button" aria-current="page">1</button><button class="page-btn" id="sea-procurement-next" type="button" onclick="changeSeaOrderPage('Procurement', 1)">下一页</button></div></div></div>
</section>
<section class="sea-business-detail-page" id="sea-procurement-detail" data-business-detail="Procurement" hidden><div class="procurement-detail-header"><div class="procurement-detail-heading"><h1>海外采购订单详情</h1><p>订单号:<span data-detail-content="orderNo">-</span></p></div><button type="button" class="btn btn-default" onclick="showSeaBusinessPage('Procurement')"><i class="fas fa-arrow-left mr-2"></i>返回</button></div><div class="procurement-detail-grid"><div class="procurement-detail-card is-product"><h3>商品信息</h3><dl><div class="detail-field"><dt>采购品类</dt><dd data-detail-content="采购品类">-</dd></div><div class="detail-field"><dt>商品规格</dt><dd data-detail-content="商品规格">-</dd></div><div class="detail-field"><dt>商品链接</dt><dd data-detail-content="商品链接">-</dd></div></dl></div><div class="procurement-detail-card"><h3>订单信息</h3><dl><div class="detail-field"><dt>订单号</dt><dd data-detail-content="orderNo">-</dd></div><div class="detail-field"><dt>采购数量</dt><dd data-detail-content="采购数量">-</dd></div><div class="detail-field"><dt>预算单价</dt><dd data-detail-content="预算单价($)">-</dd></div><div class="detail-field"><dt>预算总价</dt><dd data-detail-content="预算总价">-</dd></div><div class="detail-field"><dt>订单状态</dt><dd data-detail-content="status">-</dd></div><div class="detail-field"><dt>联系人</dt><dd data-detail-content="联系人">-</dd></div><div class="detail-field"><dt>联系电话</dt><dd data-detail-content="联系电话">-</dd></div><div class="detail-field"><dt>收货地址</dt><dd data-detail-content="收货地址">-</dd></div><div class="detail-field"><dt>订单备注</dt><dd data-detail-content="订单备注">-</dd></div></dl></div><div class="procurement-detail-card"><h3>快递信息</h3><dl><div class="detail-field"><dt>国内快递公司</dt><dd data-detail-content="国内快递公司">-</dd></div><div class="detail-field"><dt>国内运单号</dt><dd data-detail-content="国内运单号">-</dd></div><div class="detail-field"><dt>海外快递公司</dt><dd data-detail-content="海外快递公司">-</dd></div><div class="detail-field"><dt>海外运单号</dt><dd data-detail-content="海外运单号">-</dd></div></dl></div></div></section>
<div class="sea-business-form-panel" data-order-panel="Procurement" hidden>
<button onclick="showSeaBusinessPage('Procurement')" 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-6 lg:p-8 w-full">
<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-4" data-order="海外采购 Overseas Sourcing" data-order-form="Procurement">
<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="showSeaBusinessPage('Procurement')" 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>
</div>
<!-- 视图3:美国本土卡下单 -->
<div id="dashViewTruck" class="dash-view hidden">
<section class="sea-business-page" id="sea-truck-page" data-business="Truck">
<div class="sea-page-header"><div><h1>美国本土卡</h1><p>查询并管理美国本土卡头程订单</p></div><button class="btn-primary text-white font-bold px-6 py-3 rounded-xl" type="button" onclick="openSeaBusiness('Truck','new',this)">新增下单</button></div>
<div class="sea-order-filters"><input data-filter="orderNo" placeholder="订单编号"><input data-filter="route" placeholder="起运地/目的地"><select data-filter="status"><option value="">全部状态</option><option>待确认</option><option>处理中</option><option>已完成</option></select><div class="sea-filter-actions"><button class="btn-primary text-white font-semibold px-5 py-2 rounded-lg" type="button" onclick="querySeaOrders('Truck')">查询</button><button class="px-5 py-2 rounded-lg border border-gray-200 text-gray-600" type="button" onclick="resetSeaOrders('Truck')">重置</button></div></div>
<div class="sea-order-list-card"><div class="sea-order-table-wrap"><table class="sea-order-table"><thead><tr><th>序号</th><th>订单编号</th><th>运输类型</th><th>起运地</th><th>目的地</th><th>柜型</th><th>总重量(kg)</th><th>预算总价</th><th>要求时间</th><th>状态</th><th>操作</th></tr></thead><tbody id="sea-truck-orders-body"></tbody></table></div><div class="sea-order-pagination" data-sea-pagination="Truck"><div class="sea-order-pagination-info"><span>共 <strong id="sea-truck-total">0</strong> 条</span><select class="sea-order-page-size" id="sea-truck-page-size" aria-label="每页条数" onchange="changeSeaOrderPageSize('Truck', this.value)"><option value="10">10 条/页</option><option value="20">20 条/页</option><option value="50">50 条/页</option><option value="100">100 条/页</option></select></div><div class="sea-order-pagination-controls"><button class="page-btn" id="sea-truck-prev" type="button" onclick="changeSeaOrderPage('Truck', -1)">上一页</button><button class="page-btn active" id="sea-truck-page-number" type="button" aria-current="page">1</button><button class="page-btn" id="sea-truck-next" type="button" onclick="changeSeaOrderPage('Truck', 1)">下一页</button></div></div></div>
</section>
<section class="sea-business-detail-page" id="sea-truck-detail" data-business-detail="Truck" hidden>
<div class="truck-detail-header">
<div class="truck-detail-heading"><h1>美国本土卡订单详情</h1><p>订单号:<span data-detail-content="orderNo">-</span></p></div>
<div class="truck-detail-actions"><button type="button" class="truck-detail-action secondary" onclick="showSeaBusinessPage('Truck')">返回列表</button></div>
</div>
<div class="truck-detail-grid">
<section class="truck-detail-card" data-detail-card="transport"><h2>运输信息</h2><dl class="truck-detail-fields"><div class="truck-detail-field"><dt>订单编号</dt><dd data-detail-content="orderNo">-</dd></div><div class="truck-detail-field"><dt>运输类型</dt><dd data-detail-content="运输类型">-</dd></div><div class="truck-detail-field"><dt>起运地</dt><dd data-detail-content="起运地">-</dd></div><div class="truck-detail-field"><dt>目的地</dt><dd data-detail-content="目的地">-</dd></div><div class="truck-detail-field"><dt>详细地址</dt><dd data-detail-content="起运详细地址">-</dd></div><div class="truck-detail-field"><dt>目的地址/码头/仓号</dt><dd data-detail-content="目的详细地址/码头/仓号">-</dd></div></dl></section>
<section class="truck-detail-card" data-detail-card="contact"><h2>联系人信息</h2><dl class="truck-detail-fields"><div class="truck-detail-field"><dt>联系人</dt><dd data-detail-content="联系人">-</dd></div><div class="truck-detail-field"><dt>联系方式</dt><dd data-detail-content="联系方式">-</dd></div><div class="truck-detail-field"><dt>承运商</dt><dd data-detail-content="carrier">待分配</dd></div></dl></section>
<section class="truck-detail-card" data-detail-card="cargo"><h2>货物信息</h2><dl class="truck-detail-fields"><div class="truck-detail-field"><dt>柜型</dt><dd data-detail-content="柜型">-</dd></div><div class="truck-detail-field"><dt>件数</dt><dd data-detail-content="件数">-</dd></div><div class="truck-detail-field"><dt>总重量</dt><dd><span data-detail-content="总重量(kg)">-</span> kg</dd></div><div class="truck-detail-field"><dt>总体积</dt><dd><span data-detail-content="总体积(m³)">-</span> m³</dd></div></dl></section>
<section class="truck-detail-card" data-detail-card="timing"><h2>时间与费用</h2><dl class="truck-detail-fields"><div class="truck-detail-field"><dt>预算总价</dt><dd data-detail-content="预算总价">-</dd></div><div class="truck-detail-field"><dt>要求时间</dt><dd data-detail-content="要求时间">-</dd></div><div class="truck-detail-field"><dt>状态</dt><dd class="is-status" data-detail-content="status">待确认</dd></div></dl></section>
</div>
</section>
<div class="sea-business-form-panel" data-order-panel="Truck" hidden>
<button onclick="showSeaBusinessPage('Truck')" 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-6 lg:p-8 w-full">
<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" data-order-form="Truck">
<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 class="max-w-xs"><label class="dash-label">预算总价</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"></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="showSeaBusinessPage('Truck')" 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>
</div>
<!-- 视图4:美本土散板下单 -->
<div id="dashViewPallet" class="dash-view hidden">
<section class="sea-business-page" id="sea-pallet-page" data-business="Pallet">
<div class="sea-page-header"><div><h1>美本土散板</h1><p>查询并管理美本土散板头程订单</p></div><button class="btn-primary text-white font-bold px-6 py-3 rounded-xl" type="button" onclick="openSeaBusiness('Pallet','new',this)">新增下单</button></div>
<div class="sea-order-filters"><input data-filter="orderNo" placeholder="订单编号"><input data-filter="route" placeholder="起运地/目的地"><select data-filter="status"><option value="">全部状态</option><option>待确认</option><option>处理中</option><option>已完成</option></select><div class="sea-filter-actions"><button class="btn-primary text-white font-semibold px-5 py-2 rounded-lg" type="button" onclick="querySeaOrders('Pallet')">查询</button><button class="px-5 py-2 rounded-lg border border-gray-200 text-gray-600" type="button" onclick="resetSeaOrders('Pallet')">重置</button></div></div>
<div class="sea-order-list-card"><div class="sea-order-table-wrap"><table class="sea-order-table"><thead><tr><th>序号</th><th>订单编号</th><th>运输方式</th><th>是否打盘</th><th>运输类型</th><th>起运地</th><th>目的地</th><th>总重量(kg)</th><th>预算总价</th><th>要求时间</th><th>状态</th><th>操作</th></tr></thead><tbody id="sea-pallet-orders-body"></tbody></table></div><div class="sea-order-pagination" data-sea-pagination="Pallet"><div class="sea-order-pagination-info"><span>共 <strong id="sea-pallet-total">0</strong> 条</span><select class="sea-order-page-size" id="sea-pallet-page-size" aria-label="每页条数" onchange="changeSeaOrderPageSize('Pallet', this.value)"><option value="10">10 条/页</option><option value="20">20 条/页</option><option value="50">50 条/页</option><option value="100">100 条/页</option></select></div><div class="sea-order-pagination-controls"><button class="page-btn" id="sea-pallet-prev" type="button" onclick="changeSeaOrderPage('Pallet', -1)">上一页</button><button class="page-btn active" id="sea-pallet-page-number" type="button" aria-current="page">1</button><button class="page-btn" id="sea-pallet-next" type="button" onclick="changeSeaOrderPage('Pallet', 1)">下一页</button></div></div></div>
</section>
<section class="sea-business-detail-page" id="sea-pallet-detail" data-business-detail="Pallet" hidden>
<nav class="pallet-detail-breadcrumb" aria-label="面包屑"><button type="button" onclick="dashNavigate('Home')">首页</button><span aria-hidden="true">&gt;</span><button type="button" onclick="showSeaBusinessPage('Pallet')">海外业务</button><span aria-hidden="true">&gt;</span><span>美本土散板</span><span aria-hidden="true">&gt;</span><strong>美本土散板订单详情</strong></nav>
<div class="pallet-detail-header">
<div class="pallet-detail-heading"><h1>美本土散板订单详情</h1><p>订单号:<span data-detail-content="orderNo">-</span></p></div>
<div class="pallet-detail-actions"><button type="button" class="pallet-detail-action secondary" onclick="showSeaBusinessPage('Pallet')">返回列表</button></div>
</div>
<div class="pallet-detail-grid">
<section class="pallet-detail-card" data-detail-card="transport"><h2>运输信息</h2><dl class="pallet-detail-fields"><div class="pallet-detail-field"><dt>订单编号</dt><dd data-detail-content="orderNo">-</dd></div><div class="pallet-detail-field"><dt>运输方式</dt><dd data-detail-content="运输方式">-</dd></div><div class="pallet-detail-field"><dt>运输类型</dt><dd data-detail-content="运输类型">-</dd></div><div class="pallet-detail-field"><dt>是否打盘</dt><dd data-detail-content="是否打盘">-</dd></div><div class="pallet-detail-field"><dt>起运地</dt><dd data-detail-content="起运地">-</dd></div><div class="pallet-detail-field"><dt>目的地</dt><dd data-detail-content="目的地">-</dd></div></dl></section>
<section class="pallet-detail-card" data-detail-card="contact"><h2>联系人信息</h2><dl class="pallet-detail-fields"><div class="pallet-detail-field"><dt>联系人</dt><dd data-detail-content="联系人">-</dd></div><div class="pallet-detail-field"><dt>联系方式</dt><dd data-detail-content="联系方式">-</dd></div></dl></section>
<section class="pallet-detail-card" data-detail-card="cargo"><h2>货物信息</h2><dl class="pallet-detail-fields"><div class="pallet-detail-field"><dt>总重量</dt><dd><span data-detail-content="总重量(kg)">-</span> kg</dd></div></dl></section>
<section class="pallet-detail-card" data-detail-card="timing"><h2>时间与费用</h2><dl class="pallet-detail-fields"><div class="pallet-detail-field"><dt>预算总价</dt><dd data-detail-content="预算总价">-</dd></div><div class="pallet-detail-field"><dt>要求时间</dt><dd data-detail-content="要求时间">-</dd></div><div class="pallet-detail-field"><dt>状态</dt><dd class="is-status" data-detail-content="status">待确认</dd></div></dl></section>
</div>
</section>
<div class="sea-business-form-panel" data-order-panel="Pallet" hidden>
<button onclick="showSeaBusinessPage('Pallet')" 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-6 lg:p-8 w-full">
<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" data-order-form="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 class="max-w-xs"><label class="dash-label">预算总价</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"></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="showSeaBusinessPage('Pallet')" 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>
</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>
<!-- 视图6:柏悦客户端全部页面(已并入,统一作用域 .client-shell) -->
<div id="dashViewClient" class="dash-view hidden client-shell">
<div class="page-content active" id="page-order">
<div class="page-header">
<div>
<div class="page-title">头程下单</div>
<div class="page-subtitle">填写货物信息,创建新的头程运输订单</div>
</div>
</div>
<div class="first-leg-address-grid">
<section class="card first-leg-module" id="first-leg-origin">
<div class="card-header"><div class="card-title">发货地</div></div>
<div class="card-body">
<div class="form-group">
<div class="form-label"><span class="required">*</span>取货方式</div>
<div class="form-content">
<div class="dest-type-tabs" id="pickup-tabs">
<button class="dest-tab active" type="button" aria-pressed="true" onclick="setPickupTab(this,'pickup')">我司提货(有取货费)</button>
<button class="dest-tab" type="button" aria-pressed="false" onclick="setPickupTab(this,'truck')">自送货</button>
</div>
</div>
</div>
<!-- 我司提货 -->
<div class="dest-content active" id="pickup-pickup">
<div class="pickup-time-address-row">
<div class="form-group">
<div class="form-label"><span class="required">*</span>提货时间</div>
<div class="form-content">
<input type="datetime-local" class="form-input" id="pickup-time" name="pickupTime" placeholder="请选择提货时间" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">选择地址</div>
<div class="form-content">
<select class="form-select" id="pickup-address-select" name="pickupAddressId" onchange="applyPickupAddress(this.value)" style="min-width:280px;">
<option value="">请选择我的地址</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<div class="form-label">联系人</div>
<div class="form-content">
<input type="text" class="form-input" id="pickup-contact" name="pickupContact" placeholder="请输入联系人姓名" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">联系电话</div>
<div class="form-content">
<input type="tel" class="form-input" id="pickup-phone" name="pickupPhone" placeholder="请输入联系电话" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>提货地址</div>
<div class="form-content">
<input type="text" class="form-input" id="pickup-address-detail" name="pickupAddress" placeholder="请输入提货详细地址" style="min-width:400px;">
</div>
</div>
</div>
<!-- 自送货(卡车信息) -->
<div class="dest-content" id="pickup-truck">
<div class="form-group">
<div class="form-label"><span class="required">*</span>预计到达时间</div>
<div class="form-content">
<input type="datetime-local" class="form-input" placeholder="请选择预计到达时间" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">车牌号</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="请输入车牌号" style="min-width:200px;">
</div>
</div>
<div class="pickup-driver-row">
<div class="form-group">
<div class="form-label">司机姓名</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="请输入司机姓名">
</div>
</div>
<div class="form-group">
<div class="form-label">司机电话</div>
<div class="form-content">
<input type="tel" class="form-input" placeholder="请输入司机电话">
</div>
</div>
</div>
<div class="form-group">
<div class="form-label">快递单号</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="请输入快递单号">
</div>
</div>
</div>
</div>
</section>
<section class="card first-leg-module" id="first-leg-destination">
<div class="card-header"><div class="card-title">目的仓</div></div>
<div class="card-body">
<div class="form-group">
<div class="form-label">目的仓类型</div>
<div class="form-content">
<div class="dest-type-tabs" id="dest-tabs">
<button class="dest-tab active" type="button" aria-pressed="true" onclick="setDestTab(this,'fba')">FBA仓</button>
<button class="dest-tab" type="button" aria-pressed="false" onclick="setDestTab(this,'overseas')">海外仓</button>
<button class="dest-tab" type="button" aria-pressed="false" onclick="setDestTab(this,'private')">私人仓</button>
<button class="dest-tab" type="button" aria-pressed="false" onclick="setDestTab(this,'fulfillment')">一件代发</button>
</div>
</div>
</div>
<!-- FBA仓 -->
<div class="dest-content active" id="dest-fba">
<div class="form-group"><div class="form-label"><span class="required">*</span>FBA仓编码</div><div class="form-content"><div class="input-wrap"><input type="text" class="form-input" placeholder="请输入FBA仓编码,如 ONT8、LAX9" style="min-width:280px;padding-right:48px;"><span style="position:absolute;right:10px;font-size:11px;color:#bfbfbf;">搜索</span></div></div></div>
<div class="form-group"><div class="form-label">收件人姓名</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件人姓名" style="min-width:280px;"></div></div>
<div class="form-group"><div class="form-label">收件人电话</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件人电话" style="min-width:280px;"></div></div>
<div class="form-group"><div class="form-label">收件地址</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件地址" style="min-width:400px;"></div></div>
</div>
<!-- 海外仓 -->
<div class="dest-content" id="dest-overseas">
<div class="form-group"><div class="form-label"><span class="required">*</span>海外仓</div><div class="form-content"><div class="select-wrap"><select class="form-select" style="min-width:280px;"><option value="">请选择海外仓</option><option>美西仓 - 洛杉矶</option><option>美东仓 - 纽瓦克</option><option>美中仓 - 芝加哥</option><option>美南仓 - 达拉斯</option><option>萨凡纳仓</option></select></div></div></div>
<div class="first-leg-destination-pair first-leg-contact-row">
<div class="form-group"><div class="form-label"><span class="required">*</span>收件人姓名</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件人姓名"></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>收件人电话</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件人电话"></div></div>
</div>
<div class="first-leg-destination-pair first-leg-country-postal-row">
<div class="form-group"><div class="form-label"><span class="required">*</span>收件国家</div><div class="form-content"><div class="select-wrap"><select class="form-select"><option value="">请选择国家</option><option>美国</option><option>加拿大</option><option>墨西哥</option></select></div></div></div>
<div class="form-group"><div class="form-label">邮编</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入邮编"></div></div>
</div>
<div class="form-group"><div class="form-label"><span class="required">*</span>收件地址</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件详细地址" style="min-width:400px;"></div></div>
</div>
<!-- 私人仓 -->
<div class="dest-content" id="dest-private">
<div class="first-leg-destination-pair first-leg-contact-row">
<div class="form-group"><div class="form-label"><span class="required">*</span>收件人姓名</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件人姓名"></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>收件人电话</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件人电话"></div></div>
</div>
<div class="first-leg-destination-pair first-leg-country-postal-row">
<div class="form-group"><div class="form-label"><span class="required">*</span>收件国家</div><div class="form-content"><div class="select-wrap"><select class="form-select"><option value="">请选择国家</option><option>美国</option><option>加拿大</option><option>墨西哥</option></select></div></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>邮编</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入邮编"></div></div>
</div>
<div class="first-leg-private-location-row">
<div class="form-group"><div class="form-label"><span class="required">*</span>州/省</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入州/省"></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>城市</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入城市"></div></div>
</div>
<div class="form-group"><div class="form-label"><span class="required">*</span>收件地址</div><div class="form-content"><input type="text" class="form-input" placeholder="请输入收件详细地址" style="min-width:400px;"></div></div>
</div>
<!-- 一件代发 -->
<div class="dest-content" id="dest-fulfillment">
<div class="form-group"><div class="form-label"><span class="required">*</span>LX入库单号</div><div class="form-content"><input type="text" class="form-input" id="fulfillment-lx-inbound-no" name="lxInboundNo" placeholder="请输入LX入库单号" required aria-required="true" style="min-width:280px;"></div></div>
</div>
</div>
</section>
</div>
<section class="card first-leg-module" id="first-leg-products">
<div class="card-header">
<div class="card-title">商品信息</div>
<div class="first-leg-header-actions">
<label class="upload-link"><input type="file" accept=".xlsx,.xls" hidden onchange="alert('Excel已选择:'+this.files[0]?.name)"><i class="fas fa-file-arrow-up"></i>上传 Excel</label>
<a class="download-link" href="#"><i class="fas fa-file-arrow-down"></i>下载模板</a>
<button class="btn-flat" type="button" onclick="addProductRow()"><i class="fas fa-plus"></i>添加商品</button>
<button class="btn-flat btn-flat-red" type="button" onclick="deleteSelectedProductRows()"><i class="fas fa-trash-can"></i>删除所选商品</button>
</div>
</div>
<div class="card-body">
<div class="table-wrap first-leg-table-wrap">
<table class="goods-table" id="product-table">
<thead><tr>
<th class="col-checkbox"><input type="checkbox" onchange="toggleAllProductRows(this)"></th><th>#</th>
<th><span class="required" aria-hidden="true">*</span>SKU NO.</th><th>中文品名</th><th>图片</th><th>英文品名</th><th>货件编号</th><th>跟踪编号</th><th>UPC NO.</th><th>报关 HS CODE</th><th>清关 HS CODE</th>
<th>清关申报单价 USD</th><th>清关申报金额 USD</th><th>材质</th><th>用途</th><th>型号</th><th>品牌</th>
<th>产品规格 长 x 宽 x 高</th><th>备注</th>
</tr></thead>
<tbody id="product-tbody"><tr class="empty-row" id="product-empty-row"><td colspan="19"><div class="empty-icon"><i class="fas fa-box-open"></i></div><div>暂无商品信息,请添加商品或上传 Excel</div></td></tr></tbody>
</table>
</div>
<div class="first-leg-hints"><span>FBA 货物的 SKU NO.、UPC NO. 为必填项</span><span>亚马逊货物的货件编号和跟踪编号为必填项</span></div>
</div>
</section>
<section class="card first-leg-module" id="first-leg-boxes">
<div class="card-header">
<div class="card-title">箱子信息</div>
<div class="first-leg-header-actions">
<button class="btn-flat" type="button" onclick="addBoxRow()"><i class="fas fa-plus"></i>添加箱子</button>
<button class="btn-flat btn-flat-red" type="button" onclick="deleteSelectedBoxRows()"><i class="fas fa-trash-can"></i>删除所选箱子</button>
</div>
</div>
<div class="card-body">
<div class="table-wrap first-leg-table-wrap">
<table class="goods-table" id="box-table">
<thead><tr>
<th class="col-checkbox"><input type="checkbox" onchange="toggleAllBoxRows(this)"></th><th>#</th><th>关联 SKU</th>
<th>箱规长 cm</th><th>箱规宽 cm</th><th>箱规高 cm</th><th>每箱数量</th><th><span class="required" aria-hidden="true">*</span>箱数</th><th>总数量</th><th>总毛重</th><th>总净重</th><th>总体积</th>
</tr></thead>
<tbody id="box-tbody"><tr class="empty-row" id="box-empty-row"><td colspan="12"><div class="empty-icon"><i class="fas fa-boxes-stacked"></i></div><div>暂无箱子信息,请先添加商品后再添加箱子</div></td></tr></tbody>
</table>
</div>
</div>
</section>
<section class="card first-leg-module" id="first-leg-requirements">
<div class="card-header"><div class="card-title">订单要求</div></div>
<div class="card-body">
<div class="first-leg-requirements-grid">
<div class="form-group"><div class="form-label"><span class="required">*</span>收货仓</div><div class="form-content"><div class="select-wrap"><select name="warehouse" class="form-select" aria-label="收货仓" required><option value="">请选择收货仓</option><option>义乌仓</option><option>深圳仓</option><option>青岛仓</option><option>上海仓</option><option>宁波仓</option><option>厦门仓</option></select></div></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>渠道</div><div class="form-content"><div class="select-wrap"><select name="channel" class="form-select" aria-label="渠道" required><option value="">请选择渠道</option><option>美线普货专线</option><option>美线敏感货专线</option><option>美线快线</option><option>美线FBA直送</option></select></div></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>买单/退税</div><div class="form-content"><div class="select-wrap"><select name="tax" class="form-select" aria-label="买单或退税" required><option value="">请选择</option><option>买单</option><option>退税</option></select></div></div></div>
<div class="form-group"><div class="form-label">是否整柜</div><div class="form-content"><div class="select-wrap"><select name="fullContainer" class="form-select" aria-label="是否整柜"><option value="">请选择</option><option value="是">是</option><option value="否">否</option></select></div></div></div>
<div class="form-group"><div class="form-label">是否商检</div><div class="form-content"><div class="select-wrap"><select name="inspection" class="form-select" aria-label="是否商检"><option value="">请选择</option><option value="是">是</option><option value="否">否</option></select></div></div></div>
<div class="form-group"><div class="form-label">船期</div><div class="form-content"><div class="date-range"><div class="input-wrap"><input type="date" class="form-input" placeholder="开船时间"></div><div class="input-wrap"><input type="date" class="form-input" placeholder="到港时间"></div></div></div></div>
<div class="form-group"><div class="form-label">期望到达时间</div><div class="form-content"><input type="date" class="form-input" placeholder="请选择期望到达时间"></div></div>
<div class="form-group"><div class="form-label">备注</div><div class="form-content"><input type="text" class="form-input" placeholder="请填写订单备注信息(选填)"></div></div>
</div>
</div>
</section>
<section class="card first-leg-module cost-estimate" id="first-leg-cost">
<div class="card-header"><div class="card-title">费用预估</div></div>
<div class="card-body">
<div class="first-leg-cost-overview">
<div class="first-leg-summary" aria-live="polite">
<div><span>商品数</span><strong id="first-leg-product-count">0</strong></div>
<div><span>箱数</span><strong id="first-leg-box-count">0</strong></div>
<div><span>总重量</span><strong><b id="first-leg-total-weight">0.00</b> kg</strong></div>
<div><span>总体积</span><strong><b id="first-leg-total-volume">0.000</b> m³</strong></div>
</div>
<div class="first-leg-quote" aria-label="预估费用">
<span class="first-leg-quote-label">预估费用</span>
<strong class="first-leg-quote-price"><span>¥</span><b>2,990</b></strong>
<span class="first-leg-quote-note">最终费用以实际计费结果为准</span>
</div>
</div>
</div>
</section>
<div class="form-footer">
<div class="footer-tips"><span class="tip-icon">⚠️</span> 提交前请确认货物信息准确无误,提交后不可修改货物明细</div>
<div style="display:flex;gap:12px;align-items:center;justify-content:center;">
<button class="btn btn-default" type="button" onclick="resetFirstLegOrder()">重置</button>
<button class="btn-submit" onclick="submitOrder()">✈️ 提交订单</button>
</div>
</div>
</div>
<!-- ========== PAGE 2: 订单列表 ========== -->
<div class="page-content" id="page-inbound">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">订单列表</span></div>
<div class="page-header-actions">
<div class="page-title">订单列表</div>
<div class="page-action-group">
<button type="button" class="btn btn-primary" onclick="dashNavigate('order')"><i class="fas fa-plus"></i> 创建订单</button>
</div>
</div>
<div class="card">
<div class="card-body">
<div class="filter-grid">
<div class="filter-item"><span class="filter-label">订单号</span><input type="text" class="filter-input" id="inbound-filter-order-no" placeholder="请输入订单号"></div>
<div class="filter-item"><span class="filter-label">收货仓</span><select class="filter-select" id="inbound-filter-warehouse"><option value="">全部</option><option>义乌仓</option><option>深圳仓</option><option>青岛仓</option><option>上海仓</option><option>宁波仓</option><option>厦门仓</option></select></div>
<div class="filter-item"><span class="filter-label">渠道名称</span><select class="filter-select" id="inbound-filter-channel"><option value="">全部</option><option>美线普货专线</option><option>美线敏感货专线</option><option>美线快线</option><option>美线FBA直送</option></select></div>
<div class="filter-item"><span class="filter-label">客户姓名/手机号</span><input type="text" class="filter-input" id="inbound-filter-customer" placeholder="请输入"></div>
<div class="filter-item span2"><span class="filter-label">创建时间</span><div class="date-range-wrap"><input type="date" class="filter-input" id="inbound-filter-created-start"><span class="date-sep">~</span><input type="date" class="filter-input" id="inbound-filter-created-end"></div></div>
<div class="filter-item"><span class="filter-label">付款状态</span><select class="filter-select" id="inbound-filter-payment"><option value="">全部</option><option>已付款</option><option>未付款</option><option>部分付款</option></select></div>
<div class="filter-item"><span class="filter-label">出货状态</span><select class="filter-select" id="inbound-filter-shipping"><option value="">全部</option><option>已出货</option><option>未出货</option><option>出货中</option></select></div>
<div class="filter-item"><span class="filter-label">操作状态</span><select class="filter-select" id="inbound-filter-status"><option value="">全部</option><option>待处理</option><option>待确认</option><option>处理中</option><option>已确认</option><option>已完成</option><option>已取消</option></select></div>
<div class="filter-item inbound-destination-filter"><span class="filter-label">目的仓类型</span><select class="filter-select" id="inbound-filter-destination-type"><option value="">全部</option><option>FBA仓</option><option>海外仓</option><option>私人仓</option></select><button type="button" class="btn-flat btn-flat-blue inbound-filter-query" onclick="filterInboundOrders()"><i class="fas fa-magnifying-glass"></i>查询</button></div>
</div>
<div class="filter-actions">
<button class="btn-flat btn-flat-blue"><svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>下载唛头</button>
<button type="button" class="btn-flat btn-flat-blue" onclick="openInboundBatchOrder()"><i class="fas fa-file-circle-plus"></i>批量下单</button>
<button type="button" class="btn-flat btn-flat-blue" onclick="resetInboundOrders()" hidden><svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>重置查询条件</button>
<button class="btn-flat btn-flat-blue" hidden><svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>重置所有状态</button>
</div>
</div>
</div>
<div class="card">
<div class="table-wrap-simple">
<table class="data-table" id="inbound-table">
<thead>
<tr>
<th>序号</th><th class="col-checkbox"><input type="checkbox" onchange="toggleAllTable('inbound-table', this)"></th><th>操作</th><th>入仓码</th><th>订单状态</th><th>渠道</th><th>目的仓</th><th>箱数</th><th>快递公司</th><th>快递单号</th><th>SKUS</th><th>总毛重</th><th>总净重</th><th>总体积</th><th>总数量</th><th>计费单价</th><th>计费体积</th><th>计费重量</th><th>备注</th><th>货件编号</th><th>跟踪编号</th><th>国内仓入库时间</th><th>业务日期</th>
</tr>
</thead>
<tbody id="inbound-table-body">
<tr class="empty-state"><td colspan="23"><div class="empty-icon">📦</div><div class="empty-text">暂无数据</div></td></tr>
</tbody>
</table>
</div>
<div class="pagination-bar">
<div class="pagination-info"><span>共 <strong id="inbound-total">0</strong> 条</span><select class="page-size-select"><option>10 条/页</option><option>20 条/页</option><option>50 条/页</option><option>100 条/页</option></select></div>
<div class="pagination-ctrl"><button class="page-btn" disabled>上一页</button><button class="page-btn active">1</button><button class="page-btn" disabled>下一页</button></div>
</div>
</div>
</div>
<!-- ========== PAGE: 头程订单详情 ========== -->
<div class="page-content" id="page-inbound-detail">
<div class="breadcrumb"><a href="#" onclick="showInboundList();return false;">订单列表</a><span class="sep">/</span><span class="current">订单详情</span></div>
<div class="page-header-actions"><div><div class="page-title" style="margin-bottom:4px;">头程订单详情</div><div class="inbound-detail-subtitle">订单号:<strong id="inbound-detail-order-no">-</strong></div></div><button type="button" class="btn btn-default" onclick="showInboundList()"><i class="fas fa-arrow-left"></i> 返回列表</button></div>
<section class="card inbound-detail-section"><div class="card-body"><div class="inbound-detail-title">订单节点</div><div class="inbound-node-scroll"><div class="inbound-node-track" id="inbound-detail-nodes"><div class="inbound-node is-done"><span>✓</span><b>待接收审核</b><small id="inbound-node-created">2026-08-27 09:20</small></div><div class="inbound-node is-done"><span>✓</span><b>审核通过</b><small id="inbound-node-approved">2026-08-27 10:53:57</small></div><div class="inbound-node is-done"><span>✓</span><b>待发送数据</b><small id="inbound-node-shipping-data">2026-08-27 21:01:19</small></div><div class="inbound-node is-current"><span>✓</span><b>待确认</b><small id="inbound-node-confirm">2026-08-27 21:01:24</small></div><div class="inbound-node"><span>5</span><b>入库完成</b><small id="inbound-node-received">-</small></div><div class="inbound-node"><span>6</span><b>装柜完成</b><small id="inbound-node-loaded">-</small></div></div></div></div></section>
<section class="card inbound-detail-tabs-shell" aria-label="订单详情标签页"><div class="inbound-detail-tabs" role="tablist" aria-label="订单详情"><button type="button" class="inbound-detail-tab active" role="tab" aria-selected="true" aria-controls="inbound-panel-order" data-tab="order" onclick="switchInboundDetailTab('order')">订单信息</button><button type="button" class="inbound-detail-tab" role="tab" aria-selected="false" aria-controls="inbound-panel-product" data-tab="product" onclick="switchInboundDetailTab('product')">货品信息</button><button type="button" class="inbound-detail-tab" role="tab" aria-selected="false" aria-controls="inbound-panel-measure" data-tab="measure" onclick="switchInboundDetailTab('measure')">测量数据</button><button type="button" class="inbound-detail-tab" role="tab" aria-selected="false" aria-controls="inbound-panel-fees" data-tab="fees" onclick="switchInboundDetailTab('fees')">费用明细</button><button type="button" class="inbound-detail-tab" role="tab" aria-selected="false" aria-controls="inbound-panel-customs" data-tab="customs" onclick="switchInboundDetailTab('customs')">报关资料</button><button type="button" class="inbound-detail-tab" role="tab" aria-selected="false" aria-controls="inbound-panel-container" data-tab="container" onclick="switchInboundDetailTab('container')">货柜节点</button></div></section>
<div class="inbound-detail-panels">
<div class="inbound-detail-panel active" id="inbound-panel-order" role="tabpanel"><section class="card inbound-detail-section"><div class="card-body"><div class="inbound-detail-title">订单信息</div><div class="inbound-order-info-columns" id="inbound-detail-order-info"></div></div></section></div>
<div class="inbound-detail-panel" id="inbound-panel-product" role="tabpanel" hidden><section class="card inbound-detail-section"><div class="card-body"><div class="inbound-detail-title inbound-product-heading"><span>SKU明细</span><small class="inbound-product-count" id="inbound-detail-product-count">共 0 条</small></div><div class="inbound-product-table-wrap"><table class="inbound-product-table" aria-label="SKU明细"><thead><tr><th>SKU NO.</th><th>货件编号</th><th>跟踪编号</th><th>UPC NO.</th><th>中文品名</th><th>图片</th><th>英文品名</th><th>产品规格</th><th>每箱数量</th><th>每箱重量</th><th>每箱体积</th><th>箱数</th><th>材质</th><th>用途</th><th>型号</th><th>品牌</th><th>报关 HS CODE</th><th>清关 HS CODE</th><th>清关申报单价</th><th>清关申报金额</th><th>备注</th></tr></thead><tbody id="inbound-detail-product-rows"></tbody></table></div></div></section></div>
<div class="inbound-detail-panel" id="inbound-panel-measure" role="tabpanel" hidden><section class="card inbound-detail-section"><div class="card-body"><div class="inbound-detail-title">测量数据 <span class="inbound-push-label">后台推送</span><button type="button" class="btn btn-primary inbound-detail-confirm" id="inbound-detail-confirm-btn" onclick="openInboundMeasurementConfirm()">确认测量数据</button></div><div class="inbound-measure-summary" id="inbound-detail-measure-summary"></div><div class="inbound-measure-table-wrap"><table class="inbound-measure-table"><thead><tr><th>#</th><th>箱规长(cm)</th><th>箱规宽(cm)</th><th>箱规高(cm)</th><th>箱数</th><th>每箱毛重</th><th>每箱体积</th><th>箱规周长</th><th>总毛重</th><th>总体积</th><th>总材积</th><th>备注</th></tr></thead><tbody id="inbound-detail-measure-rows"></tbody></table></div></div></section></div>
<div class="inbound-detail-panel" id="inbound-panel-fees" role="tabpanel" hidden><section class="card inbound-detail-section"><div class="card-body"><div class="inbound-detail-title">费用明细</div><div class="inbound-fee-list" id="inbound-detail-fees"></div></div></section></div>
<div class="inbound-detail-panel" id="inbound-panel-customs" role="tabpanel" hidden><section class="card inbound-detail-section"><div class="card-body"><div class="inbound-detail-title">报关资料</div><div class="inbound-customs-toolbar"><div class="inbound-customs-hint">上传商业发票、装箱单等报关资料,支持 PDF、Excel、Word、图片,单个文件不超过 20MB。</div><div class="inbound-customs-actions"><label class="inbound-customs-upload"><i class="fas fa-file-arrow-up"></i>上传报关资料<input type="file" id="inbound-customs-file" hidden multiple accept=".pdf,.xlsx,.xls,.doc,.docx,.jpg,.jpeg,.png" onchange="handleInboundCustomsUpload(this)"></label><button type="button" class="inbound-customs-download" onclick="downloadInboundTaxDocs()"><i class="fas fa-file-arrow-down"></i>下载报关退税资料</button></div></div><div class="inbound-customs-files" id="inbound-customs-files"></div></div></section></div>
<div class="inbound-detail-panel" id="inbound-panel-container" role="tabpanel" hidden><section class="card inbound-detail-section"><div class="card-body"><div class="inbound-detail-title">货柜节点</div><div class="inbound-container-node-list" id="inbound-detail-container-nodes"></div></div></section></div>
</div>
</div>
<!-- ========== PAGE 3: 派送订单列表 ========== -->
<div class="page-content" id="page-delivery">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">派送订单列表</span></div>
<div class="page-title" style="margin-bottom:16px;">派送订单列表</div>
<div class="card">
<div class="card-body">
<div class="filter-grid">
<div class="filter-item"><span class="filter-label">订单号</span><input type="text" class="filter-input" placeholder="请输入订单号"></div>
<div class="filter-item"><span class="filter-label">派送仓</span><select class="filter-select"><option value="">全部</option><option>洛杉矶仓</option><option>纽约仓</option><option>芝加哥仓</option><option>达拉斯仓</option><option>萨凡纳仓</option></select></div>
<div class="filter-item"><span class="filter-label">渠道名称</span><select class="filter-select"><option value="">全部</option><option>美线普货专线</option><option>美线敏感货专线</option><option>美线快线</option><option>美线FBA直送</option></select></div>
<div class="filter-item"><span class="filter-label">客户姓名/手机号</span><input type="text" class="filter-input" placeholder="请输入"></div>
<div class="filter-item span2"><span class="filter-label">创建时间</span><div class="date-range-wrap"><input type="date" class="filter-input"><span class="date-sep">~</span><input type="date" class="filter-input"></div></div>
<div class="filter-item"><span class="filter-label">付款状态</span><select class="filter-select"><option value="">全部</option><option>已付款</option><option>未付款</option><option>部分付款</option></select></div>
<div class="filter-item"><span class="filter-label">派送状态</span><select class="filter-select"><option value="">全部</option><option>待派送</option><option>派送中</option><option>已派送</option><option>派送失败</option></select></div>
<div class="filter-item"><span class="filter-label">操作状态</span><select class="filter-select"><option value="">全部</option><option>待处理</option><option>处理中</option><option>已完成</option><option>已取消</option></select></div>
<div class="filter-item"><span class="filter-label">目的仓类型</span><select class="filter-select"><option value="">全部</option><option>FBA仓</option><option>海外仓</option><option>私人仓</option></select></div>
</div>
<div class="filter-actions">
<button class="btn-flat btn-flat-blue"><svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>查询</button>
<button class="btn-flat"><svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>重置</button>
</div>
</div>
</div>
<div class="card">
<div class="table-wrap-simple">
<table class="data-table" id="delivery-table">
<thead>
<tr>
<th>序号</th><th class="col-checkbox"><input type="checkbox" onchange="toggleAllTable('delivery-table', this)"></th><th>单号</th><th>件数</th><th>重量(kg)</th><th>入方编码</th><th>渠道名</th><th>目的仓类型</th><th>客户</th><th>收货公司</th><th>规格型号</th><th>派送状态</th><th>总数量</th><th>总毛重</th><th>总净重</th><th>总体积</th><th>清关申报单价</th><th>清关申报金额</th><th>材质</th><th>用途</th><th>品牌</th><th>创建时间</th><th>跟踪编号</th><th>国际单号</th><th>操作人</th><th>备注</th>
</tr>
</thead>
<tbody>
<tr class="empty-state"><td colspan="26"><div class="empty-icon">📦</div><div class="empty-text">暂无数据</div></td></tr>
</tbody>
</table>
</div>
<div class="pagination-bar">
<div class="pagination-info"><span>共 <strong>0</strong> 条</span><select class="page-size-select"><option>10 条/页</option><option>20 条/页</option><option>50 条/页</option><option>100 条/页</option></select></div>
<div class="pagination-ctrl"><button class="page-btn" disabled>上一页</button><button class="page-btn active">1</button><button class="page-btn" disabled>下一页</button></div>
</div>
</div>
</div>
<!-- ========== PAGE: 异常订单列表 ========== -->
<div class="page-content" id="page-exception-orders">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">异常订单</span></div>
<div class="page-title" style="margin-bottom:16px;">异常订单</div>
<div class="card">
<div class="card-body">
<div class="filter-grid exception-order-filter-grid">
<div class="filter-item span2"><span class="filter-label">关键词</span><input id="client-exception-keyword" type="search" class="filter-input" placeholder="异常订单号 / 入仓码 / 客户 / 原因" autocomplete="off"></div>
<div class="filter-item"><span class="filter-label">异常类型</span><select id="client-exception-type" class="filter-select"><option value="">全部</option><option value="退货">退货</option><option value="拒收">拒收</option></select></div>
<div class="filter-item"><span class="filter-label">处理状态</span><select id="client-exception-status" class="filter-select"><option value="">全部</option><option value="待处理">待处理</option><option value="处理中">处理中</option><option value="已完成">已完成</option></select></div>
</div>
<div class="filter-actions">
<button type="button" class="btn-flat btn-flat-blue" onclick="queryClientExceptionOrders()"><svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>查询</button>
<button type="button" class="btn-flat" onclick="resetClientExceptionOrders()"><svg class="icon-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>重置</button>
</div>
</div>
</div>
<div class="card">
<div class="table-wrap-simple client-exception-table-wrap">
<table class="data-table client-exception-table" id="client-exception-orders-table">
<thead><tr><th>序号</th><th>异常订单号</th><th>入仓码</th><th>异常类型</th><th>客户</th><th>仓库</th><th>异常原因</th><th>异常时间</th><th>处理状态</th><th>处理动作</th><th>退回物流单号</th><th>处理人</th><th>更新时间</th><th>操作</th></tr></thead>
<tbody id="client-exception-orders-body"></tbody>
</table>
</div>
<div class="pagination-bar"><div class="pagination-info">共 <strong id="client-exception-total">0</strong> 条</div></div>
</div>
</div>
<!-- ========== PAGE: 异常订单详情 ========== -->
<div class="page-content" id="page-exception-order-detail">
<div class="breadcrumb"><span>首页</span><span class="sep">/</span><span>异常订单</span><span class="sep">/</span><span class="current">详情</span></div>
<div class="page-header-actions"><div class="page-title">异常订单详情</div><button type="button" class="btn btn-default" onclick="dashNavigate('exception-orders')"><i class="fas fa-arrow-left mr-2"></i>返回列表</button></div>
<div class="card">
<div class="client-exception-detail-grid">
<section class="client-exception-detail-section">
<h2 class="client-exception-detail-title">基础信息</h2>
<div class="client-exception-detail-fields">
<div class="client-exception-detail-field"><div class="client-exception-detail-label">异常订单号</div><div class="client-exception-detail-value" data-client-exception-detail="exceptionNo">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">异常类型</div><div class="client-exception-detail-value" data-client-exception-detail="abnormalType">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">处理状态</div><div class="client-exception-detail-value" data-client-exception-detail="abnormalStatus">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">客户</div><div class="client-exception-detail-value" data-client-exception-detail="customer">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">仓库</div><div class="client-exception-detail-value" data-client-exception-detail="warehouse">—</div></div>
</div>
</section>
<section class="client-exception-detail-section">
<h2 class="client-exception-detail-title">关联订单</h2>
<div class="client-exception-detail-fields">
<div class="client-exception-detail-field"><div class="client-exception-detail-label">尾程订单号</div><div class="client-exception-detail-value" data-client-exception-detail="lastMileNo">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">派送单号</div><div class="client-exception-detail-value" data-client-exception-detail="dispatchNo">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">入库单号</div><div class="client-exception-detail-value" data-client-exception-detail="inboundNo">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">出库单号</div><div class="client-exception-detail-value" data-client-exception-detail="outboundNo">—</div></div>
</div>
</section>
<section class="client-exception-detail-section">
<h2 class="client-exception-detail-title">异常信息</h2>
<div class="client-exception-detail-fields">
<div class="client-exception-detail-field"><div class="client-exception-detail-label">异常原因</div><div class="client-exception-detail-value is-reason" data-client-exception-detail="reason">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">异常时间</div><div class="client-exception-detail-value" data-client-exception-detail="exceptionTime">—</div></div>
</div>
</section>
<section class="client-exception-detail-section">
<h2 class="client-exception-detail-title">处理信息</h2>
<div class="client-exception-detail-fields">
<div class="client-exception-detail-field"><div class="client-exception-detail-label">处理动作</div><div class="client-exception-detail-value" data-client-exception-detail="action">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">退回物流单号</div><div class="client-exception-detail-value" data-client-exception-detail="returnTrackingNo">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">处理人</div><div class="client-exception-detail-value" data-client-exception-detail="handler">—</div></div>
<div class="client-exception-detail-field"><div class="client-exception-detail-label">更新时间</div><div class="client-exception-detail-value" data-client-exception-detail="updatedAt">—</div></div>
</div>
</section>
</div>
</div>
</div>
<!-- ========== PAGE: 贴标申请列表 ========== -->
<div class="page-content" id="page-labeling-list">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">贴标申请</span></div>
<div class="page-header-actions"><div class="page-title">贴标申请</div><div class="page-action-group"><button type="button" class="btn btn-primary" onclick="openLabelingCreate()">+ 新增贴标申请</button></div></div>
<div class="card storage-transfer-section"><div class="card-body">
<div class="storage-transfer-filter-grid">
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="labeling-filter-order-no">贴标申请单号</label><input class="storage-transfer-input" id="labeling-filter-order-no" type="text" placeholder="请输入贴标申请单号"></div>
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="labeling-filter-inbound-no">入仓码</label><input class="storage-transfer-input" id="labeling-filter-inbound-no" type="text" placeholder="请输入入仓码"></div>
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="labeling-filter-status">订单状态</label><select class="storage-transfer-select" id="labeling-filter-status"><option value="">全部状态</option><option>待处理</option><option>处理中</option><option>已完成</option></select></div>
</div>
<div class="storage-transfer-filter-actions"><button type="button" class="btn btn-primary" onclick="filterLabelingOrders()">查询</button><button type="button" class="btn btn-default" onclick="resetLabelingFilters()">重置</button></div>
</div></div>
<div class="card"><div class="table-wrap-simple"><table class="data-table" id="labeling-list-table"><thead><tr><th>序号</th><th>贴标申请单号</th><th>入仓码</th><th>目的海外仓</th><th>贴标要求</th><th>SKU 数</th><th>总箱数</th><th>状态</th><th>创建时间</th><th>操作</th></tr></thead><tbody id="labeling-list-body"><tr class="empty-state"><td colspan="10"><div class="empty-text">暂无贴标申请</div></td></tr></tbody></table></div><div class="pagination-bar"><div class="pagination-info">共 <strong id="labeling-total">0</strong> 条</div></div></div>
</div>
<!-- ========== PAGE: 贴标申请详情 ========== -->
<div class="page-content" id="page-labeling-detail">
<div class="breadcrumb"><a href="#" onclick="openLabelingList();return false;">贴标申请</a><span class="sep">/</span><span class="current">详情</span></div>
<div class="page-header-actions">
<div><div class="page-title" style="margin-bottom:4px;">换标单详情</div><div class="inbound-detail-subtitle">换标单号:<strong id="labeling-detail-order-no">-</strong></div></div>
<div class="page-action-group"><span class="labeling-detail-status" id="labeling-detail-status">待处理</span><button type="button" class="btn btn-default" onclick="openLabelingList()"><i class="fas fa-arrow-left"></i> 返回列表</button></div>
</div>
<div class="labeling-detail-grid">
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">单据信息</div><dl id="labeling-detail-document"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">商品信息</div><dl id="labeling-detail-product"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">作业要求</div><dl id="labeling-detail-work"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">费用明细</div><dl id="labeling-detail-fees"></dl></div></section>
</div>
</div>
<!-- ========== PAGE: 新增贴标申请 ========== -->
<div class="page-content" id="page-labeling">
<div class="breadcrumb"><a href="#" onclick="openLabelingList();return false;">贴标申请</a><span class="sep">/</span><span class="current">新增</span></div>
<div class="page-header-actions"><div class="page-title">新增贴标申请</div><button type="button" class="btn btn-default" onclick="openLabelingList()">返回列表</button></div>
<form id="labeling-form" onsubmit="submitLabeling(event)">
<!-- 订单信息 -->
<div class="card" style="margin-bottom:12px;">
<div class="card-body">
<div class="info-section-title" style="margin-bottom:18px;">订单信息</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px 24px;">
<div class="form-group">
<div class="form-label"><span class="required">*</span>入仓码</div>
<div class="form-content"><select class="form-input" name="inboundNo" onchange="syncLabelingWarehouse()" style="width:100%;max-width:360px;" required><option value="">请选择入仓码</option></select></div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>目的海外仓</div>
<div class="form-content"><input type="text" class="form-input" name="warehouse" placeholder="选择入仓码后自动带入" style="width:100%;max-width:360px;" readonly required></div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>贴标截止时间</div>
<div class="form-content"><input type="datetime-local" class="form-input" name="deadline" style="width:100%;max-width:360px;" required></div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>联系人</div>
<div class="form-content"><input type="text" class="form-input" name="contact" placeholder="请输入联系人" style="width:100%;max-width:360px;" required></div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>联系电话</div>
<div class="form-content"><input type="text" class="form-input" name="phone" placeholder="请输入联系电话" style="width:100%;max-width:360px;" required></div>
</div>
<div class="form-group" style="grid-column:1 / -1;">
<div class="form-label">备注</div>
<div class="form-content"><textarea class="form-textarea" name="remark" placeholder="请输入备注信息" style="max-width:760px;height:70px;"></textarea></div>
</div>
</div>
</div>
</div>
<!-- 货物明细 -->
<div class="card" style="margin-bottom:12px;">
<div class="card-body">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;">
<div class="info-section-title" style="margin:0;">货物明细</div>
<div class="labeling-line-actions">
<button type="button" class="btn btn-default" onclick="downloadLabelingRelationTemplate()" style="font-size:12px;padding:6px 16px;"><i class="fas fa-download" aria-hidden="true"></i> 关联模板下载</button>
<button type="button" class="btn btn-primary" onclick="addLabelingRow()" style="font-size:12px;padding:6px 16px;">+ 添加行</button>
<button type="button" class="btn btn-default" onclick="clearLabelingRows()" style="font-size:12px;padding:6px 16px;">清空</button>
</div>
</div>
<div class="table-wrap-simple">
<table class="data-table" id="labeling-table">
<thead>
<tr>
<th class="col-checkbox"><input type="checkbox" onchange="toggleAllTable('labeling-table', this)"></th>
<th>商品SKU<span style="color:#ff4d4f;">*</span></th>
<th>商品图片</th>
<th>中文品名<span style="color:#ff4d4f;">*</span></th>
<th>总箱数<span style="color:#ff4d4f;">*</span></th>
<th>通知移仓件数(箱数)<span style="color:#ff4d4f;">*</span></th>
<th>货件编号<span style="color:#ff4d4f;">*</span></th>
<th>追踪编码</th>
<th>换标新SKU<span style="color:#ff4d4f;">*</span></th>
<th>贴标要求</th>
<th>上传贴标文件</th>
<th>上传关联文件</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody id="labeling-tbody">
<tr class="empty-state"><td colspan="14"><div class="empty-icon">📦</div><div class="empty-text">暂无货物明细,请点击「添加行」</div></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 提交按钮 -->
<div class="form-footer">
<div class="footer-tips"><span class="tip-icon">⚠️</span> 提交后海外仓将按要求进行换标贴标,请确认货物信息准确</div>
<div style="display:flex;gap:12px;align-items:center;justify-content:center;">
<button type="button" class="btn btn-default" onclick="resetLabelingForm()">重置</button>
<button type="submit" class="btn-submit" id="labeling-submit">提交贴标申请</button>
</div>
</div>
</form>
</div>
<!-- ========== MODAL: 编辑贴标申请 ========== -->
<div class="modal-overlay" id="labeling-edit-modal" onclick="if(event.target===this)closeLabelingEdit()">
<div class="modal-box labeling-edit-modal-box" role="dialog" aria-modal="true" aria-labelledby="labeling-edit-title">
<form id="labeling-edit-form" onsubmit="submitLabelingEdit(event)">
<div class="modal-header"><div class="modal-title" id="labeling-edit-title">编辑贴标申请</div><button type="button" class="modal-close" onclick="closeLabelingEdit()" aria-label="关闭">&times;</button></div>
<div class="modal-body">
<div class="modal-section-title">订单信息</div>
<div class="labeling-edit-form-grid">
<div class="form-group"><div class="form-label"><span class="required">*</span>入仓码</div><div class="form-content"><select class="form-input" name="inboundNo" onchange="syncLabelingEditWarehouse()" required><option value="">请选择入仓码</option></select></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>目的海外仓</div><div class="form-content"><input type="text" class="form-input" name="warehouse" placeholder="选择入仓码后自动带入" readonly required></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>贴标截止时间</div><div class="form-content"><input type="datetime-local" class="form-input" name="deadline" required></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>联系人</div><div class="form-content"><input type="text" class="form-input" name="contact" placeholder="请输入联系人" required></div></div>
<div class="form-group"><div class="form-label"><span class="required">*</span>联系电话</div><div class="form-content"><input type="text" class="form-input" name="phone" placeholder="请输入联系电话" required></div></div>
<div class="form-group labeling-edit-field-wide"><div class="form-label">备注</div><div class="form-content"><textarea class="form-textarea" name="remark" placeholder="请输入备注信息" style="max-width:760px;height:70px;"></textarea></div></div>
</div>
<div class="modal-section-title" style="margin-top:24px;">货物明细</div>
<div class="labeling-edit-table-wrap"><table class="data-table labeling-edit-table" id="labeling-edit-table"><thead><tr><th>商品SKU<span style="color:#ff4d4f;">*</span></th><th>商品图片</th><th>中文品名<span style="color:#ff4d4f;">*</span></th><th>总箱数<span style="color:#ff4d4f;">*</span></th><th>通知移仓件数(箱数)<span style="color:#ff4d4f;">*</span></th><th>货件编号<span style="color:#ff4d4f;">*</span></th><th>追踪编码</th><th>换标新SKU<span style="color:#ff4d4f;">*</span></th><th>贴标要求</th><th>上传贴标文件</th><th>上传关联文件</th><th>备注</th><th>操作</th></tr></thead><tbody id="labeling-edit-tbody"></tbody></table></div>
<button type="button" class="btn btn-primary" onclick="addLabelingEditRow()" style="font-size:12px;padding:6px 16px;margin-top:12px;">+ 添加行</button>
</div>
<div class="modal-footer"><button type="button" class="btn btn-default" onclick="closeLabelingEdit()">取消</button><button type="submit" class="btn btn-primary">保存修改</button></div>
</form>
</div>
</div>
<!-- ========== PAGE: 仓储转运单列表 ========== -->
<div class="page-content" id="page-storage-transfer-list">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">仓储转运单</span></div>
<div class="page-header-actions">
<div class="page-title">仓储转运单</div>
<div class="page-action-group">
<button type="button" class="btn btn-primary" onclick="openStorageTransferCreate()">+ 新增订单</button>
</div>
</div>
<div class="card storage-transfer-section">
<div class="card-body">
<div class="storage-transfer-filter-grid">
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="storage-transfer-filter-order-no">转运单号</label><input class="storage-transfer-input" id="storage-transfer-filter-order-no" type="text" placeholder="请输入转运单号"></div>
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="storage-transfer-filter-inbound-no">入仓码</label><input class="storage-transfer-input" id="storage-transfer-filter-inbound-no" type="text" placeholder="请输入入仓码"></div>
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="storage-transfer-filter-warehouse">操作仓</label><select class="storage-transfer-select" id="storage-transfer-filter-warehouse"><option value="">全部操作仓</option><option>美西洛杉矶仓</option><option>美东新泽西仓</option><option>美中芝加哥仓</option></select></div>
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="storage-transfer-filter-fba">FBA 目的仓</label><input class="storage-transfer-input" id="storage-transfer-filter-fba" type="text" placeholder="请输入仓代码"></div>
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="storage-transfer-filter-relabel">是否换标</label><select class="storage-transfer-select" id="storage-transfer-filter-relabel"><option value="">全部</option><option value="yes">是</option><option value="no">否</option></select></div>
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="storage-transfer-filter-status">订单状态</label><select class="storage-transfer-select" id="storage-transfer-filter-status"><option value="">全部状态</option><option>待处理</option><option>处理中</option><option>已完成</option><option>已提交</option></select></div>
<div class="storage-transfer-filter-item"><span class="storage-transfer-filter-label">创建时间</span><div class="storage-transfer-date-range"><input class="storage-transfer-input" id="storage-transfer-filter-created-start" type="date" aria-label="创建开始时间"><span>至</span><input class="storage-transfer-input" id="storage-transfer-filter-created-end" type="date" aria-label="创建结束时间"></div></div>
</div>
<div class="storage-transfer-filter-actions">
<button type="button" class="btn btn-primary" onclick="filterStorageTransferOrders()">查询</button>
<button type="button" class="btn btn-default" onclick="resetStorageTransferFilters()">重置</button>
</div>
</div>
</div>
<div class="card storage-transfer-list-card">
<div class="table-wrap-simple">
<table class="data-table" id="storage-transfer-list-table">
<thead><tr><th>序号</th><th>转运单号</th><th>入仓码</th><th>发货箱数</th><th>操作仓</th><th>FBA 目的仓</th><th>FBA货件编号</th><th>派送方式</th><th>是否换标</th><th>后台尾程订单号</th><th>后台换标单号</th><th>状态</th><th>创建时间</th><th>操作</th></tr></thead>
<tbody id="storage-transfer-list-body"><tr class="empty-state"><td colspan="14"><div class="empty-text">暂无仓储转运订单</div></td></tr></tbody>
</table>
</div>
<div class="storage-transfer-pagination" id="storage-transfer-pagination">
<div class="pagination-info">共 <strong id="storage-transfer-total">0</strong> 条</div>
<div class="pagination-ctrl"><button class="page-btn" id="storage-transfer-prev" onclick="changeStorageTransferPage(-1)">上一页</button><button class="page-btn active" id="storage-transfer-page-number">1</button><button class="page-btn" id="storage-transfer-next" onclick="changeStorageTransferPage(1)">下一页</button></div>
</div>
</div>
</div>
<!-- ========== PAGE: 新增仓储转运单 ========== -->
<div class="page-content" id="page-storage-transfer-create">
<div class="breadcrumb"><a href="#" onclick="openStorageTransferList();return false;">仓储转运单</a><span class="sep">/</span><span class="current">新增订单</span></div>
<div class="page-title" style="margin-bottom:16px;">新增仓储转运单</div>
<form id="storage-transfer-form" onsubmit="submitStorageTransferOrder(event)">
<div class="card storage-transfer-section">
<div class="card-body">
<div class="storage-transfer-section-title">基础与派送信息</div>
<div class="storage-transfer-form-grid">
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-inbound-no">入仓码</label><input class="storage-transfer-input" id="storage-transfer-inbound-no" name="inboundNo" type="text"></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-warehouse">操作仓</label><select class="storage-transfer-select" id="storage-transfer-warehouse" name="warehouse"><option value="">请选择操作仓</option><option>美西洛杉矶仓</option><option>美东新泽西仓</option><option>美中芝加哥仓</option></select></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-fba-code"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA 目的仓代码</label><input class="storage-transfer-input" id="storage-transfer-fba-code" name="fbaCode" type="text" required></div>
<div class="storage-transfer-field storage-transfer-span-3"><label class="storage-transfer-field-label" for="storage-transfer-address"><span class="storage-transfer-required" aria-hidden="true">*</span>详细派送地址</label><input class="storage-transfer-input" id="storage-transfer-address" name="address" type="text" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-box-count"><span class="storage-transfer-required" aria-hidden="true">*</span>发货箱数</label><input class="storage-transfer-input" id="storage-transfer-box-count" name="boxCount" type="number" min="1" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-delivery-method"><span class="storage-transfer-required" aria-hidden="true">*</span>派送方式</label><select class="storage-transfer-select" id="storage-transfer-delivery-method" name="deliveryMethod" required><option value="">请选择派送方式</option><option>卡车派送</option><option>快递派送</option></select></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-expected-arrival"><span class="storage-transfer-required" aria-hidden="true">*</span>期望到达时间</label><input class="storage-transfer-input" id="storage-transfer-expected-arrival" name="expectedArrival" type="datetime-local" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-shipment-id"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA 货件编号</label><input class="storage-transfer-input" id="storage-transfer-shipment-id" name="shipmentId" type="text" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-tracking-id"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA 追踪编号</label><input class="storage-transfer-input" id="storage-transfer-tracking-id" name="trackingId" type="text" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-box-id"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA BOX 编号</label><input class="storage-transfer-input" id="storage-transfer-box-id" name="boxId" type="text" placeholder="支持单号或连续范围" required></div>
<div class="storage-transfer-field storage-transfer-span-3"><label class="storage-transfer-field-label" for="storage-transfer-note">订单备注</label><textarea class="storage-transfer-textarea" id="storage-transfer-note" name="note"></textarea></div>
</div>
</div>
</div>
<div class="card storage-transfer-section storage-transfer-settings-card">
<div class="storage-transfer-settings-header">
<div class="storage-transfer-settings-title">换标设置</div>
<span class="storage-transfer-settings-status is-on" id="storage-transfer-relabel-status" aria-live="polite">已开启</span>
</div>
<div class="storage-transfer-settings-body">
<label class="storage-transfer-settings-toggle" for="storage-transfer-relabel-toggle">
<input id="storage-transfer-relabel-toggle" name="relabel" type="checkbox" checked onchange="toggleStorageTransferRelabel()">
<span>是否换标</span>
</label>
<div class="storage-transfer-settings-fields" id="storage-transfer-relabel-fields">
<div class="storage-transfer-requirement-grid">
<div class="storage-transfer-settings-field storage-transfer-field">
<label class="storage-transfer-field-label" for="storage-transfer-relabel-requirement">换标要求</label>
<select class="storage-transfer-select" id="storage-transfer-relabel-requirement" name="relabelRequirement" onchange="updateStorageTransferRelabelRequirements()">
<option value="Case1">换箱唛</option>
<option value="Case2">换SKU标</option>
<option value="Case3">既换箱唛,又换SKU标</option>
</select>
</div>
<div class="storage-transfer-template-field">
<span class="storage-transfer-field-label">关联文件模板</span>
<button type="button" class="btn btn-default" onclick="downloadStorageTransferRelationTemplate()"><i class="fas fa-download" aria-hidden="true"></i> 下载模板</button>
</div>
</div>
<div class="storage-transfer-upload-grid">
<div class="storage-transfer-upload-card" id="storage-transfer-carton-upload-card">
<label for="storage-transfer-carton-label">上传箱唛</label>
<input id="storage-transfer-carton-label" name="relabelCartonLabel" type="file" accept=".pdf,.png,.jpg,.jpeg" onchange="updateStorageTransferRelabelRequirements()">
</div>
<div class="storage-transfer-upload-card" id="storage-transfer-sku-upload-card">
<label for="storage-transfer-sku-label">上传 SKU 标签</label>
<input id="storage-transfer-sku-label" name="relabelSkuLabel" type="file" accept=".pdf,.png,.jpg,.jpeg" onchange="updateStorageTransferRelabelRequirements()">
</div>
</div>
<div class="storage-transfer-associated-grid">
<div class="storage-transfer-upload-card" id="storage-transfer-relation-upload-card">
<label for="storage-transfer-relation-file">上传关联文件</label>
<input id="storage-transfer-relation-file" name="relabelRelationFile" type="file" accept=".xlsx,.xls" onchange="updateStorageTransferRelabelRequirements()">
</div>
<div class="storage-transfer-associated-field" id="storage-transfer-relabel-remark-card">
<label for="storage-transfer-relabel-remark">备注</label>
<textarea id="storage-transfer-relabel-remark" name="relabelRemark" maxlength="200" placeholder="请输入关联文件备注(选填)"></textarea>
</div>
</div>
<div class="storage-transfer-relabel-hint" id="storage-transfer-relabel-hint">换箱唛:需要上传箱唛</div>
<div class="storage-transfer-relabel-copy">开启换标后,提交订单时将同步生成后台换标单。</div>
</div>
<div class="storage-transfer-settings-empty" id="storage-transfer-relabel-empty" hidden>开启后可配置换标要求并上传对应标签文件。</div>
</div>
</div>
<div class="storage-transfer-form-actions">
<button type="button" class="btn btn-default" onclick="cancelStorageTransferCreate()">取消</button>
<button type="submit" class="btn btn-primary storage-transfer-submit" id="storage-transfer-submit">提交订单</button>
</div>
</form>
</div>
<!-- ========== PAGE: 仓储转运单详情 ========== -->
<div class="page-content" id="page-storage-transfer-detail">
<div class="breadcrumb"><a href="#" onclick="openStorageTransferList();return false;">仓储转运单</a><span class="sep">/</span><span class="current">订单详情</span></div>
<div class="page-header-actions storage-transfer-detail-header">
<div><div class="page-title" style="margin-bottom:4px;">仓储转运单详情</div><div class="inbound-detail-subtitle">转运单号:<strong id="storage-transfer-detail-order-no">-</strong></div></div>
<div class="page-action-group"><span class="labeling-detail-status" id="storage-transfer-detail-status">待处理</span><button type="button" class="btn btn-default" onclick="openStorageTransferList()"><i class="fas fa-arrow-left"></i> 返回列表</button></div>
</div>
<div class="labeling-detail-grid">
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">单据信息</div><dl id="storage-transfer-detail-document"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">货物信息</div><dl id="storage-transfer-detail-cargo"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">派送信息</div><dl id="storage-transfer-detail-delivery"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">换标信息</div><dl id="storage-transfer-detail-relabel"></dl><div class="empty-text" id="storage-transfer-detail-relabel-empty" hidden>该订单未开启换标</div></div></section>
</div>
</div>
<!-- ========== PAGE: 货物废弃列表 ========== -->
<div class="page-content" id="page-cargo-disposal-list">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">货物废弃</span></div>
<div class="page-header-actions"><div class="page-title">货物废弃</div><div class="page-action-group"><button type="button" class="btn btn-primary" onclick="openCargoDisposalCreate()">+ 新增货物废弃</button></div></div>
<div class="card cargo-disposal-section"><div class="card-body"><div class="cargo-disposal-filter-grid">
<div class="cargo-disposal-filter-item"><label class="cargo-disposal-label" for="cargo-disposal-filter-order-no">货物废弃单号</label><input class="cargo-disposal-input" id="cargo-disposal-filter-order-no" placeholder="请输入货物废弃单号"></div>
<div class="cargo-disposal-filter-item"><label class="cargo-disposal-label" for="cargo-disposal-filter-inbound-no">入库单号</label><input class="cargo-disposal-input" id="cargo-disposal-filter-inbound-no" placeholder="请输入入库单号"></div>
<div class="cargo-disposal-filter-item"><label class="cargo-disposal-label" for="cargo-disposal-filter-warehouse">操作仓</label><select class="cargo-disposal-select" id="cargo-disposal-filter-warehouse"><option value="">全部操作仓</option><option>美西洛杉矶仓</option><option>美东新泽西仓</option></select></div>
<div class="cargo-disposal-filter-item"><label class="cargo-disposal-label" for="cargo-disposal-filter-status">订单状态</label><select class="cargo-disposal-select" id="cargo-disposal-filter-status"><option value="">全部状态</option><option>待处理</option><option>处理中</option><option>已完成</option></select></div>
<div class="cargo-disposal-filter-item"><span class="cargo-disposal-label">创建时间</span><div style="display:grid;grid-template-columns:1fr auto 1fr;gap:7px;align-items:center;"><input class="cargo-disposal-input" id="cargo-disposal-filter-created-start" type="date" aria-label="创建开始时间"><span>至</span><input class="cargo-disposal-input" id="cargo-disposal-filter-created-end" type="date" aria-label="创建结束时间"></div></div>
</div><div class="cargo-disposal-actions"><button type="button" class="btn btn-primary" onclick="filterCargoDisposalOrders()">查询</button><button type="button" class="btn btn-default" onclick="resetCargoDisposalFilters()">重置</button></div></div></div>
<div class="card"><div class="table-wrap-simple"><table class="data-table" id="cargo-disposal-list-table"><thead><tr><th>序号</th><th>货物废弃单号</th><th>入仓码</th><th>关联入库单</th><th>废弃箱数</th><th>操作仓</th><th>弃货费</th><th>关联出库单</th><th>状态</th><th>创建时间</th><th>操作</th></tr></thead><tbody id="cargo-disposal-list-body"><tr class="empty-state"><td colspan="11">暂无货物废弃订单</td></tr></tbody></table></div><div class="storage-transfer-pagination"><div class="pagination-info">共 <strong id="cargo-disposal-total">0</strong> 条</div><div class="pagination-ctrl"><button class="page-btn" id="cargo-disposal-prev" onclick="changeCargoDisposalPage(-1)">上一页</button><button class="page-btn active" id="cargo-disposal-page-number">1</button><button class="page-btn" id="cargo-disposal-next" onclick="changeCargoDisposalPage(1)">下一页</button></div></div></div>
</div>
<!-- ========== PAGE: 新增货物废弃 ========== -->
<div class="page-content" id="page-cargo-disposal-create">
<div class="breadcrumb"><a href="#" onclick="openCargoDisposalList();return false;">货物废弃</a><span class="sep">/</span><span class="current">新增</span></div>
<div class="page-header-actions"><div class="page-title">新增货物废弃</div><button type="button" class="btn btn-default" onclick="openCargoDisposalList()">返回列表</button></div>
<form id="cargo-disposal-form" onsubmit="submitCargoDisposalOrder(event)">
<div class="card"><div class="card-body"><div class="storage-transfer-section-title">选择入仓码</div><div class="cargo-disposal-inbound-meta">展开入仓码后填写每条明细的可用箱数与废弃箱数,废弃箱数不能超过可用箱数。</div><div class="cargo-disposal-inbound-search"><label class="cargo-disposal-label" for="cargo-disposal-inbound-search"><span class="required">*</span>入仓码搜索</label><input class="cargo-disposal-input" id="cargo-disposal-inbound-search" type="search" placeholder="请输入入仓码" autocomplete="off" oninput="filterCargoDisposalInboundCards()"></div><input class="cargo-disposal-quantity" type="number" hidden aria-hidden="true"><div id="cargo-disposal-inbound-cards" aria-live="polite"><div class="empty-text">正在加载入仓码...</div></div><div class="cargo-disposal-fee-bar"><span>弃货费按废弃箱数 × 5 USD/箱计算</span><strong class="cargo-disposal-fee-amount" id="cargo-disposal-fee">$0.00</strong></div><div class="storage-transfer-error" id="cargo-disposal-error" aria-live="polite"></div></div></div>
<div class="cargo-disposal-actions"><button type="button" class="btn btn-default" onclick="openCargoDisposalList()">取消</button><button type="submit" class="btn btn-primary" id="cargo-disposal-submit">提交货物废弃</button></div>
</form>
</div>
<!-- ========== PAGE: 货物废弃详情 ========== -->
<div class="page-content" id="page-cargo-disposal-detail">
<div class="breadcrumb"><a href="#" onclick="openCargoDisposalList();return false;">货物废弃</a><span class="sep">/</span><span class="current">订单详情</span></div>
<div class="page-header-actions cargo-disposal-detail-header">
<div><div class="page-title" style="margin-bottom:4px;">货物废弃详情</div><div class="inbound-detail-subtitle">货物废弃单号:<strong id="cargo-disposal-detail-order-no">-</strong></div></div>
<div class="page-action-group cargo-disposal-detail-actions"><span class="labeling-detail-status" id="cargo-disposal-detail-status">待处理</span><button type="button" class="btn btn-default" onclick="openCargoDisposalList()"><i class="fas fa-arrow-left"></i> 返回列表</button></div>
</div>
<div class="labeling-detail-grid">
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">单据信息</div><dl id="cargo-disposal-detail-document"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">货物明细</div><div class="product-table-wrap"><table class="product-table"><thead><tr><th>商品SKU</th><th>中文品名</th><th>可用箱数</th><th>废弃箱数</th></tr></thead><tbody id="cargo-disposal-detail-items"></tbody></table></div></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">费用明细</div><dl id="cargo-disposal-detail-fees"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">关联单据</div><dl id="cargo-disposal-detail-relations"><div class="labeling-detail-field"><dt>后台尾程单号</dt><dd id="cargo-disposal-detail-tail-order-no">-</dd></div><div class="labeling-detail-field"><dt>关联出库单</dt><dd id="cargo-disposal-detail-outbound-order-no">-</dd></div><div class="labeling-detail-field"><dt>弃货费计费单号</dt><dd id="cargo-disposal-detail-billing-no">-</dd></div></dl></div></section>
</div>
</div>
<!-- ========== PAGE: 退货换标(合并退货入库/换标/出库) ========== -->
<div class="page-content" id="page-return-relabel-list">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">退货换标</span></div>
<div class="page-header-actions"><div class="page-title">退货换标</div><button class="btn btn-primary" onclick="openReturnRelabelCreate()">+ 新建退货换标单</button></div>
<div class="card storage-transfer-section"><div class="card-body"><div class="storage-transfer-filter-grid">
<div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="return-relabel-filter-order-no">退货换标单号</label><input class="storage-transfer-input" id="return-relabel-filter-order-no" type="text"></div><div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="return-relabel-filter-source">来源 FBA 仓</label><select class="storage-transfer-select" id="return-relabel-filter-source"><option value="">全部</option><option>ONT8</option><option>ABE8</option><option>CLT2</option></select></div><div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="return-relabel-filter-destination">目的 FBA 仓</label><select class="storage-transfer-select" id="return-relabel-filter-destination"><option value="">全部</option><option>ONT8</option><option>LAX9</option><option>ABE8</option></select></div><div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="return-relabel-filter-sku">商品 SKU</label><input class="storage-transfer-input" id="return-relabel-filter-sku" type="text"></div><div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="return-relabel-filter-enabled">是否换标</label><select class="storage-transfer-select" id="return-relabel-filter-enabled"><option value="">全部</option><option value="yes">是</option><option value="no">否</option></select></div><div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="return-relabel-filter-tail-status">尾程订单状态</label><select class="storage-transfer-select" id="return-relabel-filter-tail-status"><option value="">全部</option><option>待入库</option><option>已入库待出库</option><option>出库中</option><option>已完成</option></select></div><div class="storage-transfer-filter-item"><label class="storage-transfer-filter-label" for="return-relabel-filter-status">换标状态</label><select class="storage-transfer-select" id="return-relabel-filter-status"><option value="">全部</option><option>待换标</option><option>换标中</option><option>已完成</option></select></div><div class="storage-transfer-filter-item"><span class="storage-transfer-filter-label">创建时间</span><div class="storage-transfer-date-range"><input class="storage-transfer-input" id="return-relabel-filter-created-start" type="date" aria-label="创建开始时间"><span>至</span><input class="storage-transfer-input" id="return-relabel-filter-created-end" type="date" aria-label="创建结束时间"></div></div>
<div class="storage-transfer-filter-actions"><button type="button" class="btn btn-primary" onclick="filterReturnRelabelOrders()">查询</button><button type="button" class="btn btn-default" onclick="resetReturnRelabelFilters()">重置</button></div>
</div></div></div>
<div class="card"><div class="table-wrap-simple"><table class="data-table return-relabel-table"><thead><tr><th>序号</th><th>退货换标单号</th><th>入仓码</th><th>来源FBA仓</th><th>目的FBA仓</th><th>SKU汇总</th><th>退件总数</th><th>是否换标</th><th>贴标要求</th><th>换标单号</th><th>换标状态</th><th>创建时间</th><th>操作</th></tr></thead><tbody id="return-relabel-list-body"></tbody></table></div><div class="pagination-bar"><div class="pagination-info">共 <strong id="return-relabel-total">0</strong> 条</div><div class="pagination-ctrl"><button class="page-btn" onclick="changeReturnRelabelPage(-1)" id="return-relabel-prev">上一页</button><button class="page-btn active" id="return-relabel-page-number">1</button><button class="page-btn" onclick="changeReturnRelabelPage(1)" id="return-relabel-next">下一页</button></div></div></div>
</div>
<div class="page-content" id="page-return-relabel-create">
<div class="breadcrumb"><a href="#" onclick="openReturnRelabelList();return false;">退货换标</a><span class="sep">/</span><span class="current">新建</span></div>
<div class="page-header-actions"><div class="page-title">新建退货换标单</div><button class="btn btn-default" onclick="openReturnRelabelList()">返回列表</button></div>
<form id="return-relabel-form" onsubmit="submitReturnRelabelOrder(event)">
<div class="card return-relabel-section"><div class="card-body">
<div class="return-relabel-section-title">退货信息</div>
<div class="return-relabel-form-grid">
<div><label><span style="color:#ff4d4f;">*</span>入仓码</label><input class="return-relabel-input" name="inboundCode" required></div>
<div><label>来源 FBA 仓库</label><select class="return-relabel-select" name="sourceFba" required><option value="">请选择</option><option>ONT8</option><option>ABE8</option><option>CLT2</option></select></div>
<div><label>退货原因</label><select class="return-relabel-select" name="reason" required><option value="">请选择</option><option>客户退货</option><option>亚马逊移除</option><option>不可售</option></select></div>
<div><label>收货仓</label><select class="return-relabel-select" name="receivingWarehouse" required><option value="">请选择</option><option>洛杉矶仓</option><option>纽约仓</option></select></div>
<div class="return-relabel-span-2"><label>退货备注</label><textarea class="return-relabel-textarea" name="returnRemark"></textarea></div>
</div>
</div></div>
<div class="card return-relabel-section"><div class="card-body">
<div class="return-relabel-section-title">货物信息</div>
<div class="return-relabel-table-wrap">
<table class="product-table" id="return-relabel-sku-table">
<thead><tr><th>承运商</th><th>运单号</th><th>商品SKU<span style="color:#ff4d4f;">*</span></th><th>中文品名</th><th>FNSKU<span style="color:#ff4d4f;">*</span></th><th>换标新SKU</th><th>件数<span style="color:#ff4d4f;">*</span></th><th>操作</th></tr></thead>
<tbody id="return-relabel-sku-body"></tbody>
</table>
</div>
<button type="button" class="btn btn-ghost" onclick="addReturnRelabelSkuRow()">+ 添加 SKU</button>
</div></div>
<div class="card return-relabel-section"><div class="card-body">
<div class="return-relabel-section-title">换标设置</div>
<label><input type="checkbox" name="relabel" onchange="toggleReturnRelabel()"> 是否换标</label>
<div id="return-relabel-settings-fields"><div class="return-relabel-form-grid"><div><label>贴标要求</label><select class="return-relabel-select" name="labelCase" onchange="updateFileRequirements()"><option value="Case1">换箱唛</option><option value="Case2">换SKU标</option><option value="Case3">既换箱唛,又换SKU标</option></select></div></div><div class="return-relabel-file-grid"><div><label>上传箱唛</label><input type="file" name="cartonLabel"></div><div><label>上传 SKU 标签</label><input type="file" name="skuLabel"></div></div><div id="return-relabel-file-hint"></div></div>
</div></div>
<div class="card return-relabel-section"><div class="card-body">
<div class="return-relabel-section-title">尾程信息</div>
<div class="return-relabel-form-grid"><div><label>目的 FBA 仓库</label><select class="return-relabel-select" name="destinationFba" required><option value="">请选择</option><option>ONT8</option><option>LAX9</option><option>ABE8</option></select></div><div><label>派送地址</label><input class="return-relabel-input" name="address" required></div><div><label>期望到达时间</label><input class="return-relabel-input" name="expectedArrival" type="datetime-local" required></div><div class="return-relabel-span-2"><label>尾程备注</label><textarea class="return-relabel-textarea" name="tailRemark"></textarea></div></div>
<div id="return-relabel-error" class="return-relabel-error"></div>
</div></div>
<div class="return-relabel-form-actions"><button type="button" class="btn btn-default" onclick="openReturnRelabelList()">取消</button><button type="submit" class="btn btn-primary" id="return-relabel-submit">提交订单</button></div>
</form>
</div>
<div class="page-content" id="page-return-relabel-detail">
<div class="breadcrumb"><a href="#" onclick="openReturnRelabelList();return false;">退货换标</a><span class="sep">/</span><span class="current">订单详情</span></div>
<div class="page-header-actions">
<div><div class="page-title" style="margin-bottom:4px;">退货换标详情</div><div class="inbound-detail-subtitle">换标单号:<strong id="return-relabel-detail-order-no">-</strong></div></div>
<div class="page-action-group"><span class="labeling-detail-status" id="return-relabel-detail-status">待换标</span><button type="button" class="btn btn-default" onclick="openReturnRelabelList()"><i class="fas fa-arrow-left"></i> 返回列表</button></div>
</div>
<div class="labeling-detail-grid return-relabel-detail-grid">
<section class="card labeling-detail-card return-relabel-detail-order-card"><div class="card-body"><div class="info-section-title">订单信息</div><dl class="return-relabel-detail-order-grid" id="return-relabel-detail-summary"></dl></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">商品信息</div><div class="product-table-wrap"><table class="product-table" id="return-relabel-detail-sku-table"><thead><tr><th>商品SKU</th><th>中文品名</th><th>FNSKU</th><th>换标新SKU</th><th>件数</th></tr></thead><tbody id="return-relabel-detail-sku-body"></tbody></table></div></div></section>
<section class="card labeling-detail-card"><div class="card-body"><div class="info-section-title">换标数据</div><dl id="return-relabel-detail-relabel-data"><div class="labeling-detail-field"><dt>后台换标单号</dt><dd id="return-relabel-detail-relabel-order-no">-</dd></div><div class="labeling-detail-field"><dt>换标状态</dt><dd id="return-relabel-detail-relabel-status">待换标</dd></div><div class="labeling-detail-field"><dt>是否换标</dt><dd id="return-relabel-detail-relabel-enabled">-</dd></div><div class="labeling-detail-field"><dt>贴标要求</dt><dd id="return-relabel-detail-label-case">-</dd></div><div class="labeling-detail-field"><dt>上传箱唛</dt><dd id="return-relabel-detail-carton-label">-</dd></div><div class="labeling-detail-field"><dt>上传 SKU 标签</dt><dd id="return-relabel-detail-sku-label">-</dd></div><div class="labeling-detail-field"><dt>上传关联文件</dt><dd id="return-relabel-detail-relation-file">-</dd></div><div class="labeling-detail-field"><dt>关联文件备注</dt><dd id="return-relabel-detail-relation-remark">-</dd></div></dl></div></section>
</div>
</div>
<!-- ========== PAGE: 旧退货入库(保留 DOM 兼容,不再出现在导航) ========== -->
<div class="page-content" id="page-label-return-in">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">退货入库</span></div>
<div class="page-header-actions">
<div class="page-title">退货入库</div>
<div class="page-action-group">
<button class="btn btn-primary" onclick="openReturnInModal()">+ 新建</button>
<button class="btn btn-default" onclick="alert('导出Excel')">导出Excel</button>
</div>
</div>
<div class="card" style="margin-bottom:12px;">
<div class="card-body">
<div class="filter-grid" style="grid-template-columns:200px 1fr 160px 120px 80px 80px;">
<div class="filter-item">
<select class="filter-select"><option value="">全部FBA仓库</option><option>FBA ONT8</option><option>FBA CLT2</option><option>FBA MEM1</option><option>DE Frankfurt</option><option>FBA IND9</option></select>
</div>
<div class="filter-item">
<input type="text" class="filter-input" placeholder="退货单号">
</div>
<div class="filter-item">
<select class="filter-select"><option value="">全部状态</option><option>待接收</option><option>已接收</option></select>
</div>
<button class="btn-flat btn-flat-blue" onclick="alert('查询')">查询</button>
<button class="btn-flat" onclick="alert('重置')">重置</button>
</div>
</div>
</div>
<div class="card">
<div class="table-wrap-simple">
<table class="data-table">
<thead>
<tr>
<th>退货单号</th><th>FBA仓库</th><th>商品SKU</th><th>中文品名</th><th>箱数</th><th>退货原因</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>RTN20260701001</td><td>FBA ONT8</td><td>SKU-A1001</td><td>蓝牙耳机套件</td><td>6</td><td>客户退货-质量问题</td>
<td><span class="status-badge status-pending">待接收</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTN20260701002</td><td>FBA CLT2</td><td>SKU-B2002</td><td>手机保护壳</td><td>10</td><td>客户退货-七天无理由</td>
<td><span class="status-badge status-processing">已接收</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTN20260701003</td><td>FBA MEM1</td><td>SKU-C3003</td><td>充电器套装</td><td>14</td><td>亚马逊移除-不可售</td>
<td><span class="status-badge status-pending">待接收</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTN20260701004</td><td>DE Frankfurt</td><td>SKU-D4004</td><td>LED台灯</td><td>8</td><td>客户退货-尺寸不符</td>
<td><span class="status-badge status-processing">已接收</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTN20260701005</td><td>FBA IND9</td><td>SKU-E5005</td><td>收纳盒组合</td><td>16</td><td>亚马逊移除-可售</td>
<td><span class="status-badge status-pending">待接收</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
</tbody>
</table>
</div>
<div class="pagination-bar">
<div class="pagination-info"><span>共 <strong>5</strong> 条</span></div>
<div class="pagination-ctrl"><button class="page-btn" disabled>上一页</button><button class="page-btn active">1</button><button class="page-btn" disabled>下一页</button></div>
</div>
</div>
</div>
<!-- ========== PAGE: 退货换标 ========== -->
<div class="page-content" id="page-label-return-swap">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">退货换标</span></div>
<div class="page-header-actions">
<div class="page-title">退货换标</div>
<div class="page-action-group">
<button class="btn btn-primary" onclick="alert('新建换标单')">+ 新建换标单</button>
<button class="btn btn-default" onclick="alert('导出Excel')">导出Excel</button>
</div>
</div>
<div class="card" style="margin-bottom:12px;">
<div class="card-body">
<div class="filter-grid" style="grid-template-columns:200px 1fr 160px 120px 80px 80px;">
<div class="filter-item">
<select class="filter-select"><option value="">全部FBA仓库</option><option>FBA ONT8</option><option>FBA CLT2</option><option>FBA MEM1</option><option>DE Frankfurt</option><option>FBA IND9</option></select>
</div>
<div class="filter-item">
<input type="text" class="filter-input" placeholder="商品SKU">
</div>
<div class="filter-item">
<select class="filter-select"><option value="">全部状态</option><option>待换标</option><option>换标中</option><option>已完成</option></select>
</div>
<button class="btn-flat btn-flat-blue" onclick="alert('查询')">查询</button>
<button class="btn-flat" onclick="alert('重置')">重置</button>
</div>
</div>
</div>
<div class="card">
<div class="table-wrap-simple">
<table class="data-table">
<thead>
<tr>
<th>换标单号</th><th>FBA仓库</th><th>商品SKU</th><th>中文品名</th><th>箱数</th><th>贴标要求</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>RRL20260710001</td><td>FBA ONT8</td><td>SKU-A1001</td><td>蓝牙耳机套件</td><td>6</td><td>全覆盖换标</td>
<td><span class="status-badge status-pending">待换标</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RRL20260711002</td><td>FBA CLT2</td><td>SKU-B2002</td><td>手机保护壳</td><td>10</td><td>加贴新标</td>
<td><span class="status-badge status-processing">换标中</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RRL20260712003</td><td>FBA MEM1</td><td>SKU-C3003</td><td>充电器套装</td><td>14</td><td>移除原标+换标</td>
<td><span class="status-badge status-done">已完成</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RRL20260713004</td><td>DE Frankfurt</td><td>SKU-D4004</td><td>LED台灯</td><td>8</td><td>全覆盖换标</td>
<td><span class="status-badge status-pending">待换标</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RRL20260714005</td><td>FBA IND9</td><td>SKU-E5005</td><td>收纳盒组合</td><td>16</td><td>加贴新标</td>
<td><span class="status-badge status-processing">换标中</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
</tbody>
</table>
</div>
<div class="pagination-bar">
<div class="pagination-info"><span>共 <strong>5</strong> 条</span></div>
<div class="pagination-ctrl"><button class="page-btn" disabled>上一页</button><button class="page-btn active">1</button><button class="page-btn" disabled>下一页</button></div>
</div>
</div>
</div>
<!-- ========== PAGE: 退货出库 ========== -->
<div class="page-content" id="page-label-return-out">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">退货出库</span></div>
<div class="page-header-actions">
<div class="page-title">退货出库</div>
<div class="page-action-group">
<button class="btn btn-primary" onclick="alert('新建出库单')">+ 新建出库单</button>
<button class="btn btn-default" onclick="alert('导出Excel')">导出Excel</button>
</div>
</div>
<div class="card" style="margin-bottom:12px;">
<div class="card-body">
<div class="filter-grid" style="grid-template-columns:200px 1fr 160px 120px 80px 80px;">
<div class="filter-item">
<select class="filter-select"><option value="">全部FBA仓库</option><option>FBA ONT8</option><option>FBA CLT2</option><option>FBA MEM1</option><option>DE Frankfurt</option><option>FBA IND9</option></select>
</div>
<div class="filter-item">
<input type="text" class="filter-input" placeholder="出库单号">
</div>
<div class="filter-item">
<select class="filter-select"><option value="">全部状态</option><option>待出库</option><option>已出库</option></select>
</div>
<button class="btn-flat btn-flat-blue" onclick="alert('查询')">查询</button>
<button class="btn-flat" onclick="alert('重置')">重置</button>
</div>
</div>
</div>
<div class="card">
<div class="table-wrap-simple">
<table class="data-table">
<thead>
<tr>
<th>出库单号</th><th>FBA仓库</th><th>目的仓</th><th>商品SKU</th><th>中文品名</th><th>箱数</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>RTO20260715001</td><td>FBA ONT8</td><td>FBA LAX9</td><td>SKU-A1001</td><td>蓝牙耳机套件</td><td>6</td>
<td><span class="status-badge status-pending">待出库</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTO20260716002</td><td>FBA CLT2</td><td>FBA SBD1</td><td>SKU-B2002</td><td>手机保护壳</td><td>10</td>
<td><span class="status-badge status-done">已出库</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTO20260717003</td><td>FBA MEM1</td><td>FBA FTW1</td><td>SKU-C3003</td><td>充电器套装</td><td>14</td>
<td><span class="status-badge status-pending">待出库</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTO20260718004</td><td>DE Frankfurt</td><td>DE Munich</td><td>SKU-D4004</td><td>LED台灯</td><td>8</td>
<td><span class="status-badge status-done">已出库</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
<tr>
<td>RTO20260719005</td><td>FBA IND9</td><td>FBA LAS1</td><td>SKU-E5005</td><td>收纳盒组合</td><td>16</td>
<td><span class="status-badge status-pending">待出库</span></td>
<td><span class="link-action">查看</span><span class="link-action">编辑</span></td>
</tr>
</tbody>
</table>
</div>
<div class="pagination-bar">
<div class="pagination-info"><span>共 <strong>5</strong> 条</span></div>
<div class="pagination-ctrl"><button class="page-btn" disabled>上一页</button><button class="page-btn active">1</button><button class="page-btn" disabled>下一页</button></div>
</div>
</div>
</div>
<!-- PAGE: 一键导入(FBA批量下单) -->
<div class="page-content" id="page-bulk">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="sep-item">美线头程下单</span><span class="sep">/</span><span class="current">一键导入(FBA批量下单)</span></div>
<div class="page-title" style="margin-bottom:16px;">一键导入(FBA批量下单)</div>
<!-- 基本信息 -->
<div class="card" style="margin-bottom:12px;">
<div class="card-body">
<div class="form-group">
<div class="form-label"><span class="required">*</span>取货方式</div>
<div class="form-content">
<div class="dest-type-tabs" id="pickup-tabs-bulk">
<button class="dest-tab active" onclick="setPickupTabBulk(this,'express')">自送货-快递</button>
<button class="dest-tab" onclick="setPickupTabBulk(this,'pickup')">我司提货(有取货费)</button>
<button class="dest-tab" onclick="setPickupTabBulk(this,'truck')">自送货-卡车</button>
</div>
</div>
</div>
<!-- 自送货-快递 -->
<div class="dest-content-bulk active" id="pickup-bulk-express">
<div class="form-group">
<div class="form-label">预计送达时间</div>
<div class="form-content">
<input type="datetime-local" class="form-input" placeholder="请选择预计送达时间" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">快递单号</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="请输入快递单号" style="min-width:300px;">
</div>
</div>
</div>
<!-- 我司提货 -->
<div class="dest-content-bulk" id="pickup-bulk-pickup">
<div class="form-group">
<div class="form-label"><span class="required">*</span>提货地址</div>
<div class="form-content">
<textarea class="form-input" placeholder="请输入提货详细地址" style="min-width:400px;height:80px;"></textarea>
</div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>提货时间</div>
<div class="form-content">
<input type="datetime-local" class="form-input" placeholder="请选择提货时间" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">联系人</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="请输入联系人姓名" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">联系电话</div>
<div class="form-content">
<input type="tel" class="form-input" placeholder="请输入联系电话" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">提货备注</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="如:小区门禁、特殊要求等" style="min-width:300px;">
</div>
</div>
</div>
<!-- 自送货-卡车 -->
<div class="dest-content-bulk" id="pickup-bulk-truck">
<div class="form-group">
<div class="form-label"><span class="required">*</span>预计到达时间</div>
<div class="form-content">
<input type="datetime-local" class="form-input" placeholder="请选择预计到达时间" style="min-width:280px;">
</div>
</div>
<div class="form-group">
<div class="form-label">车牌号</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="请输入车牌号" style="min-width:200px;">
</div>
</div>
<div class="form-group">
<div class="form-label">司机姓名</div>
<div class="form-content">
<input type="text" class="form-input" placeholder="请输入司机姓名" style="min-width:200px;">
</div>
</div>
<div class="form-group">
<div class="form-label">司机电话</div>
<div class="form-content">
<input type="tel" class="form-input" placeholder="请输入司机电话" style="min-width:200px;">
</div>
</div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>收货仓</div>
<div class="form-content">
<label class="radio-label"><input type="radio" name="bulk-wh"> 义乌仓</label>
<label class="radio-label"><input type="radio" name="bulk-wh"> 深圳仓</label>
<label class="radio-label"><input type="radio" name="bulk-wh"> 青岛仓</label>
<label class="radio-label"><input type="radio" name="bulk-wh"> 上海仓</label>
<label class="radio-label"><input type="radio" name="bulk-wh"> 宁波仓</label>
<label class="radio-label"><input type="radio" name="bulk-wh"> 厦门仓</label>
</div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>渠道</div>
<div class="form-content">
<select class="form-select" style="width:220px;">
<option value="">请选择渠道</option>
<option>美线普货专线</option>
<option>美线敏感货专线</option>
<option>美线快线</option>
<option>美线FBA直送</option>
</select>
</div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>买单/退税</div>
<div class="form-content">
<label class="radio-label"><input type="radio" name="bulk-tax"> 买单</label>
<label class="radio-label"><input type="radio" name="bulk-tax"> 退税</label>
</div>
</div>
<div class="form-group">
<div class="form-label">是否整柜</div>
<div class="form-content">
<label class="radio-label"><input type="radio" name="bulk-container"> 整柜</label>
<label class="radio-label"><input type="radio" name="bulk-container"> 散货</label>
</div>
</div>
<div class="form-group">
<div class="form-label"><span class="required">*</span>是否商检</div>
<div class="form-content">
<label class="radio-label"><input type="radio" name="bulk-inspect"> 是</label>
<label class="radio-label"><input type="radio" name="bulk-inspect"> 否</label>
</div>
</div>
<div class="form-group">
<div class="form-label">船期</div>
<div class="form-content">
<div class="date-range">
<input type="date" class="form-input" style="width:160px;">
<span class="date-sep">~</span>
<input type="date" class="form-input" style="width:160px;">
</div>
</div>
</div>
<div class="form-group" style="align-items:flex-start;">
<div class="form-label" style="padding-top:6px;">备注NOTE</div>
<div class="form-content">
<textarea class="form-textarea" placeholder="请填写订单备注信息(选填)"></textarea>
</div>
</div>
</div>
</div>
<!-- 货物明细表格 -->
<div class="card" style="margin-bottom:12px;">
<div style="padding:16px 16px 12px;border-bottom:1px solid #f0f0f0;">
<div style="font-size:14px;font-weight:500;color:#262626;margin-bottom:12px;">货物明细</div>
<!-- 上传/下载按钮行 -->
<div style="display:flex;gap:10px;align-items:center;margin-bottom:10px;">
<label class="upload-link" style="cursor:pointer;">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
上传 Excel<input type="file" accept=".xlsx,.xls" style="display:none;" onchange="alert('已选择:'+(this.files[0]?.name||''))">
</label>
<a class="download-link" href="#">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
下载模板
</a>
</div>
</div>
<div style="overflow-x:auto;">
<table style="width:100%;border-collapse:collapse;font-size:12px;" id="bulk-table">
<thead>
<tr style="background:#fafafa;">
<th style="width:36px;padding:10px 8px;border-bottom:1px solid #f0f0f0;text-align:center;"><input type="checkbox" id="bulk-check-all" onchange="toggleBulkAll(this)"></th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">序号</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">目的仓类型</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">亚马逊仓代码</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">货件编号(ShipmentID)</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">跟踪编号(ReferenceID)</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">收件人</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">收件地址</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">产品英文名称</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">FNSKU</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">ASIN</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">单品申报价值(USD)</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">总件数</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">箱数</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">每箱件数</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">长(cm)</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">宽(cm)</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">高(cm)</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">毛重(kg)</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">是否带电</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">是否含液体</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">是否纯电</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">海关品名</th>
<th style="padding:10px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap;font-weight:500;color:#8c8c8c;text-align:left;">备注</th>
</tr>
</thead>
<tbody id="bulk-tbody">
<tr id="bulk-empty-row">
<td colspan="24" style="padding:40px 0;text-align:center;color:#bfbfbf;">
<div style="font-size:28px;margin-bottom:8px;">📦</div>
<div style="font-size:13px;">暂无数据,请点击「添加行」或导入 Excel</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 添加行/删除行按钮 — 表格下方 -->
<div style="padding:12px 16px;border-top:1px solid #f0f0f0;display:flex;gap:10px;align-items:center;">
<button class="btn-outline-blue" onclick="addBulkRow()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
添加行
</button>
<button class="btn-outline-red" onclick="deleteBulkRows()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
删除行
</button>
</div>
</div>
<!-- 底部按钮 -->
<div class="form-footer">
<div class="footer-tips"><span class="tip-icon">⚠</span>提交前请确认货物明细信息无误</div>
<div style="display:flex;gap:8px;">
<button class="btn btn-default" onclick="alert('已保存草稿')">保存草稿</button>
<button class="btn btn-primary" onclick="alert('提交成功!')">提交订单</button>
</div>
</div>
</div>
<!-- PAGE: 快速下单 -->
<div class="page-content" id="page-quick">
<div class="page-header">
<h1 class="page-title">快速下单</h1>
</div>
<div class="card">
<div class="card-header" style="flex-direction:column;align-items:flex-start;border-bottom:1px solid #f0f0f0;">
<div class="card-title">客户下单</div>
<div class="card-subtitle" style="line-height:1.6;">
快速下单:只需输入来源目的仓产品SKU/箱数,即可下单<br>
每行数据提交成功后给客户生成一个订单,支持一行订单多个产品SKU情况
</div>
</div>
<div class="card-body">
<div class="quick-table-wrap">
<table class="quick-table">
<thead>
<tr>
<th style="width:140px;">选择渠道</th>
<th style="width:160px;">目的仓</th>
<th style="width:180px;">操作</th>
<th>产品信息:产品SKU/箱数</th>
</tr>
</thead>
<tbody id="quick-rows">
<tr class="quick-row">
<td>
<div class="select-wrap">
<select class="form-select">
<option value="">请选择</option>
<option>美线普货专线</option>
<option>美线敏感货专线</option>
<option>美线快线</option>
<option>美线FBA直送</option>
</select>
</div>
</td>
<td>
<label class="radio-label" style="display:block;margin-bottom:4px;"><input type="radio" name="dest1"> FBA</label>
<label class="radio-label" style="display:block;margin-bottom:4px;"><input type="radio" name="dest1"> 海外仓</label>
<label class="radio-label" style="display:block;"><input type="radio" name="dest1"> 私人仓</label>
</td>
<td>
<div class="quick-row-actions">
<a href="javascript:void(0)" onclick="addProduct(this)" style="color:#1677ff;font-size:13px;">+ 添加产品</a>
<a href="javascript:void(0)" onclick="delQuickRow(this)" style="color:#ff4d4f;font-size:13px;">- 删除本行</a>
</div>
</td>
<td>
<div class="product-line">
<input type="text" class="form-input" placeholder="产品SKU" style="width:140px;">
<input type="number" class="form-input" value="0" style="width:80px;text-align:center;">
<a href="javascript:void(0)" onclick="delProduct(this)" style="color:#ff4d4f;font-size:13px;">- 删除此产品</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div style="margin-top:12px;">
<button class="btn btn-ghost" onclick="addQuickRow()" style="font-size:13px;">+ 新增一行</button>
</div>
<div style="text-align:center;margin-top:24px;">
<button class="btn btn-primary" onclick="submitQuickOrder()">提交订单</button>
</div>
</div>
</div>
</div>
<!-- ========== PAGE: 应付账单 ========== -->
<div class="page-content" id="page-biz-fee">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">账单结果</span></div>
<div class="page-title" style="margin-bottom:16px;">账单列表</div>
<div class="card">
<div class="table-wrap-simple" style="overflow-x:auto;">
<table class="data-table" id="finance-table">
<thead>
<tr>
<th>#</th>
<th>账单编号</th>
<th>创建日期</th>
<th>币种</th>
<th>汇率</th>
<th>开票日期</th>
<th>最近一次收款日期</th>
<th>账期(天)</th>
<th>应付金额</th>
<th>已付金额</th>
<th>备注</th>
<th>总金额</th>
<th>退票结果日</th>
<th>退款到期日</th>
<th>单据状态</th>
<th>操作</th>
</tr>
</thead>
<tbody id="client-bill-table-body">
<tr>
<td>1</td>
<td><a href="#" class="link-primary" onclick="viewBillDetail('Y2407069');return false;">查看明细</a></td>
<td><span class="tag tag-success">已确认</span></td>
<td>Y2407069</td>
<td>2024-07-04 13:53:13</td>
<td>CNY</td>
<td>7.26</td>
<td>—</td>
<td>2024-08-03</td>
<td>30</td>
<td>10160.5</td>
<td>0</td>
<td>0</td>
<td>2024-08-03</td>
<td>2024-07-04</td>
</tr>
<tr>
<td>2</td>
<td><a href="#" class="link-primary" onclick="viewBillDetail('Y2506061');return false;">查看明细</a></td>
<td><span class="tag tag-error">已作废</span></td>
<td>Y2506061</td>
<td>2025-05-02 09:09:57</td>
<td>CNY</td>
<td>7.11</td>
<td>—</td>
<td>2025-06-01</td>
<td>30</td>
<td>267769.75</td>
<td>0</td>
<td>0</td>
<td>2025-06-08</td>
<td>2025-05-09</td>
</tr>
<tr>
<td>3</td>
<td><a href="#" class="link-primary" onclick="viewBillDetail('Y2508075');return false;">查看明细</a></td>
<td><span class="tag tag-error">已作废</span></td>
<td>Y2508075</td>
<td>2025-05-09 09:50:08</td>
<td>CNY</td>
<td>7.11</td>
<td>—</td>
<td>—</td>
<td>30</td>
<td>913.6</td>
<td>0</td>
<td>0</td>
<td>2025-06-08</td>
<td>2025-05-09</td>
</tr>
<tr>
<td>4</td>
<td><a href="#" class="link-primary" onclick="viewBillDetail('Y2508047');return false;">查看明细</a></td>
<td><span class="tag tag-success">已确认</span></td>
<td>Y2508047</td>
<td>2025-05-05 16:13:05</td>
<td>CNY</td>
<td>7.22</td>
<td>—</td>
<td>—</td>
<td>30</td>
<td>202858</td>
<td>0</td>
<td>0</td>
<td>2025-06-05</td>
<td>2025-05-05</td>
</tr>
<tr>
<td>5</td>
<td><a href="#" class="link-primary" onclick="viewBillDetail('Y2507049');return false;">查看明细</a></td>
<td><span class="tag tag-success">已确认</span></td>
<td>Y2507049</td>
<td>2025-07-08 10:50:18</td>
<td>CNY</td>
<td>7.2</td>
<td>—</td>
<td>—</td>
<td>30</td>
<td>202881.6</td>
<td>0</td>
<td>0</td>
<td>2025-08-07</td>
<td>2025-07-08</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination-bar">
<div class="pagination-info"><span>共 <strong id="client-bill-total">0</strong> 条</span></div>
<div class="pagination-ctrl">
<button class="page-btn" disabled>上一页</button>
<button class="page-btn active">1</button>
<button class="page-btn">2</button>
<button class="page-btn">3</button>
<button class="page-btn">4</button>
<button class="page-btn">下一页</button>
</div>
</div>
</div>
</div>
<!-- ========== PAGE: 账单明细 ========== -->
<div class="page-content" id="page-bill-detail">
<div class="breadcrumb"><a href="#" onclick="switchPage('biz-fee');return false;">首页</a><span class="sep">/</span><a href="#" onclick="switchPage('biz-fee');return false;">应付账单</a><span class="sep">/</span><span class="current">账单明细</span></div>
<div class="page-title" style="margin-bottom:16px;">账单明细 <span id="detail-bill-no" style="font-size:14px;color:#8c8c8c;font-weight:400;margin-left:8px;"></span></div>
<div class="card">
<div class="table-wrap-simple" style="overflow-x:auto;">
<table class="data-table" id="bill-detail-table">
<thead>
<tr>
<th>#</th>
<th>入仓单号</th>
<th>SKU</th>
<th>渠道</th>
<th>FBA仓</th>
<th>FBA编号</th>
<th>箱数</th>
<th>毛重</th>
<th>体积</th>
<th>费用类型</th>
<th>币种</th>
<th>账单金额</th>
<th>入仓时间</th>
<th>出仓时间</th>
<th>结算结束日</th>
<th>结算起始日</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>PCHWK24061161</td>
<td>海外仓日用</td>
<td>洛杉矶普船海卡包税</td>
<td>—</td>
<td>—</td>
<td>6</td>
<td>106</td>
<td>0.292</td>
<td>运费</td>
<td>人民币</td>
<td>0</td>
<td>2024-06-11</td>
<td>—</td>
<td>2024-06-11</td>
<td>2024-07-11</td>
</tr>
<tr>
<td>2</td>
<td>BYHWK24061281</td>
<td>海外仓日用</td>
<td>美森加班海卡包税</td>
<td>—</td>
<td>—</td>
<td>37</td>
<td>654</td>
<td>1.799</td>
<td>运费</td>
<td>人民币</td>
<td>0</td>
<td>2024-06-12</td>
<td>—</td>
<td>2024-06-12</td>
<td>2024-07-12</td>
</tr>
<tr>
<td>3</td>
<td>PCHWK24061739</td>
<td>海外仓日用</td>
<td>洛杉矶普船海卡包税</td>
<td>—</td>
<td>—</td>
<td>2</td>
<td>36</td>
<td>0.097</td>
<td>运费</td>
<td>人民币</td>
<td>0</td>
<td>2024-06-17</td>
<td>—</td>
<td>2024-06-17</td>
<td>2024-07-17</td>
</tr>
<tr>
<td>4</td>
<td>PCHWK24062923</td>
<td>海外仓日用</td>
<td>洛杉矶普船海卡包税</td>
<td>—</td>
<td>—</td>
<td>2</td>
<td>36</td>
<td>0.097</td>
<td>运费</td>
<td>人民币</td>
<td>0</td>
<td>2024-06-29</td>
<td>—</td>
<td>2024-07-01</td>
<td>2024-07-31</td>
</tr>
<tr>
<td>5</td>
<td>PCHWK24062971</td>
<td>小白快递</td>
<td>洛杉矶普船海卡包税</td>
<td>—</td>
<td>—</td>
<td>5</td>
<td>1462</td>
<td>8.77</td>
<td>叉车费</td>
<td>人民币</td>
<td>130</td>
<td>2024-06-29</td>
<td>—</td>
<td>2024-07-03</td>
<td>2024-08-02</td>
</tr>
<tr>
<td>6</td>
<td>PCHWK24063021</td>
<td>小白快递</td>
<td>洛杉矶普船海卡包税</td>
<td>—</td>
<td>—</td>
<td>29</td>
<td>1175</td>
<td>7.045</td>
<td>运费</td>
<td>人民币</td>
<td>6932.5</td>
<td>2024-06-11</td>
<td>—</td>
<td>2024-07-04</td>
<td>2024-08-03</td>
</tr>
<tr>
<td>7</td>
<td>PCHWK24062971</td>
<td>小白快递</td>
<td>洛杉矶普船海卡包税</td>
<td>—</td>
<td>—</td>
<td>5</td>
<td>1462</td>
<td>8.77</td>
<td>运费</td>
<td>人民币</td>
<td>9503</td>
<td>2024-06-29</td>
<td>—</td>
<td>2024-07-04</td>
<td>2024-08-03</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- ========== PAGE: 公司信息 ========== -->
<div class="page-content" id="page-company">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">公司信息</span></div>
<div class="page-title" style="margin-bottom:16px;">公司信息</div>
<div class="info-tabs">
<button class="info-tab active" onclick="setInfoTab(this,'basic')">企业基本信息</button>
<button class="info-tab" onclick="setInfoTab(this,'contact')">联系信息</button>
<button class="info-tab" onclick="setInfoTab(this,'qualify')">资质文件</button>
</div>
<!-- Tab 1: 企业基本信息 -->
<div class="info-tab-content active" id="info-basic">
<div class="info-card">
<div class="info-section-title">企业基本信息</div>
<div class="info-grid">
<div class="info-row">
<div class="info-label">公司名称</div>
<div class="info-value">华远贸易有限公司</div>
</div>
<div class="info-row">
<div class="info-label">统一信用代码</div>
<div class="info-value">91110108MA01XXXXXX</div>
</div>
<div class="info-row">
<div class="info-label">企业类型</div>
<div class="info-value">有限责任公司</div>
</div>
<div class="info-row">
<div class="info-label">注册地</div>
<div class="info-value">北京市朝阳区</div>
</div>
<div class="info-row">
<div class="info-label">成立日期</div>
<div class="info-value">2018-06-15</div>
</div>
<div class="info-row">
<div class="info-label">注册资本</div>
<div class="info-value">500万人民币</div>
</div>
<div class="info-row">
<div class="info-label"><span style="color:#ff4d4f;margin-right:4px;">*</span>法人姓名</div>
<div class="info-value">李明华</div>
</div>
<div class="info-row">
<div class="info-label"><span style="color:#ff4d4f;margin-right:4px;">*</span>法人电话</div>
<div class="info-value">139****8866</div>
</div>
</div>
</div>
</div>
<!-- Tab 2: 联系信息 -->
<div class="info-tab-content" id="info-contact">
<div class="info-card">
<div class="info-section-title">联系信息</div>
<div class="info-grid">
<div class="info-row">
<div class="info-label">联系人</div>
<div class="info-value">张伟</div>
</div>
<div class="info-row">
<div class="info-label">手机</div>
<div class="info-value">138****6789</div>
</div>
<div class="info-row">
<div class="info-label">邮箱</div>
<div class="info-value">zhangwei@huayuan.com</div>
</div>
<div class="info-row">
<div class="info-label">微信</div>
<div class="info-value">zhangwei_hy</div>
</div>
<div class="info-row" style="grid-column: 1 / -1;">
<div class="info-label">地址</div>
<div class="info-value">北京市朝阳区建国路88号</div>
</div>
</div>
</div>
</div>
<!-- Tab 3: 资质文件 -->
<div class="info-tab-content" id="info-qualify">
<div class="info-card">
<div class="info-section-title">资质文件</div>
<table class="info-doc-table">
<thead>
<tr>
<th>文件类型</th>
<th>状态</th>
<th>有效期</th>
<th style="width:80px;">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>营业执照</td>
<td><span class="doc-status">已验证</span></td>
<td>2028-06-15</td>
<td><button class="doc-btn" onclick="viewDoc('营业执照','2028-06-15','已验证')">查看</button></td>
</tr>
<tr>
<td>法人身份证</td>
<td><span class="doc-status">已验证</span></td>
<td>2030-01-01</td>
<td><button class="doc-btn" onclick="viewDoc('法人身份证','2030-01-01','已验证')">查看</button></td>
</tr>
<tr>
<td>货代资质证书</td>
<td><span class="doc-status">已验证</span></td>
<td>2027-03-01</td>
<td><button class="doc-btn" onclick="viewDoc('货代资质证书','2027-03-01','已验证')">查看</button></td>
</tr>
<tr>
<td>银行开户许可证</td>
<td><span class="doc-status">已验证</span></td>
<td>长期有效</td>
<td><button class="doc-btn" onclick="viewDoc('银行开户许可证','长期有效','已验证')">查看</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!--
MESSAGE CENTER EXTENSION
THESIS: 系统通知必须在一次扫视内显露优先级与未读状态,不另造孤立消息门户。
OWN-WORLD: 延续工作台白色面板、海军蓝文字、操作蓝与克制红色状态点。
STORY: 客户从顶栏发现未读数量,进入我的信息,展开通知并完成清零。
FIRST VIEWPORT: 余额、个人信息标签和紧凑通知列表按现有页面层级连续出现,全部已读位于列表标题右侧。
FORM: 采用个人中心内嵌消息列表这一局部扩展形态,不改变既有视觉系统与页面骨架。
-->
<!-- ========== PAGE: 我的信息 ========== -->
<div class="page-content" id="page-profile">
<div class="breadcrumb"><a href="#" onclick="switchPage('order');return false;">首页</a><span class="sep">/</span><span class="current">我的信息</span></div>
<div class="page-title" style="margin-bottom:16px;">我的信息</div>
<!-- 账户余额 -->
<div class="balance-card">
<div class="balance-info">
<div class="balance-label" data-i18n="m_balance">账户余额</div>
<div class="balance-amount">¥ <span id="profileBalance">12,850.00</span></div>
</div>
<div class="balance-actions">
<button class="balance-detail-btn" onclick="openBalanceDetails()"><i class="fas fa-list-ul"></i>余额明细</button>
<button class="btn btn-primary" onclick="openTopup()" style="font-size:13px;padding:8px 22px;"><span data-i18n="dash_topup">充值</span></button>
</div>
</div>
<div class="info-tabs">
<button class="info-tab active" onclick="setProfileTab(this,'base')">基本信息</button>
<button class="info-tab" onclick="setProfileTab(this,'address')">地址管理</button>
<button class="info-tab" data-profile-tab="messages" onclick="setProfileTab(this,'messages')">消息通知 <span class="profile-message-tab-count" id="profile-message-unread-count" hidden>0</span></button>
<button class="info-tab" onclick="setProfileTab(this,'password')">修改密码</button>
</div>
<!-- Tab 1: 基本信息 -->
<div class="info-tab-content active" id="profile-base">
<div class="info-card">
<div class="info-section-title" style="display:flex;justify-content:space-between;align-items:center;">
基本信息
<button class="btn btn-primary" id="btn-edit-profile" onclick="toggleEditProfile(true)" style="font-size:12px;padding:6px 18px;">编辑</button>
</div>
<!-- 查看模式 -->
<div class="info-grid" id="profile-view">
<div class="info-row">
<div class="info-label">用户名</div>
<div class="info-value">zhangwei</div>
</div>
<div class="info-row">
<div class="info-label">真实姓名</div>
<div class="info-value">张伟</div>
</div>
<div class="info-row">
<div class="info-label">手机号</div>
<div class="info-value">138****6789</div>
</div>
<div class="info-row">
<div class="info-label">邮箱</div>
<div class="info-value">zhangwei@huayuan.com</div>
</div>
<div class="info-row">
<div class="info-label">微信</div>
<div class="info-value">zhangwei_hy</div>
</div>
<div class="info-row">
<div class="info-label">所属公司</div>
<div class="info-value">华远贸易有限公司</div>
</div>
</div>
<!-- 编辑模式 -->
<div class="info-grid" id="profile-edit" style="display:none;">
<div class="info-row">
<div class="info-label">用户名</div>
<div class="info-value"><input type="text" class="form-input" value="zhangwei" style="width:260px;"></div>
</div>
<div class="info-row">
<div class="info-label">真实姓名</div>
<div class="info-value"><input type="text" class="form-input" value="张伟" style="width:260px;"></div>
</div>
<div class="info-row">
<div class="info-label">手机号</div>
<div class="info-value"><input type="text" class="form-input" value="138****6789" style="width:260px;"></div>
</div>
<div class="info-row">
<div class="info-label">邮箱</div>
<div class="info-value"><input type="text" class="form-input" value="zhangwei@huayuan.com" style="width:260px;"></div>
</div>
<div class="info-row">
<div class="info-label">微信</div>
<div class="info-value"><input type="text" class="form-input" value="zhangwei_hy" style="width:260px;"></div>
</div>
<div class="info-row">
<div class="info-label">所属公司</div>
<div class="info-value"><input type="text" class="form-input" value="华远贸易有限公司" style="width:260px;" disabled></div>
</div>
</div>
<div id="profile-actions" style="margin-top:20px;padding-top:16px;border-top:1px solid #f0f0f0;display:none;">
<button class="btn btn-primary" onclick="saveProfile()">保存修改</button>
<button class="btn btn-default" onclick="cancelEditProfile()" style="margin-left:10px;">取消</button>
</div>
</div>
</div>
<!-- Tab 2: 地址管理 -->
<div class="info-tab-content" id="profile-address">
<div class="profile-address-panel">
<div class="profile-address-header">
<div>
<div class="info-section-title">地址列表</div>
<div class="profile-address-subtitle">管理下单常用地址</div>
</div>
<button type="button" class="btn btn-primary" id="profile-address-add" onclick="openProfileAddressModal()">
<i class="fas fa-plus" aria-hidden="true"></i><span>新增地址</span>
</button>
</div>
<div class="profile-address-table-wrap">
<table class="profile-address-table">
<thead>
<tr><th>操作</th><th>地址名称</th><th>仓库代码</th><th>联系人</th><th>联系电话</th><th>城市</th><th>州/省</th><th>国家</th><th>邮编</th><th>状态</th></tr>
</thead>
<tbody id="profile-address-body"></tbody>
</table>
</div>
<div class="profile-address-pagination">
<span>共 <strong id="profile-address-total">0</strong> 条</span>
<div class="profile-address-page-controls">
<button type="button" id="profile-address-prev" onclick="changeProfileAddressPage(-1)">上一页</button>
<span id="profile-address-page-number">1 / 1</span>
<button type="button" id="profile-address-next" onclick="changeProfileAddressPage(1)">下一页</button>
</div>
</div>
</div>
</div>
<!-- Tab 3: 消息通知 -->
<div class="info-tab-content" id="profile-messages">
<section class="profile-message-panel" aria-labelledby="profile-message-title">
<div class="profile-message-header">
<div>
<div class="profile-message-title-row">
<div class="info-section-title" id="profile-message-title">系统通知</div>
<span class="profile-message-count" id="profile-message-summary">0 条未读</span>
</div>
<div class="profile-message-subtitle">订单、账单与服务进度会在这里及时通知您</div>
</div>
<button type="button" class="btn btn-default" id="profile-message-read-all" onclick="markAllProfileMessagesRead()">
<i class="fas fa-check-double" aria-hidden="true"></i> 全部已读
</button>
</div>
<div class="profile-message-list" id="profile-message-list" aria-live="polite"></div>
</section>
</div>
<!-- Tab 4: 修改密码 -->
<div class="info-tab-content" id="profile-password">
<div class="info-card" style="max-width:480px;">
<div class="info-section-title">修改密码</div>
<div class="form-row" style="margin-bottom:16px;">
<label class="form-label" style="width:100px;">当前密码</label>
<input type="password" class="form-input" placeholder="请输入当前密码" style="width:260px;">
</div>
<div class="form-row" style="margin-bottom:16px;">
<label class="form-label" style="width:100px;">新密码</label>
<input type="password" class="form-input" placeholder="请输入新密码" style="width:260px;">
</div>
<div class="form-row" style="margin-bottom:16px;">
<label class="form-label" style="width:100px;">确认新密码</label>
<input type="password" class="form-input" placeholder="请再次输入新密码" style="width:260px;">
</div>
<div style="margin-top:20px;padding-top:16px;border-top:1px solid #f0f0f0;">
<button class="btn btn-primary" onclick="changePassword()">确认修改</button>
</div>
</div>
</div>
</div>
<!-- ========== MODAL: 编辑退货换标单 ========== -->
<div class="modal-overlay" id="return-relabel-edit-modal" onclick="if(event.target===this)closeReturnRelabelEdit()">
<div class="modal-box return-relabel-edit-modal-box" role="dialog" aria-modal="true" aria-labelledby="return-relabel-edit-title">
<form id="return-relabel-edit-form" onsubmit="submitReturnRelabelEdit(event)">
<div class="modal-header">
<div class="modal-title" id="return-relabel-edit-title">编辑退货换标单</div>
<button type="button" class="modal-close" onclick="closeReturnRelabelEdit()" aria-label="关闭">&times;</button>
</div>
<div class="modal-body">
<section class="return-relabel-edit-section">
<div class="return-relabel-section-title">退货信息</div>
<div class="return-relabel-form-grid">
<div><label for="return-relabel-edit-inbound-code">入仓码</label><input class="return-relabel-input" id="return-relabel-edit-inbound-code" name="inboundCode" required></div>
<div><label for="return-relabel-edit-source-fba">来源 FBA 仓库</label><select class="return-relabel-select" id="return-relabel-edit-source-fba" name="sourceFba" required><option value="">请选择</option><option>ONT8</option><option>ABE8</option><option>CLT2</option></select></div>
<div><label for="return-relabel-edit-reason">退货原因</label><select class="return-relabel-select" id="return-relabel-edit-reason" name="reason" required><option value="">请选择</option><option>客户退货</option><option>亚马逊移除</option><option>不可售</option></select></div>
<div><label for="return-relabel-edit-receiving-warehouse">收货仓</label><select class="return-relabel-select" id="return-relabel-edit-receiving-warehouse" name="receivingWarehouse" required><option value="">请选择</option><option>洛杉矶仓</option><option>纽约仓</option></select></div>
<div><label for="return-relabel-edit-carrier">物流承运商</label><input class="return-relabel-input" id="return-relabel-edit-carrier" name="carrier" required></div>
<div><label for="return-relabel-edit-tracking-no">物流跟踪号</label><input class="return-relabel-input" id="return-relabel-edit-tracking-no" name="trackingNo" required></div>
<div class="return-relabel-span-2"><label for="return-relabel-edit-return-remark">退货备注</label><textarea class="return-relabel-textarea" id="return-relabel-edit-return-remark" name="returnRemark"></textarea></div>
</div>
</section>
<section class="return-relabel-edit-section">
<div class="return-relabel-section-title">货物明细</div>
<div class="return-relabel-table-wrap">
<table class="product-table" id="return-relabel-edit-sku-table">
<thead><tr><th>商品SKU<span class="required">*</span></th><th>中文品名</th><th>FNSKU<span class="required">*</span></th><th>换标新SKU</th><th>件数<span class="required">*</span></th><th>操作</th></tr></thead>
<tbody id="return-relabel-edit-sku-body"></tbody>
</table>
</div>
<button type="button" class="btn btn-ghost" onclick="addReturnRelabelEditSkuRow()">+ 添加 SKU</button>
</section>
<section class="return-relabel-edit-section return-relabel-settings-panel">
<div class="return-relabel-settings-header">
<div class="return-relabel-section-title">换标设置</div>
<span class="return-relabel-settings-status" id="return-relabel-edit-settings-status" aria-live="polite">可选操作</span>
</div>
<div class="return-relabel-settings-body">
<label class="return-relabel-settings-toggle" for="return-relabel-edit-relabel"><input id="return-relabel-edit-relabel" type="checkbox" name="relabel" onchange="toggleReturnRelabelEdit()"> 是否换标</label>
<div class="return-relabel-settings-fields" id="return-relabel-edit-settings-fields">
<div class="return-relabel-form-grid"><div><label for="return-relabel-edit-label-case">贴标要求</label><select class="return-relabel-select" id="return-relabel-edit-label-case" name="labelCase" onchange="updateReturnRelabelEditFileRequirements()"><option value="Case1">换箱唛</option><option value="Case2">换SKU标</option><option value="Case3">既换箱唛,又换SKU标</option></select></div><div class="return-relabel-associated-template"><span class="return-relabel-associated-template-label">关联文件模板</span><button type="button" class="btn btn-default" onclick="downloadReturnRelabelRelationTemplate()"><i class="fas fa-download" aria-hidden="true"></i> 下载模板</button></div></div>
<div class="return-relabel-upload-grid">
<div class="return-relabel-upload-card" id="return-relabel-edit-carton-card"><label for="return-relabel-edit-carton-label">上传箱唛</label><input id="return-relabel-edit-carton-label" type="file" name="cartonLabel" accept=".pdf,.png,.jpg,.jpeg" onchange="captureReturnRelabelEditFileName(this,'return-relabel-edit-carton-current')"><input id="return-relabel-edit-carton-existing" type="hidden"><div class="return-relabel-edit-current-file" id="return-relabel-edit-carton-current">当前文件:<strong>无</strong></div></div>
<div class="return-relabel-upload-card" id="return-relabel-edit-sku-card"><label for="return-relabel-edit-sku-label">上传 SKU 标签</label><input id="return-relabel-edit-sku-label" type="file" name="skuLabel" accept=".pdf,.png,.jpg,.jpeg" onchange="captureReturnRelabelEditFileName(this,'return-relabel-edit-sku-current')"><input id="return-relabel-edit-sku-existing" type="hidden"><div class="return-relabel-edit-current-file" id="return-relabel-edit-sku-current">当前文件:<strong>无</strong></div></div>
</div>
<div class="return-relabel-associated-fields">
<div class="return-relabel-associated-field"><label for="return-relabel-edit-relation-file">上传关联文件</label><input id="return-relabel-edit-relation-file" type="file" name="relationFile" accept=".xlsx,.xls,.pdf,.png,.jpg,.jpeg" onchange="captureReturnRelabelEditFileName(this,'return-relabel-edit-relation-current')"><input id="return-relabel-edit-relation-existing" type="hidden"><div class="return-relabel-associated-current-file" id="return-relabel-edit-relation-current">当前文件:<strong>无</strong></div></div>
<div class="return-relabel-associated-field"><label for="return-relabel-edit-relation-remark">备注</label><textarea id="return-relabel-edit-relation-remark" name="relationRemark" placeholder="请输入关联文件备注"></textarea></div>
</div>
<div class="return-relabel-file-hint" id="return-relabel-edit-file-hint"></div>
</div>
<div class="return-relabel-settings-empty" id="return-relabel-edit-settings-empty" hidden>开启后配置贴标要求和上传文件</div>
</div>
</section>
<section class="return-relabel-edit-section">
<div class="return-relabel-section-title">尾程信息</div>
<div class="return-relabel-form-grid">
<div><label for="return-relabel-edit-destination-fba">目的 FBA 仓库</label><select class="return-relabel-select" id="return-relabel-edit-destination-fba" name="destinationFba" required><option value="">请选择</option><option>ONT8</option><option>LAX9</option><option>ABE8</option></select></div>
<div><label for="return-relabel-edit-address">派送地址</label><input class="return-relabel-input" id="return-relabel-edit-address" name="address" required></div>
<div><label for="return-relabel-edit-expected-arrival">期望到达时间</label><input class="return-relabel-input" id="return-relabel-edit-expected-arrival" name="expectedArrival" type="datetime-local" required></div>
<div class="return-relabel-span-2"><label for="return-relabel-edit-tail-remark">尾程备注</label><textarea class="return-relabel-textarea" id="return-relabel-edit-tail-remark" name="tailRemark"></textarea></div>
</div>
</section>
<div id="return-relabel-edit-error" class="return-relabel-error" role="alert" aria-live="polite"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="closeReturnRelabelEdit()">取消</button>
<button type="submit" class="btn btn-primary return-relabel-edit-submit" id="return-relabel-edit-submit">保存修改</button>
</div>
</form>
</div>
</div>
<!-- ========== MODAL: 编辑仓储转运单 ========== -->
<div class="modal-overlay" id="storage-transfer-edit-modal" onclick="if(event.target===this)closeStorageTransferEdit()">
<div class="modal-box storage-transfer-edit-modal-box" role="dialog" aria-modal="true" aria-labelledby="storage-transfer-edit-title">
<form id="storage-transfer-edit-form" onsubmit="submitStorageTransferEdit(event)">
<div class="modal-header">
<div class="modal-title" id="storage-transfer-edit-title">编辑仓储转运单</div>
<button type="button" class="modal-close" onclick="closeStorageTransferEdit()" aria-label="关闭">&times;</button>
</div>
<div class="modal-body">
<section class="storage-transfer-edit-section">
<div class="storage-transfer-section-title">基础与派送信息</div>
<div class="storage-transfer-form-grid">
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-inbound-no">入仓码</label><input class="storage-transfer-input" id="storage-transfer-edit-inbound-no" name="inboundNo" type="text"></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-warehouse">操作仓</label><select class="storage-transfer-select" id="storage-transfer-edit-warehouse" name="warehouse"><option value="">请选择操作仓</option><option>美西洛杉矶仓</option><option>美东新泽西仓</option><option>美中芝加哥仓</option></select></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-fba-code"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA 目的仓代码</label><input class="storage-transfer-input" id="storage-transfer-edit-fba-code" name="fbaCode" type="text" required></div>
<div class="storage-transfer-field storage-transfer-span-3"><label class="storage-transfer-field-label" for="storage-transfer-edit-address"><span class="storage-transfer-required" aria-hidden="true">*</span>详细派送地址</label><input class="storage-transfer-input" id="storage-transfer-edit-address" name="address" type="text" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-box-count"><span class="storage-transfer-required" aria-hidden="true">*</span>发货箱数</label><input class="storage-transfer-input" id="storage-transfer-edit-box-count" name="boxCount" type="number" min="1" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-delivery-method"><span class="storage-transfer-required" aria-hidden="true">*</span>派送方式</label><select class="storage-transfer-select" id="storage-transfer-edit-delivery-method" name="deliveryMethod" required><option value="">请选择派送方式</option><option>卡车派送</option><option>快递派送</option></select></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-expected-arrival"><span class="storage-transfer-required" aria-hidden="true">*</span>期望到达时间</label><input class="storage-transfer-input" id="storage-transfer-edit-expected-arrival" name="expectedArrival" type="datetime-local" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-shipment-id"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA 货件编号</label><input class="storage-transfer-input" id="storage-transfer-edit-shipment-id" name="shipmentId" type="text" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-tracking-id"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA 追踪编号</label><input class="storage-transfer-input" id="storage-transfer-edit-tracking-id" name="trackingId" type="text" required></div>
<div class="storage-transfer-field"><label class="storage-transfer-field-label" for="storage-transfer-edit-box-id"><span class="storage-transfer-required" aria-hidden="true">*</span>FBA BOX 编号</label><input class="storage-transfer-input" id="storage-transfer-edit-box-id" name="boxId" type="text" placeholder="支持单号或连续范围" required></div>
<div class="storage-transfer-field storage-transfer-span-3"><label class="storage-transfer-field-label" for="storage-transfer-edit-note">订单备注</label><textarea class="storage-transfer-textarea" id="storage-transfer-edit-note" name="note"></textarea></div>
</div>
</section>
<section class="storage-transfer-edit-section storage-transfer-settings-card">
<div class="storage-transfer-settings-header">
<div class="storage-transfer-settings-title">换标设置</div>
<span class="storage-transfer-settings-status" id="storage-transfer-edit-relabel-status" aria-live="polite">未开启</span>
</div>
<div class="storage-transfer-settings-body">
<label class="storage-transfer-settings-toggle" for="storage-transfer-edit-relabel-toggle">
<input id="storage-transfer-edit-relabel-toggle" name="relabel" type="checkbox" onchange="toggleStorageTransferEditRelabel()">
<span>是否换标</span>
</label>
<div class="storage-transfer-settings-fields" id="storage-transfer-edit-relabel-fields">
<div class="storage-transfer-requirement-grid">
<div class="storage-transfer-settings-field storage-transfer-field">
<label class="storage-transfer-field-label" for="storage-transfer-edit-relabel-requirement">换标要求</label>
<select class="storage-transfer-select" id="storage-transfer-edit-relabel-requirement" name="relabelRequirement" onchange="updateStorageTransferEditRelabelRequirements()">
<option value="Case1">换箱唛</option>
<option value="Case2">换SKU标</option>
<option value="Case3">既换箱唛,又换SKU标</option>
</select>
</div>
<div class="storage-transfer-template-field">
<span class="storage-transfer-field-label">关联文件模板</span>
<button type="button" class="btn btn-default" onclick="downloadStorageTransferRelationTemplate()"><i class="fas fa-download" aria-hidden="true"></i> 下载模板</button>
</div>
</div>
<div class="storage-transfer-upload-grid">
<div class="storage-transfer-upload-card" id="storage-transfer-edit-carton-upload-card">
<label for="storage-transfer-edit-carton-label">上传箱唛</label>
<input id="storage-transfer-edit-carton-label" name="relabelCartonLabel" type="file" accept=".pdf,.png,.jpg,.jpeg" onchange="captureStorageTransferEditFileName(this, 'storage-transfer-edit-carton-current')">
<input id="storage-transfer-edit-carton-existing" type="hidden">
<div class="storage-transfer-edit-current-file" id="storage-transfer-edit-carton-current">当前文件:<strong>无</strong></div>
</div>
<div class="storage-transfer-upload-card" id="storage-transfer-edit-sku-upload-card">
<label for="storage-transfer-edit-sku-label">上传 SKU 标签</label>
<input id="storage-transfer-edit-sku-label" name="relabelSkuLabel" type="file" accept=".pdf,.png,.jpg,.jpeg" onchange="captureStorageTransferEditFileName(this, 'storage-transfer-edit-sku-current')">
<input id="storage-transfer-edit-sku-existing" type="hidden">
<div class="storage-transfer-edit-current-file" id="storage-transfer-edit-sku-current">当前文件:<strong>无</strong></div>
</div>
</div>
<div class="storage-transfer-associated-grid">
<div class="storage-transfer-upload-card" id="storage-transfer-edit-relation-upload-card">
<label for="storage-transfer-edit-relation-file">上传关联文件</label>
<input id="storage-transfer-edit-relation-file" name="relabelRelationFile" type="file" accept=".xlsx,.xls" onchange="captureStorageTransferEditFileName(this, 'storage-transfer-edit-relation-current')">
<input id="storage-transfer-edit-relation-existing" type="hidden">
<div class="storage-transfer-edit-current-file" id="storage-transfer-edit-relation-current">当前文件:<strong>无</strong></div>
</div>
<div class="storage-transfer-associated-field" id="storage-transfer-edit-relabel-remark-card">
<label for="storage-transfer-edit-relabel-remark">备注</label>
<textarea id="storage-transfer-edit-relabel-remark" name="relabelRemark" maxlength="200" placeholder="请输入关联文件备注(选填)"></textarea>
</div>
</div>
<div class="storage-transfer-relabel-hint" id="storage-transfer-edit-relabel-hint"></div>
<div class="storage-transfer-relabel-copy">保存后将同步更新该转运单的换标信息。</div>
</div>
<div class="storage-transfer-settings-empty" id="storage-transfer-edit-relabel-empty" hidden>开启后可配置换标要求并上传对应标签文件。</div>
</div>
</section>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="closeStorageTransferEdit()">取消</button>
<button type="submit" class="btn btn-primary storage-transfer-submit" id="storage-transfer-edit-submit">保存修改</button>
</div>
</form>
</div>
</div>
<!-- ========== MODAL: 编辑货物废弃 ========== -->
<div class="modal-overlay" id="cargo-disposal-edit-modal" onclick="if(event.target===this)closeCargoDisposalEdit()">
<div class="modal-box cargo-disposal-edit-modal-box" role="dialog" aria-modal="true" aria-labelledby="cargo-disposal-edit-title">
<form id="cargo-disposal-edit-form" onsubmit="submitCargoDisposalEdit(event)">
<div class="modal-header">
<div class="modal-title" id="cargo-disposal-edit-title">编辑货物废弃</div>
<button type="button" class="modal-close" onclick="closeCargoDisposalEdit()" aria-label="关闭">&times;</button>
</div>
<div class="modal-body">
<div class="cargo-disposal-inbound-search">
<label class="cargo-disposal-label" for="cargo-disposal-edit-inbound-search"><span class="required">*</span>入仓码搜索</label>
<input class="cargo-disposal-input" id="cargo-disposal-edit-inbound-search" type="search" placeholder="请输入入仓码" autocomplete="off" oninput="filterCargoDisposalEditInboundCards()">
</div>
<div id="cargo-disposal-edit-inbound-cards" aria-live="polite"></div>
<div class="cargo-disposal-fee-bar"><span>弃货费按废弃箱数 × 5 USD/箱计算</span><strong class="cargo-disposal-fee-amount" id="cargo-disposal-edit-fee">$0.00</strong></div>
<div class="storage-transfer-error" id="cargo-disposal-edit-error" aria-live="polite"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="closeCargoDisposalEdit()">取消</button>
<button type="submit" class="btn btn-primary" id="cargo-disposal-edit-submit">保存修改</button>
</div>
</form>
</div>
</div>
<!-- ========== MODAL: 新增/编辑地址 ========== -->
<div class="modal-overlay" id="profile-address-modal" onclick="if(event.target===this)closeProfileAddressModal()">
<div class="modal-box profile-address-modal-box" role="dialog" aria-modal="true" aria-labelledby="profile-address-modal-title">
<form id="profile-address-form" onsubmit="submitProfileAddress(event)">
<div class="modal-header">
<div class="modal-title" id="profile-address-modal-title">新增地址</div>
<button type="button" class="modal-close" onclick="closeProfileAddressModal()" aria-label="关闭">&times;</button>
</div>
<div class="modal-body">
<div class="profile-address-form-grid">
<div class="profile-address-field">
<label for="profile-address-name"><span class="required">*</span>地址名称</label>
<input type="text" class="form-input" id="profile-address-name" name="addressName" placeholder="请输入地址名称" required>
</div>
<div class="profile-address-field">
<label for="profile-address-warehouse-code"><span class="required">*</span>仓库代码</label>
<input type="text" class="form-input" id="profile-address-warehouse-code" name="warehouseCode" placeholder="请输入仓库代码" required>
</div>
<div class="profile-address-field profile-address-field-wide">
<label for="profile-address-detail">详细地址</label>
<input type="text" class="form-input" id="profile-address-detail" name="detailAddress" placeholder="请输入街道、门牌号等详细地址">
</div>
<div class="profile-address-field">
<label for="profile-address-contact">联系人</label>
<input type="text" class="form-input" id="profile-address-contact" name="contact" placeholder="请输入联系人">
</div>
<div class="profile-address-field">
<label for="profile-address-phone">联系电话</label>
<input type="tel" class="form-input" id="profile-address-phone" name="phone" placeholder="请输入联系电话">
</div>
<div class="profile-address-field">
<label for="profile-address-city">城市</label>
<input type="text" class="form-input" id="profile-address-city" name="city" placeholder="请输入城市">
</div>
<div class="profile-address-field">
<label for="profile-address-state-province">州/省</label>
<input type="text" class="form-input" id="profile-address-state-province" name="stateProvince" placeholder="请输入州或省">
</div>
<div class="profile-address-field">
<label for="profile-address-country"><span class="required">*</span>国家</label>
<input type="text" class="form-input" id="profile-address-country" name="country" placeholder="请输入国家" required>
</div>
<div class="profile-address-field">
<label for="profile-address-postal-code">邮编</label>
<input type="text" class="form-input" id="profile-address-postal-code" name="postalCode" placeholder="请输入邮编">
</div>
<div class="profile-address-field">
<label for="profile-address-enabled">状态</label>
<select class="form-input" id="profile-address-enabled" name="enabled">
<option value="true">启用</option>
<option value="false">停用</option>
</select>
</div>
</div>
<div class="profile-address-form-error" id="profile-address-form-error" role="alert"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="closeProfileAddressModal()">取消</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
</div>
<!-- ========== MODAL: 删除地址 ========== -->
<div class="modal-overlay" id="profile-address-delete-modal" onclick="if(event.target===this)closeProfileAddressDeleteModal()">
<div class="modal-box profile-address-delete-box" role="alertdialog" aria-modal="true" aria-labelledby="profile-address-delete-title" aria-describedby="profile-address-delete-message">
<div class="modal-header">
<div class="modal-title" id="profile-address-delete-title">删除地址</div>
<button type="button" class="modal-close" onclick="closeProfileAddressDeleteModal()" aria-label="关闭">&times;</button>
</div>
<div class="modal-body">
<p class="profile-address-delete-copy" id="profile-address-delete-message">确认删除该地址吗?</p>
<p class="profile-address-delete-hint">删除后将无法恢复。</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="closeProfileAddressDeleteModal()">取消</button>
<button type="button" class="btn profile-address-delete-confirm" onclick="confirmDeleteProfileAddress()">删除</button>
</div>
</div>
</div>
<!-- ========== MODAL: 客户确认对账单 ========== -->
<div class="modal-overlay" id="client-bill-confirm-modal" onclick="if(event.target===this)closeClientBillConfirm()">
<div class="modal-box" style="width:520px" role="dialog" aria-modal="true" aria-labelledby="client-bill-confirm-title">
<div class="modal-header">
<div class="modal-title" id="client-bill-confirm-title">确认对账单</div>
<button type="button" class="modal-close" onclick="closeClientBillConfirm()" aria-label="关闭">&times;</button>
</div>
<div class="modal-body">
<p style="margin:0 0 18px;color:#475569;line-height:1.7">确认后,该对账单将进入财务结算流程。</p>
<div class="doc-info">
<div class="doc-info-row"><div class="doc-info-label">对账单号</div><div class="doc-info-value" id="client-bill-confirm-no">-</div></div>
<div class="doc-info-row"><div class="doc-info-label">币种</div><div class="doc-info-value" id="client-bill-confirm-currency">-</div></div>
<div class="doc-info-row"><div class="doc-info-label">应付金额</div><div class="doc-info-value" id="client-bill-confirm-amount">-</div></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="closeClientBillConfirm()">取消</button>
<button type="button" class="btn btn-primary" id="client-bill-confirm-submit" onclick="confirmClientBill()">确认对账单</button>
</div>
</div>
</div>
<!-- ========== MODAL: 资质文件查看 ========== -->
<div class="modal-overlay" id="doc-modal" onclick="if(event.target===this)closeDocModal()">
<div class="modal-box">
<div class="modal-header">
<div class="modal-title" id="doc-modal-title">资质文件查看</div>
<button class="modal-close" onclick="closeDocModal()">&times;</button>
</div>
<div class="modal-body">
<div class="doc-preview">
<div style="width:100%;height:320px;background:linear-gradient(135deg,#f5f7fa,#e8ecf1);border-radius:12px;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;">
<div style="font-size:64px;">📄</div>
<div style="font-size:14px;color:#8c8c8c;" id="doc-placeholder-text">文件预览</div>
</div>
</div>
<div class="doc-info">
<div class="doc-info-row"><div class="doc-info-label">文件类型</div><div class="doc-info-value" id="doc-modal-type">-</div></div>
<div class="doc-info-row"><div class="doc-info-label">验证状态</div><div class="doc-info-value" id="doc-modal-status">-</div></div>
<div class="doc-info-row"><div class="doc-info-label">有效期</div><div class="doc-info-value" id="doc-modal-expire">-</div></div>
<div class="doc-info-row"><div class="doc-info-label">上传时间</div><div class="doc-info-value" id="doc-modal-upload">2024-03-10</div></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" onclick="closeDocModal()">关闭</button>
<button class="btn btn-primary" onclick="alert('开始下载文件...')">下载文件</button>
</div>
</div>
</div>
<!-- ========== MODAL: 新建退货入库单 ========== -->
<div class="modal-overlay" id="return-in-modal" onclick="if(event.target===this)closeReturnInModal()">
<div class="modal-box modal-wide">
<div class="modal-header">
<div class="modal-title">新建退货入库单</div>
<button class="modal-close" onclick="closeReturnInModal()">&times;</button>
</div>
<div class="modal-body">
<div class="form-section">
<div class="modal-section-title">基础信息</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px 24px;">
<div class="form-group">
<div class="form-label"><span class="required">*</span>仓库</div>
<div class="form-content">
<select class="form-input" style="width:100%;">
<option value="">请选择</option>
<option>FBA ONT8</option>
<option>FBA CLT2</option>
<option>FBA MEM1</option>
<option>DE Frankfurt</option>
<option>FBA IND9</option>
</select>
</div>
</div>
<div class="form-group">
<div class="form-label">移除订单Order ID</div>
<div class="form-content"><input type="text" class="form-input" placeholder="请输入移除订单Order ID" style="width:100%;"></div>
</div>
<div class="form-group" style="grid-column:1 / -1;">
<div class="form-label">备注</div>
<div class="form-content">
<textarea class="form-textarea" placeholder="请输入备注" style="width:100%;height:70px;max-width:100%;"></textarea>
<div style="text-align:right;font-size:12px;color:#bfbfbf;">0/255</div>
</div>
</div>
</div>
</div>
<div class="form-section">
<div class="modal-section-title">产品信息</div>
<div class="product-table-wrap">
<table class="product-table" id="return-in-product-table">
<thead>
<tr>
<th><span class="required">*</span>物流承运商</th>
<th><span class="required">*</span>物流跟踪号</th>
<th><span class="required">*</span>SKU</th>
<th><span class="required">*</span>产品名称</th>
<th><span class="required">*</span>FNSKU</th>
<th><span class="required">*</span>退件数量</th>
<th>操作</th>
</tr>
</thead>
<tbody id="return-in-product-tbody">
<tr>
<td><input type="text" class="cell-input" placeholder="物流承运商"></td>
<td><input type="text" class="cell-input" placeholder="物流跟踪号"></td>
<td>—</td>
<td>—</td>
<td><input type="text" class="cell-input" placeholder="FNSKU"></td>
<td><input type="number" class="cell-input" placeholder="数量"></td>
<td><button class="btn-text" style="color:#ff4d4f;" onclick="this.closest('tr').remove()">删除</button></td>
</tr>
</tbody>
</table>
</div>
<div style="display:flex;justify-content:center;margin:12px 0;">
<button class="btn-text" onclick="alert('选择产品')"><span style="font-size:14px;">◎</span> 选择产品</button>
</div>
<button class="btn-ghost" onclick="addReturnInProductRow()">+ 添加产品</button>
</div>
</div>
<div class="modal-footer">
<button class="btn-ghost" onclick="closeReturnInModal()">取消</button>
<button class="btn btn-primary" onclick="submitReturnInModal()">提交</button>
<button class="btn btn-default" onclick="saveReturnInDraft()">保存草稿</button>
</div>
</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>
<!-- 充值弹窗 -->
<div id="topupModal" class="topup-overlay hidden" onclick="if(event.target===this)closeTopup()">
<div class="topup-modal" role="dialog" aria-modal="true" aria-labelledby="topupModalTitle">
<div class="topup-head">
<h3 id="topupModalTitle" data-i18n="dash_topup_title">账户充值</h3>
<button type="button" class="topup-close" onclick="closeTopup()" data-i18n-aria-label="dash_topup_close_label" aria-label="关闭">&times;</button>
</div>
<div class="topup-body">
<div class="topup-stepper" data-i18n-aria-label="dash_topup_progress_label" aria-label="充值进度">
<div class="topup-step active" data-topup-step="1"><span class="topup-step-dot">1</span><span data-i18n="dash_topup_step_amount">填写金额</span></div>
<div class="topup-step" data-topup-step="2"><span class="topup-step-dot">2</span><span data-i18n="dash_topup_step_proof">上传凭证</span></div>
<div class="topup-step" data-topup-step="3"><span class="topup-step-dot">3</span><span data-i18n="dash_topup_step_success">提交成功</span></div>
</div>
<section class="topup-panel" id="topupStep1" aria-labelledby="topupAmountLabel">
<p class="topup-help" data-i18n="dash_topup_amount_help">请输入本次转账充值金额。</p>
<label class="topup-amount-label" id="topupAmountLabel" for="topupAmount" data-i18n="dash_topup_amount">充值金额</label>
<div class="topup-amount-input" id="topupAmountField">
<span class="cur">¥</span>
<input type="number" id="topupAmount" min="0.01" step="0.01" inputmode="decimal" aria-describedby="topupAmountError" oninput="setTopupAmount(this.value)">
</div>
<div class="topup-presets" id="topupPresets">
<button type="button" class="topup-preset" onclick="setTopupAmount(500,this)">¥500</button>
<button type="button" class="topup-preset" onclick="setTopupAmount(1000,this)">¥1,000</button>
<button type="button" class="topup-preset" onclick="setTopupAmount(3000,this)">¥3,000</button>
<button type="button" class="topup-preset" onclick="setTopupAmount(5000,this)">¥5,000</button>
</div>
<div class="topup-error" id="topupAmountError" role="alert"></div>
</section>
<section class="topup-panel" id="topupStep2" aria-labelledby="topupProofTitle" hidden>
<h4 class="topup-panel-title" id="topupProofTitle" data-i18n="dash_topup_proof_title">上传转账凭证</h4>
<p class="topup-help" data-i18n="dash_topup_proof_help">请上传清晰的银行或支付转账凭证,以便财务核对。</p>
<div class="topup-payment-qr" id="topupPaymentQrCard">
<div class="topup-payment-qr-heading">
<span class="topup-payment-qr-icon" aria-hidden="true"><i class="fas fa-qrcode"></i></span>
<div>
<h5 data-i18n="dash_topup_qr_title">收款二维码</h5>
<p data-i18n="dash_topup_qr_hint">请扫码完成转账,转账后上传打款凭证</p>
</div>
</div>
<div class="topup-payment-qr-visual">
<img id="topupPaymentQr" src="assets/payment-qr-placeholder.svg" data-i18n-alt="dash_topup_qr_alt" alt="收款二维码占位图,待替换">
<span data-i18n="dash_topup_qr_placeholder">待替换</span>
</div>
</div>
<input id="topupProof" type="file" accept="image/jpeg,image/png,application/pdf,.jpg,.jpeg,.png,.pdf" hidden onchange="handleTopupProof(this.files[0])">
<button type="button" class="topup-upload" id="topupUpload" onclick="document.getElementById('topupProof').click()" ondragover="handleTopupDragOver(event)" ondragleave="handleTopupDragLeave(event)" ondrop="handleTopupDrop(event)" aria-describedby="topupProofError">
<i class="fas fa-cloud-arrow-up" aria-hidden="true"></i>
<span class="topup-upload-title" data-i18n="dash_topup_upload_title">点击或拖拽上传凭证</span>
<span class="topup-upload-hint" data-i18n="dash_topup_upload_hint">支持 JPG、PNG、PDF,文件不超过 10MB</span>
</button>
<div class="topup-proof-summary" id="topupProofSummary" hidden>
<span class="topup-proof-icon"><i class="fas fa-file-lines" aria-hidden="true"></i></span>
<div class="topup-proof-meta"><div class="topup-proof-name" id="topupProofName"></div><div class="topup-proof-size" id="topupProofSize"></div></div>
<button type="button" class="topup-remove" onclick="removeTopupProof()" data-i18n-aria-label="dash_topup_remove_label" aria-label="移除文件"><i class="fas fa-trash-can" aria-hidden="true"></i></button>
</div>
<div class="topup-error" id="topupProofError" role="alert"></div>
</section>
<section class="topup-panel" id="topupStep3" aria-labelledby="topupSuccessTitle" aria-live="polite" hidden>
<div class="topup-success">
<span class="topup-success-icon"><i class="fas fa-check" aria-hidden="true"></i></span>
<h4 class="topup-panel-title" id="topupSuccessTitle" tabindex="-1" data-i18n="dash_topup_success_title">充值申请已提交</h4>
<div class="topup-status"><i class="fas fa-clock" aria-hidden="true"></i><span data-i18n="dash_topup_pending">待财务审核</span></div>
<div class="topup-result">
<div class="topup-result-row"><span data-i18n="dash_topup_result_amount">充值金额</span><strong id="topupSuccessAmount">-</strong></div>
<div class="topup-result-row"><span data-i18n="dash_topup_result_proof">转账凭证</span><strong id="topupSuccessFile">-</strong></div>
</div>
<div class="topup-notice"><i class="fas fa-circle-info" aria-hidden="true"></i><span data-i18n="dash_topup_arrival_notice">预计 24 小时到账,财务审核完成后余额会自动更新</span></div>
<div class="topup-sms" id="topupSmsNotice"><i class="fas fa-message" aria-hidden="true"></i> <span data-i18n="dash_topup_sms_notice">已自动发送短信提醒财务</span></div>
</div>
</section>
</div>
<div class="topup-foot">
<div class="topup-foot-group" id="topupFoot1">
<button type="button" class="topup-cancel" onclick="closeTopup()" data-i18n="dash_cancel">取消</button>
<button type="button" class="topup-confirm" onclick="goToTopupProof()" data-i18n="dash_topup_next">下一步</button>
</div>
<div class="topup-foot-group" id="topupFoot2" hidden>
<button type="button" class="topup-cancel" onclick="goBackToTopupAmount()" data-i18n="dash_topup_back">上一步</button>
<button type="button" class="topup-confirm" onclick="submitTopupRequest()" data-i18n="dash_topup_submit">提交申请</button>
</div>
<div class="topup-foot-group" id="topupFoot3" hidden>
<button type="button" class="topup-confirm" onclick="finishTopup()" data-i18n="dash_topup_finish">完成</button>
</div>
</div>
</div>
</div>
<!-- 余额明细弹窗 -->
<div id="balanceDetailsModal" class="topup-overlay hidden" onclick="if(event.target===this)closeBalanceDetails()">
<div class="topup-modal balance-detail-modal">
<div class="topup-head">
<h3>余额明细</h3>
<button class="topup-close" onclick="closeBalanceDetails()" aria-label="关闭">&times;</button>
</div>
<div class="topup-body">
<div class="balance-detail-summary">
<div class="balance-summary-item">
<div class="balance-summary-label">当前余额</div>
<div class="balance-summary-value">¥12,850.00</div>
</div>
<div class="balance-summary-item">
<div class="balance-summary-label">本月收入</div>
<div class="balance-summary-value income">+¥8,000.00</div>
</div>
<div class="balance-summary-item">
<div class="balance-summary-label">本月支出</div>
<div class="balance-summary-value expense">-¥3,260.00</div>
</div>
</div>
<div class="balance-detail-filter">
<div class="balance-quick-periods" id="balanceQuickPeriods">
<button class="balance-period-btn" onclick="setBalancePeriod('today',this)">今天</button>
<button class="balance-period-btn" onclick="setBalancePeriod(7,this)">近7天</button>
<button class="balance-period-btn" onclick="setBalancePeriod('month',this)">本月</button>
<button class="balance-period-btn active" onclick="setBalancePeriod(30,this)">近30天</button>
</div>
<div class="balance-date-range">
<input type="date" id="balanceStartDate" aria-label="开始日期">
<span>至</span>
<input type="date" id="balanceEndDate" aria-label="结束日期">
<button class="balance-filter-action" onclick="filterBalanceDetails()">查询</button>
<button class="balance-filter-action balance-filter-reset" onclick="resetBalanceFilter()">重置</button>
</div>
</div>
<div class="balance-detail-table-wrap">
<table class="balance-detail-table">
<thead><tr><th>交易时间</th><th>业务类型</th><th>关联单号</th><th>收支金额</th><th>交易后余额</th><th>状态</th></tr></thead>
<tbody id="balanceDetailRows">
<tr data-balance-date="2026-07-28"><td>2026-07-28 14:32</td><td>账户充值</td><td>CZ20260728001</td><td class="amount-in">+¥5,000.00</td><td>¥12,850.00</td><td><span class="balance-status">已完成</span></td></tr>
<tr data-balance-date="2026-07-25"><td>2026-07-25 10:18</td><td>运费扣款</td><td>BY2607250086</td><td class="amount-out">-¥1,860.00</td><td>¥7,850.00</td><td><span class="balance-status">已完成</span></td></tr>
<tr data-balance-date="2026-07-20"><td>2026-07-20 16:45</td><td>账户充值</td><td>CZ20260720003</td><td class="amount-in">+¥3,000.00</td><td>¥9,710.00</td><td><span class="balance-status">已完成</span></td></tr>
<tr data-balance-date="2026-07-16"><td>2026-07-16 09:26</td><td>运费扣款</td><td>BY2607160032</td><td class="amount-out">-¥1,400.00</td><td>¥6,710.00</td><td><span class="balance-status">已完成</span></td></tr>
<tr class="balance-empty-row" id="balanceEmptyRow" hidden><td colspan="6">所选时间范围内暂无余额明细</td></tr>
</tbody>
</table>
</div>
<div class="balance-detail-note" id="balanceDetailNote">仅展示近 30 天余额流水</div>
</div>
</div>
</div>
<div class="topup-overlay hidden" id="inbound-measurement-modal" onclick="if(event.target===this)closeInboundMeasurementConfirm()">
<div class="topup-modal inbound-measurement-modal" role="dialog" aria-modal="true" aria-labelledby="inbound-measurement-title">
<div class="topup-head"><h3 id="inbound-measurement-title">确认</h3><button class="topup-close" type="button" onclick="closeInboundMeasurementConfirm()" aria-label="关闭">&times;</button></div>
<div class="topup-body"><p class="inbound-measurement-notice"><i class="fas fa-circle-info"></i> 请确认是否接受后台测量结果?确认后将按测量结果计费,且不可修改。</p></div>
<div class="topup-foot"><button type="button" class="topup-cancel" onclick="closeInboundMeasurementConfirm()">暂不确认</button><button type="button" class="topup-confirm" onclick="confirmInboundMeasurement()">确认</button></div>
</div>
</div>
<div class="topup-overlay hidden" id="inbound-batch-order-modal" onclick="if(event.target===this)closeInboundBatchOrder()">
<div class="topup-modal inbound-batch-modal" role="dialog" aria-modal="true" aria-labelledby="inbound-batch-order-title">
<div class="topup-head"><h3 id="inbound-batch-order-title">批量下单</h3><button class="topup-close" type="button" onclick="closeInboundBatchOrder()" aria-label="关闭">&times;</button></div>
<div class="topup-body">
<p class="inbound-batch-intro">请先下载模板并按字段要求填写。带 * 的字段为必填项,必填字段已排列在各工作表前方。</p>
<div class="inbound-batch-template">
<div class="inbound-batch-template-copy"><strong>头程批量下单模板</strong><span>包含订单信息、商品信息、箱子信息 3 个工作表</span></div>
<button type="button" class="inbound-batch-download" onclick="downloadInboundBatchTemplate()"><i class="fas fa-file-arrow-down"></i>下载模板</button>
</div>
<label class="inbound-batch-upload" id="inbound-batch-order-upload" for="inbound-batch-order-file" ondragover="handleInboundBatchDragOver(event)" ondragleave="handleInboundBatchDragLeave(event)" ondrop="handleInboundBatchDrop(event)">
<input type="file" id="inbound-batch-order-file" accept=".xlsx,.xls" hidden onchange="handleInboundBatchFile(this.files[0])">
<span><i class="fas fa-cloud-arrow-up"></i><strong>点击或拖拽上传 Excel 文件</strong><span>支持 .xlsx、.xls,文件不超过 10MB</span></span>
</label>
<div class="inbound-batch-file" id="inbound-batch-order-file-state">
<div class="inbound-batch-file-main"><i class="fas fa-file-excel"></i><div><span class="inbound-batch-file-name" id="inbound-batch-order-file-name"></span><span class="inbound-batch-file-summary" id="inbound-batch-order-file-summary"></span></div></div>
<button type="button" class="inbound-batch-remove" onclick="resetInboundBatchFile()" aria-label="移除已上传文件"><i class="fas fa-xmark"></i></button>
</div>
<p class="inbound-batch-message" id="inbound-batch-order-message" role="alert" aria-live="polite"></p>
</div>
<div class="topup-foot"><button type="button" class="topup-cancel" onclick="closeInboundBatchOrder()">取消</button><button type="button" class="topup-confirm" id="inbound-batch-order-submit" onclick="submitInboundBatchOrder()" disabled>提交订单</button></div>
</div>
</div>
<div class="sea-edit-overlay" id="sea-order-edit-modal" hidden onclick="if(event.target===this)closeSeaOrderEditor()">
<div class="sea-edit-dialog" role="dialog" aria-modal="true" aria-labelledby="sea-order-edit-title">
<div class="sea-edit-header"><h2 id="sea-order-edit-title">编辑订单</h2><button class="sea-edit-close" type="button" onclick="closeSeaOrderEditor()" aria-label="关闭"><i class="fas fa-times"></i></button></div>
<div class="sea-edit-body"><form id="sea-order-edit-form" class="space-y-4"></form></div>
<div class="sea-edit-footer"><button type="button" class="px-5 py-2 rounded-lg border border-gray-200 text-gray-600" onclick="closeSeaOrderEditor()">取消</button><button type="submit" form="sea-order-edit-form" class="btn-primary text-white font-semibold px-5 py-2 rounded-lg">保存修改</button></div>
</div>
</div>
<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();
}
const BUILTIN_VIEWS = ['Home', 'Procurement', 'Truck', 'Pallet', 'Orders'];
const seaOrders = {
Procurement: [{ id:'proc-demo-1', orderNo:'HC202608120001', customer:'王经理', summary:'家居用品', createdAt:'2026-08-12 09:20', status:'处理中', fields:{'采购品类':'家居用品','商品规格':'白色 / 45×35×28cm','商品链接':'https://example.com/product','采购数量':'120','预算单价($)':'8.50','收货地址':'洛杉矶海外仓 A 区','联系人':'王经理','联系电话':'13800000000','订单备注':'采购完成后统一送仓','国内快递公司':'顺丰速运','国内运单号':'SF1234567890','海外快递公司':'UPS','海外运单号':'1Z999AA10123456784'} }],
Truck: [{ id:'truck-demo-1', orderNo:'HC202608120002', customer:'李经理', summary:'洛杉矶港 → 安大略仓', createdAt:'2026-08-12 10:05', status:'待确认', carrier:'待分配', fields:{'运输类型':'港口提柜','起运地':'洛杉矶港','目的地':'安大略仓','起运详细地址':'Pier 400','目的详细地址/码头/仓号':'Ontario WH-02','柜型':'40HQ','件数':'680','总重量(kg)':'12600','总体积(m³)':'62','预算总价':'3800','联系人':'李经理','联系方式':'13900000000','要求时间':'2026-08-16'} }],
Pallet: [{ id:'pallet-demo-1', orderNo:'HC202608120003', customer:'陈经理', summary:'工业市 → 达拉斯', createdAt:'2026-08-12 11:30', status:'处理中', carrier:'美西拼载运输', fields:{'运输方式':'拼车','是否打盘':'是','运输类型':'仓到仓','起运地':'工业市','目的地':'达拉斯','起运详细地址':'Industry WH-08','目的详细地址/码头/仓号':'Dallas WH-03','柜型':'LTL','件数':'48','总重量(kg)':'920','总体积(m³)':'6.8','预算总价':'1500','联系人':'陈经理','联系方式':'13700000000','要求时间':'2026-08-18'} }]
};
const seaPageState = { Procurement: { orderNo:'', customer:'', status:'' }, Truck: { orderNo:'', customer:'', status:'' }, Pallet: { orderNo:'', customer:'', status:'' } };
const seaPaginationState = {
Procurement: { currentPage: 1, pageSize: 10 },
Truck: { currentPage: 1, pageSize: 10 },
Pallet: { currentPage: 1, pageSize: 10 }
};
let editingSeaOrder = null;
function seaBusinessSlug(type) { return {Procurement:'procurement',Truck:'truck',Pallet:'pallet'}[type] || 'procurement'; }
function seaBusinessName(type) { return {Procurement:'海外采购',Truck:'美国本土卡',Pallet:'美本土散板'}[type] || '海外采购'; }
function escapeSeaOrderHtml(value) { return String(value == null ? '' : value).replace(/[&<>"']/g, char => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char])); }
const INBOUND_ORDER_SEED = [
{ id:'inbound-demo-1', orderNo:'RK202608270001', inboundNo:'RK202608270001', warehouse:'义乌仓', channel:'美线普货专线', customer:'张先生 / 13800000001', createdAt:'2026-08-27 09:20', paymentStatus:'已付款', shippingStatus:'未出货', status:'待确认', destinationType:'FBA仓', destinationWarehouse:'ONT8', boxes:12, carrier:'顺丰速运', expressNo:'SF1568000123', skus:'CC-LED-01, CC-LED-02', grossWeight:'186.4 kg', netWeight:'172.8 kg', volume:'1.26 m³', quantity:480, unitPrice:'$0.85/kg', billableVolume:'1.26 m³', billableWeight:'186.4 kg', remark:'普货,按预约入仓', shipmentNo:'FBA15K8Q2', trackingNo:'1Z999AA10123456784', receivedAt:'2026-08-27 08:45', businessDate:'2026-08-27', measurementConfirmed:false, customsDocs:[{name:'商业发票-示例.xlsx',size:'24 KB',uploadedAt:'2026-08-27 21:03'}], nodeTimes:{approved:'2026-08-27 10:53:57',shippingData:'2026-08-27 21:01:19',confirm:'2026-08-27 21:01:24',received:'-',loaded:'-'}, containerNodes:[{name:'国内报关完成',status:'done',time:'2026-07-22 11:13'},{name:'预计开船',status:'done',time:'2026-07-20'},{name:'实际开船',status:'pending',time:'-'},{name:'预计到港',status:'done',time:'2026-08-05'},{name:'国外清关完成',status:'pending',time:'-'},{name:'实际到港',status:'pending',time:'-'},{name:'等待提柜',status:'pending',time:'-'},{name:'预约提柜',status:'done',time:'2026-08-10 09:00'},{name:'卸柜',status:'done',time:'2026-08-12 16:30'}], measurement:{totalBoxes:12,totalGrossWeight:'186.4 kg',totalVolume:'1.26 m³',totalMaterialWeight:'172.8 kg',billableVolume:'1.26 m³',billableWeight:'186.4 kg',rows:[{length:52,width:38,height:34,boxes:6,grossWeight:'15.8 kg',volume:'0.067 m³',perimeter:'180 cm',totalGross:'94.8 kg',totalVolume:'0.402 m³',material:'0.402 m³',remark:'标准箱'},{length:60,width:42,height:36,boxes:6,grossWeight:'15.266 kg',volume:'0.091 m³',perimeter:'204 cm',totalGross:'91.6 kg',totalVolume:'0.858 m³',material:'0.858 m³',remark:'加固包装'}]} },
{ id:'inbound-demo-2', orderNo:'RK202608260002', inboundNo:'RK202608260002', warehouse:'深圳仓', channel:'美线快线', customer:'李女士 / 13800000002', createdAt:'2026-08-26 14:10', paymentStatus:'部分付款', shippingStatus:'出货中', status:'处理中', destinationType:'海外仓', destinationWarehouse:'洛杉矶海外仓 A 区', boxes:8, carrier:'京东物流', expressNo:'JDVA006520481', skus:'CC-BAG-12, CC-BAG-18', grossWeight:'96.8 kg', netWeight:'88.2 kg', volume:'0.74 m³', quantity:320, unitPrice:'$1.10/kg', billableVolume:'0.74 m³', billableWeight:'96.8 kg', remark:'优先安排航班', shipmentNo:'WH-LAX-2602', trackingNo:'1Z999AA10123456785', receivedAt:'2026-08-26 13:30', businessDate:'2026-08-26' },
{ id:'inbound-demo-3', orderNo:'RK202608250003', inboundNo:'RK202608250003', warehouse:'青岛仓', channel:'美线FBA直送', customer:'王经理 / 13800000003', createdAt:'2026-08-25 11:35', paymentStatus:'已付款', shippingStatus:'已出货', status:'已完成', destinationType:'FBA仓', destinationWarehouse:'LGB8', boxes:18, carrier:'德邦快递', expressNo:'DPK20260825003', skus:'CC-HOME-33, CC-HOME-41', grossWeight:'264.6 kg', netWeight:'243.0 kg', volume:'1.88 m³', quantity:720, unitPrice:'$0.92/kg', billableVolume:'1.88 m³', billableWeight:'264.6 kg', remark:'已完成入仓与出货', shipmentNo:'FBA17P3M6', trackingNo:'1Z999AA10123456786', receivedAt:'2026-08-25 10:20', businessDate:'2026-08-25' },
{ id:'inbound-demo-4', orderNo:'RK202608240004', inboundNo:'RK202608240004', warehouse:'上海仓', channel:'美线敏感货专线', customer:'陈小姐 / 13800000004', createdAt:'2026-08-24 16:40', paymentStatus:'未付款', shippingStatus:'未出货', status:'已取消', destinationType:'私人仓', destinationWarehouse:'达拉斯私人仓', boxes:6, carrier:'中通快递', expressNo:'ZT20260824004', skus:'CC-BEAUTY-07', grossWeight:'58.2 kg', netWeight:'51.6 kg', volume:'0.42 m³', quantity:180, unitPrice:'$1.35/kg', billableVolume:'0.42 m³', billableWeight:'58.2 kg', remark:'客户取消订单', shipmentNo:'-', trackingNo:'-', receivedAt:'-', businessDate:'2026-08-24' }
];
let inboundFilteredOrders = null;
function escapeInboundHtml(value) { return String(value == null ? '' : value).replace(/[&<>"']/g, char => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char])); }
function inboundStatusClass(status) { return status === '待处理' || status === '待确认' ? 'status-pending' : status === '处理中' ? 'status-processing' : status === '已完成' || status === '已确认' ? 'status-done' : 'status-rejected'; }
function inboundOrderById(id) { return INBOUND_ORDER_SEED.find(order => !id || order.id === id) || INBOUND_ORDER_SEED[0]; }
function inboundMeasureStats(measurement) {
const m = measurement || {};
return [['测量总箱数', m.totalBoxes || '-'], ['测量总毛重', m.totalGrossWeight || '-'], ['测量总体积', m.totalVolume || '-'], ['测量总材积/重量', m.totalMaterialWeight || '-'], ['计费体积', m.billableVolume || '-'], ['计费重量', m.billableWeight || '-']];
}
function renderInboundMeasureSummary(targetId, measurement) {
const target = document.getElementById(targetId); if (!target) return;
target.innerHTML = inboundMeasureStats(measurement).map(item => '<div class="inbound-measure-stat"><span>' + item[0] + '</span><strong>' + escapeInboundHtml(item[1]) + '</strong></div>').join('');
}
function renderInboundMeasureRows(targetId, measurement) {
const target = document.getElementById(targetId); if (!target) return;
const rows = (measurement && measurement.rows) || [];
target.innerHTML = rows.length ? rows.map((row,index) => '<tr><td>' + (index + 1) + '</td><td>' + escapeInboundHtml(row.length) + '</td><td>' + escapeInboundHtml(row.width) + '</td><td>' + escapeInboundHtml(row.height) + '</td><td>' + escapeInboundHtml(row.boxes) + '</td><td>' + escapeInboundHtml(row.grossWeight) + '</td><td>' + escapeInboundHtml(row.volume) + '</td><td>' + escapeInboundHtml(row.perimeter) + '</td><td>' + escapeInboundHtml(row.totalGross) + '</td><td>' + escapeInboundHtml(row.totalVolume) + '</td><td>' + escapeInboundHtml(row.material) + '</td><td>' + escapeInboundHtml(row.remark || '-') + '</td></tr>').join('') : '<tr><td colspan="12" class="sea-order-empty">暂无测量明细</td></tr>';
}
function switchInboundDetailTab(tab) {
const tabs = document.querySelectorAll('#page-inbound-detail .inbound-detail-tab');
const panels = document.querySelectorAll('#page-inbound-detail .inbound-detail-panel');
tabs.forEach(button => { const active = button.dataset.tab === tab; button.classList.toggle('active', active); button.setAttribute('aria-selected', active ? 'true' : 'false'); });
panels.forEach(panel => { const active = panel.id === 'inbound-panel-' + tab; panel.classList.toggle('active', active); panel.hidden = !active; });
}
// ===== Shared business detail tabs =====
function switchBusinessDetailTab(pageId, tab) {
const root = document.getElementById(pageId);
if (!root) return;
const tabs = Array.from(root.querySelectorAll('.business-detail-tab'));
const panels = Array.from(root.querySelectorAll('.business-detail-panel'));
if (!tabs.some(button => button.dataset.detailTab === tab)) return;
tabs.forEach(button => { const active = button.dataset.detailTab === tab; button.classList.toggle('active', active); button.setAttribute('aria-selected', active ? 'true' : 'false'); button.setAttribute('tabindex', active ? '0' : '-1'); });
panels.forEach(panel => { const active = panel.dataset.detailPanel === tab; panel.classList.toggle('active', active); panel.hidden = !active; });
}
function handleBusinessDetailTabKeydown(event, pageId) {
const supportedKeys = ['ArrowLeft', 'ArrowRight', 'Home', 'End'];
if (!supportedKeys.includes(event.key)) return;
const root = document.getElementById(pageId);
if (!root) return;
const tabs = Array.from(root.querySelectorAll('.business-detail-tab'));
const currentIndex = tabs.indexOf(event.currentTarget);
if (currentIndex < 0 || !tabs.length) return;
event.preventDefault();
let nextIndex = event.key === 'Home' ? 0 : event.key === 'End' ? tabs.length - 1 : currentIndex + (event.key === 'ArrowRight' ? 1 : -1);
nextIndex = (nextIndex + tabs.length) % tabs.length;
const target = tabs[nextIndex];
switchBusinessDetailTab(pageId, target.dataset.detailTab);
target.focus();
}
// ===== End shared business detail tabs =====
let inboundDetailOrderId = null;
function inboundFileSize(bytes) {
if (!Number.isFinite(bytes)) return '-';
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
function renderInboundContainerNodes(nodes) {
const target = document.getElementById('inbound-detail-container-nodes'); if (!target) return;
const statusText = { done:'已完成', current:'进行中', pending:'待更新' };
const defaultNodes = ['国内报关完成','预计开船','实际开船','预计到港','国外清关完成','实际到港','等待提柜','预约提柜','卸柜'].map(name => ({ name, status:'pending', time:'-' }));
const safeNodes = Array.isArray(nodes) && nodes.length ? nodes : defaultNodes;
target.innerHTML = safeNodes.map((node, index) => {
const status = ['done','current','pending'].includes(node.status) ? node.status : 'pending';
const marker = status === 'done' ? '✓' : status === 'current' ? '•' : index + 1;
return '<div class="inbound-container-node is-' + status + '"><span class="inbound-container-node-marker" aria-hidden="true">' + marker + '</span><div><div class="inbound-container-node-name">' + escapeInboundHtml(node.name || '-') + '</div><div class="inbound-container-node-time">' + escapeInboundHtml(node.time || '-') + '</div></div><span class="inbound-container-node-status">' + statusText[status] + '</span></div>';
}).join('');
}
function renderInboundCustomsDocs(order) {
const target = document.getElementById('inbound-customs-files'); if (!target) return;
const docs = (order && order.customsDocs) || [];
target.innerHTML = docs.length ? docs.map(doc => '<div class="inbound-customs-file"><div class="inbound-customs-file-main"><i class="fas fa-file-lines"></i><span class="inbound-customs-file-name" title="' + escapeInboundHtml(doc.name) + '">' + escapeInboundHtml(doc.name) + '</span></div><span class="inbound-customs-file-meta">' + escapeInboundHtml(doc.size || '-') + ' · ' + escapeInboundHtml(doc.uploadedAt || '-') + '</span></div>').join('') : '<div class="inbound-customs-empty">暂无已上传报关资料</div>';
}
function renderInboundOrderInfo(order) {
const target = document.getElementById('inbound-detail-order-info'); if (!target) return;
const value = key => key === 'orderType' ? (order && order.orderType || '头程订单') : (order && order[key] ? order[key] : '—');
const columns = [
[['入仓码','inboundNo'],['开船时间','sailingAt'],['到港时间','arrivedAt'],['提货方式','pickupMethod'],['提货时间','pickupAt'],['提货联系人电话','pickupContactPhone'],['提货地址','pickupAddress'],['提货邮编','pickupPostalCode'],['提货联系人邮箱','pickupContactEmail'],['预计到货时间','estimatedArrivalAt'],['货物到仓时间','cargoReceivedAt']],
[['订单类型','orderType'],['渠道','channel'],['集装箱号','containerNo'],['目的仓类型','destinationType'],['目的仓','destinationWarehouse'],['邮编','destinationPostalCode'],['联系人','contactName'],['联系人电话','contactPhone'],['是否整柜','fullContainer'],['商检验','inspection']],
[['买单/退税','customsMode'],['总箱数','boxes'],['总毛重(T.K.GW)','grossWeight'],['总净重(T.K.NW)','netWeight'],['总体积(m³)','volume'],['计费毛重','billableWeight'],['计费体积(m³)','billableVolume'],['预估重量','estimatedWeight'],['创建时间','createdAt'],['备注','remark']]
];
target.innerHTML = columns.map(column => '<div class="inbound-order-info-column">' + column.map(item => inboundDetailField(item[0], value(item[1]))).join('') + '</div>').join('');
}
function renderInboundProductItems(order) {
const body = document.getElementById('inbound-detail-product-rows');
const count = document.getElementById('inbound-detail-product-count');
if (!body) return;
let items = order && Array.isArray(order.productItems) ? order.productItems : [];
if (!items.length) items = String(order && order.skus || '').split(/[,,]/).map(sku => sku.trim()).filter(Boolean).map(skuNo => ({ skuNo }));
if (count) count.textContent = '共 ' + items.length + ' 条';
if (!items.length) { body.innerHTML = '<tr><td colspan="21" class="inbound-product-empty">暂无 SKU 明细</td></tr>'; return; }
const fields = ['skuNo','shipmentNo','trackingNo','upcNo','chineseName','image','englishName','specification','quantityPerBox','weightPerBox','volumePerBox','boxes','material','purpose','model','brand','customsHsCode','clearanceHsCode','declaredUnitPrice','declaredAmount','remark'];
body.innerHTML = items.map(item => {
const cells = fields.map(key => {
if (key === 'image') {
const source = String(item.image || '').trim();
const allowed = /^(https?:\/\/|data:image\/|\/)/i.test(source);
const alt = escapeInboundHtml(item.chineseName || item.englishName || item.skuNo || '商品图片');
return '<td>' + (allowed ? '<img class="inbound-product-image" src="' + escapeInboundHtml(source) + '" alt="' + alt + '">' : '—') + '</td>';
}
const value = item[key] === 0 ? '0' : item[key] || '—';
return '<td title="' + escapeInboundHtml(value) + '">' + escapeInboundHtml(value) + '</td>';
});
return '<tr>' + cells.join('') + '</tr>';
}).join('');
}
function renderInboundFees(order) {
const target = document.getElementById('inbound-detail-fees'); if (!target) return;
const value = key => order && order[key] ? order[key] : '—';
const rows = [['计费单价','unitPrice'],['计费体积','billableVolume'],['计费重量','billableWeight'],['费用总计','totalFee'],['费用状态','paymentStatus']];
target.innerHTML = rows.map(item => '<div class="inbound-fee-row"><span>' + item[0] + '</span><strong>' + escapeInboundHtml(value(item[1])) + '</strong></div>').join('');
}
function handleInboundCustomsUpload(input) {
const order = inboundOrderById(inboundDetailOrderId); if (!order || !input) return;
const files = Array.from(input.files || []); const maxBytes = 20 * 1024 * 1024;
order.customsDocs = order.customsDocs || [];
files.forEach(file => { if (file.size > maxBytes) { if (typeof showToast === 'function') showToast('文件超过20MB,未上传:' + file.name); return; } order.customsDocs.unshift({ name:file.name, size:inboundFileSize(file.size), uploadedAt:new Date().toLocaleString('zh-CN', { hour12:false }).replace(/\//g,'-') }); });
input.value = ''; renderInboundCustomsDocs(order);
if (files.length && typeof showToast === 'function') showToast('报关资料已上传');
}
function downloadInboundTaxDocs() {
const order = inboundOrderById(inboundDetailOrderId); if (!order) return;
const content = ['报关退税资料', '订单号:' + order.orderNo, '入仓码:' + order.inboundNo, '目的仓:' + order.destinationWarehouse, '生成时间:' + new Date().toLocaleString('zh-CN', { hour12:false })].join('\n');
const blob = new Blob([content], { type:'text/plain;charset=utf-8' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = '报关退税资料-' + order.orderNo + '.txt'; document.body.appendChild(link); link.click(); link.remove(); setTimeout(() => URL.revokeObjectURL(url), 0);
if (typeof showToast === 'function') showToast('报关退税资料已开始下载');
}
function inboundDetailField(label, value) { return '<div class="inbound-detail-field"><dt>' + label + '</dt><dd>' + escapeInboundHtml(value || '-') + '</dd></div>'; }
function showInboundOrderDetail(id, initialTab) {
const order = inboundOrderById(id); if (!order) return;
switchPage('inbound-detail');
inboundDetailOrderId = order.id; switchInboundDetailTab(initialTab || 'order');
document.getElementById('inbound-detail-order-no').textContent = order.orderNo;
document.getElementById('inbound-node-created').textContent = order.createdAt || '-';
const nodeTimes = order.nodeTimes || {};
[['inbound-node-approved','approved'],['inbound-node-shipping-data','shippingData'],['inbound-node-confirm','confirm'],['inbound-node-received','received'],['inbound-node-loaded','loaded']].forEach(([id,key])=>{ const el=document.getElementById(id); if(el) el.textContent=nodeTimes[key] || '-'; });
if (typeof renderInboundOrderInfo === 'function') renderInboundOrderInfo(order);
if (typeof renderInboundProductItems === 'function') renderInboundProductItems(order);
renderInboundMeasureSummary('inbound-detail-measure-summary', order.measurement); renderInboundMeasureRows('inbound-detail-measure-rows', order.measurement);
if (typeof renderInboundFees === 'function') renderInboundFees(order);
renderInboundCustomsDocs(order);
if (typeof renderInboundContainerNodes === 'function') renderInboundContainerNodes(order.containerNodes);
const confirmButton=document.getElementById('inbound-detail-confirm-btn'); if(confirmButton) { confirmButton.hidden = false; confirmButton.disabled = !!order.measurementConfirmed; confirmButton.textContent = order.measurementConfirmed ? '已确认' : '确认测量数据'; }
}
function showInboundList() { switchPage('inbound'); renderInboundOrders(); }
let inboundMeasurementOrderId = null;
function openInboundMeasurementConfirm(id) {
const order = inboundOrderById(id || inboundMeasurementOrderId); if (!order || !order.measurement) return;
inboundMeasurementOrderId = order.id;
const modal = document.getElementById('inbound-measurement-modal'); if (modal) { modal.classList.remove('hidden'); document.body.style.overflow='hidden'; }
}
function closeInboundMeasurementConfirm() { const modal=document.getElementById('inbound-measurement-modal'); if(modal) modal.classList.add('hidden'); document.body.style.overflow=''; inboundMeasurementOrderId=null; }
function getInboundBatchTemplateSheets() {
return [
{ name:'订单信息', headers:['批次编号*','取货方式*','目的仓类型*','收货仓*','渠道*','买单/退税*','提货时间*','提货地址*','预计到达时间*','FBA仓编码*','海外仓*','LX入库单号*','收件人姓名*','收件人电话*','收件国家*','邮编*','州/省*','城市*','收件地址*','选择地址','提货联系人','提货联系电话','车牌号','司机姓名','司机电话','快递单号','开船时间','到港时间','期望到达时间','订单备注'] },
{ name:'商品信息', headers:['批次编号*','SKU NO.*','中文品名','图片链接','英文品名','货件编号','跟踪编号','UPC NO.','报关 HS CODE','清关 HS CODE','清关申报单价 USD','清关申报金额 USD','材质','用途','型号','品牌','产品规格 长 x 宽 x 高','商品备注'] },
{ name:'箱子信息', headers:['批次编号*','关联 SKU*','箱数*','箱规长(cm)','箱规宽(cm)','箱规高(cm)','每箱数量','总数量','总毛重(kg)','总净重(kg)','总体积(m³)'] }
];
}
function validateInboundBatchWorkbookData(workbookData) {
const templateSheets = getInboundBatchTemplateSheets();
const counts = [];
for (const template of templateSheets) {
const matrix = workbookData && workbookData[template.name];
if (!Array.isArray(matrix) || !matrix.length) return { ok:false, message:'缺少工作表「' + template.name + '」' };
const headers = matrix[0].map(value => String(value || '').trim());
const missing = template.headers.filter(header => !headers.includes(header));
if (missing.length) return { ok:false, message:'工作表「' + template.name + '」缺少字段:' + missing.join('、') };
const rows = matrix.slice(1).filter(row => row.some(value => String(value == null ? '' : value).trim()));
if (!rows.length) return { ok:false, message:'工作表「' + template.name + '」至少需要填写 1 行数据' };
const universallyRequired = template.name === '订单信息' ? template.headers.slice(0, 6) : template.headers.filter(header => header.endsWith('*'));
for (let rowIndex = 0; rowIndex < rows.length; rowIndex += 1) {
for (const header of universallyRequired) {
const columnIndex = headers.indexOf(header);
if (!String(rows[rowIndex][columnIndex] == null ? '' : rows[rowIndex][columnIndex]).trim()) return { ok:false, message:'工作表「' + template.name + '」第 ' + (rowIndex + 2) + ' 行缺少必填字段「' + header.replace(/\*$/, '') + '」' };
}
if (template.name === '订单信息') {
const rowValue = header => String(rows[rowIndex][headers.indexOf(header)] == null ? '' : rows[rowIndex][headers.indexOf(header)]).trim();
const pickupMethod = rowValue('取货方式*');
const destinationType = rowValue('目的仓类型*');
let conditionalRequired = [];
if (pickupMethod.includes('我司提货')) conditionalRequired.push('提货时间*', '提货地址*');
if (pickupMethod.includes('自送货')) conditionalRequired.push('预计到达时间*');
if (destinationType === 'FBA仓') conditionalRequired.push('FBA仓编码*');
if (destinationType === '海外仓') conditionalRequired.push('海外仓*', '收件人姓名*', '收件人电话*', '收件国家*', '收件地址*');
if (destinationType === '私人仓') conditionalRequired.push('收件人姓名*', '收件人电话*', '收件国家*', '邮编*', '州/省*', '城市*', '收件地址*');
if (destinationType === '一件代发') conditionalRequired.push('LX入库单号*');
const missingConditional = conditionalRequired.find(header => !rowValue(header));
if (missingConditional) return { ok:false, message:'工作表「订单信息」第 ' + (rowIndex + 2) + ' 行缺少必填字段「' + missingConditional.replace(/\*$/, '') + '」' };
}
}
counts.push(rows.length);
}
return { ok:true, counts };
}
function createInboundOrdersFromBatchData(workbookData, nowValue) {
const now = nowValue instanceof Date ? nowValue : new Date(nowValue || Date.now());
const rowsBySheet = name => {
const matrix = workbookData[name] || []; const headers = matrix[0] || [];
return matrix.slice(1).filter(row => row.some(value => String(value == null ? '' : value).trim())).map(row => {
const value = {}; headers.forEach((header, index) => { value[header] = row[index]; }); return value;
});
};
const orderRows = rowsBySheet('订单信息'); const productRows = rowsBySheet('商品信息'); const boxRows = rowsBySheet('箱子信息');
const pad = value => String(value).padStart(2, '0');
const timestamp = now.getFullYear() + '-' + pad(now.getMonth() + 1) + '-' + pad(now.getDate()) + ' ' + pad(now.getHours()) + ':' + pad(now.getMinutes());
const dateCode = String(now.getFullYear()) + pad(now.getMonth() + 1) + pad(now.getDate());
const numberSum = (rows, field) => rows.reduce((sum, row) => sum + (Number(row[field]) || 0), 0);
return orderRows.map((row, index) => {
const batchId = String(row['批次编号*'] || '').trim();
const products = productRows.filter(item => String(item['批次编号*'] || '').trim() === batchId);
const boxes = boxRows.filter(item => String(item['批次编号*'] || '').trim() === batchId);
const destinationType = String(row['目的仓类型*'] || '').trim();
const destinationWarehouse = destinationType === 'FBA仓' ? row['FBA仓编码*'] : destinationType === '海外仓' ? row['海外仓*'] : destinationType === '一件代发' ? row['LX入库单号*'] : row['收件地址*'];
const grossWeight = numberSum(boxes, '总毛重(kg)'); const netWeight = numberSum(boxes, '总净重(kg)'); const volume = numberSum(boxes, '总体积(m³)');
return {
id:'inbound-batch-' + now.getTime() + '-' + index, orderNo:'RK' + dateCode + pad(index + 1), inboundNo:'RK' + dateCode + pad(index + 1), warehouse:String(row['收货仓*'] || ''), channel:String(row['渠道*'] || ''), customer:String(row['提货联系人'] || row['收件人姓名*'] || '-'), createdAt:timestamp, paymentStatus:'未付款', shippingStatus:'未出货', status:'待处理', destinationType, destinationWarehouse:String(destinationWarehouse || '-'), lxInboundNo:String(row['LX入库单号*'] || ''), boxes:numberSum(boxes, '箱数*'), carrier:'-', expressNo:String(row['快递单号'] || '-'), skus:products.map(item => String(item['SKU NO.*'] || '').trim()).filter(Boolean).join(', '), grossWeight:grossWeight + ' kg', netWeight:netWeight + ' kg', volume:volume + ' m³', quantity:numberSum(boxes, '总数量'), unitPrice:'-', billableVolume:'-', billableWeight:'-', remark:String(row['订单备注'] || '-'), shipmentNo:products.map(item=>String(item['货件编号'] || '').trim()).filter(Boolean).join(', ') || '-', trackingNo:products.map(item=>String(item['跟踪编号'] || '').trim()).filter(Boolean).join(', ') || '-', receivedAt:'-', businessDate:timestamp.slice(0,10), pickupMethod:String(row['取货方式*'] || ''), pickupAt:String(row['提货时间*'] || ''), pickupAddress:String(row['提货地址*'] || ''), customsMode:String(row['买单/退税*'] || ''), productItems:products.map(item=>({skuNo:item['SKU NO.*'],chineseName:item['中文品名'],image:item['图片链接'],englishName:item['英文品名'],shipmentNo:item['货件编号'],trackingNo:item['跟踪编号'],upcNo:item['UPC NO.'],customsHsCode:item['报关 HS CODE'],clearanceHsCode:item['清关 HS CODE'],declaredUnitPrice:item['清关申报单价 USD'],declaredAmount:item['清关申报金额 USD'],material:item['材质'],purpose:item['用途'],model:item['型号'],brand:item['品牌'],specification:item['产品规格 长 x 宽 x 高'],remark:item['商品备注']}))
};
});
}
let inboundBatchOrderData = null;
let inboundBatchOrderReturnFocus = null;
function inboundBatchMessage(message) { const target=document.getElementById('inbound-batch-order-message'); if(target) target.textContent=message || ''; }
function openInboundBatchOrder() {
const modal=document.getElementById('inbound-batch-order-modal'); if(!modal) return;
inboundBatchOrderReturnFocus=document.activeElement; resetInboundBatchFile(); modal.classList.remove('hidden'); document.body.style.overflow='hidden';
setTimeout(()=>modal.querySelector('.topup-close')?.focus(), 0);
}
function closeInboundBatchOrder() {
const modal=document.getElementById('inbound-batch-order-modal'); if(modal) modal.classList.add('hidden');
document.body.style.overflow=''; inboundBatchOrderReturnFocus?.focus?.(); inboundBatchOrderReturnFocus=null;
}
function downloadInboundBatchTemplate() {
if (typeof XLSX === 'undefined') { inboundBatchMessage('模板组件加载失败,请刷新页面后重试'); return; }
const workbook=XLSX.utils.book_new();
getInboundBatchTemplateSheets().forEach(template=>{
const worksheet=XLSX.utils.aoa_to_sheet([template.headers]);
worksheet['!cols']=template.headers.map(header=>({wch:Math.max(14, Math.min(28, header.length * 2))}));
XLSX.utils.book_append_sheet(workbook, worksheet, template.name);
});
XLSX.writeFile(workbook, '头程批量下单模板.xlsx'); inboundBatchMessage('模板已下载,请填写后上传');
}
function resetInboundBatchFile() {
inboundBatchOrderData=null;
const input=document.getElementById('inbound-batch-order-file'); if(input) input.value='';
document.getElementById('inbound-batch-order-file-state')?.classList.remove('is-visible');
const submit=document.getElementById('inbound-batch-order-submit'); if(submit) submit.disabled=true;
inboundBatchMessage('');
}
function readInboundBatchWorkbook(file) {
return file.arrayBuffer().then(buffer=>{
const workbook=XLSX.read(buffer, {type:'array'}); const data={};
workbook.SheetNames.forEach(name=>{ data[name]=XLSX.utils.sheet_to_json(workbook.Sheets[name], {header:1, defval:'', blankrows:false}); });
return data;
});
}
async function handleInboundBatchFile(file) {
resetInboundBatchFile(); if(!file) return;
if (!/\.(xlsx|xls)$/i.test(file.name)) { inboundBatchMessage('文件格式不正确,请上传 .xlsx 或 .xls 文件'); return; }
if (file.size > 10 * 1024 * 1024) { inboundBatchMessage('文件超过 10MB,请压缩数据后重新上传'); return; }
if (typeof XLSX === 'undefined') { inboundBatchMessage('文件解析组件加载失败,请刷新页面后重试'); return; }
inboundBatchMessage('正在校验文件...');
try {
const data=await readInboundBatchWorkbook(file); const result=validateInboundBatchWorkbookData(data);
if(!result.ok) { inboundBatchMessage(result.message); return; }
inboundBatchOrderData=data;
document.getElementById('inbound-batch-order-file-name').textContent=file.name;
document.getElementById('inbound-batch-order-file-summary').textContent='订单 ' + result.counts[0] + ' 条 · 商品 ' + result.counts[1] + ' 条 · 箱规 ' + result.counts[2] + ' 条';
document.getElementById('inbound-batch-order-file-state')?.classList.add('is-visible');
const submit=document.getElementById('inbound-batch-order-submit'); if(submit) submit.disabled=false;
inboundBatchMessage('文件校验通过,可以提交订单');
} catch (error) { inboundBatchMessage('文件无法读取,请使用下载的模板重新填写'); }
}
function handleInboundBatchDragOver(event) { event.preventDefault(); event.currentTarget.classList.add('is-dragover'); }
function handleInboundBatchDragLeave(event) { event.currentTarget.classList.remove('is-dragover'); }
function handleInboundBatchDrop(event) { event.preventDefault(); event.currentTarget.classList.remove('is-dragover'); handleInboundBatchFile(event.dataTransfer.files[0]); }
function submitInboundBatchOrder() {
if(!inboundBatchOrderData) { inboundBatchMessage('请先上传并通过校验的 Excel 文件'); return; }
const orders=createInboundOrdersFromBatchData(inboundBatchOrderData); INBOUND_ORDER_SEED.unshift(...orders); inboundFilteredOrders=null; renderInboundOrders(); closeInboundBatchOrder(); resetInboundBatchFile();
const message='已成功提交 ' + orders.length + ' 条订单'; if(typeof showToast==='function') showToast(message); else alert(message);
}
document.addEventListener('keydown', event=>{
if(event.key==='Escape' && !document.getElementById('inbound-batch-order-modal')?.classList.contains('hidden')) closeInboundBatchOrder();
});
function confirmInboundMeasurement() {
const order = inboundOrderById(inboundMeasurementOrderId); if (!order) return;
order.measurementConfirmed = true; order.status = '已确认'; closeInboundMeasurementConfirm(); renderInboundOrders();
if (document.getElementById('page-inbound-detail')?.classList.contains('active')) showInboundOrderDetail(order.id);
if (typeof showToast === 'function') showToast('测量数据已确认');
}
function renderInboundOrders() {
const body = document.getElementById('inbound-table-body');
if (!body) return;
const orders = inboundFilteredOrders === null ? INBOUND_ORDER_SEED : inboundFilteredOrders;
body.innerHTML = orders.length ? orders.map((order, index) => {
const cells = [
'<td>' + (index + 1) + '</td>',
'<td><input type="checkbox" aria-label="选择订单 ' + escapeInboundHtml(order.orderNo) + '"></td>',
'<td><div class="inbound-actions"><button type="button" class="inbound-action" onclick="showInboundOrderDetail(\'' + order.id + '\')">查看</button>' + (order.measurement && !order.measurementConfirmed ? '<button type="button" class="inbound-action" onclick="showInboundOrderDetail(\'' + order.id + '\',\'measure\')">确认计费重</button>' : '') + '</div></td>',
'<td>' + escapeInboundHtml(order.inboundNo) + '</td>',
'<td><span class="status-badge ' + inboundStatusClass(order.status) + '">' + escapeInboundHtml(order.status) + '</span></td>',
'<td>' + escapeInboundHtml(order.channel) + '</td>', '<td>' + escapeInboundHtml(order.destinationWarehouse) + '</td>',
'<td>' + order.boxes + '</td>', '<td>' + escapeInboundHtml(order.carrier) + '</td>', '<td>' + escapeInboundHtml(order.expressNo) + '</td>',
'<td>' + escapeInboundHtml(order.skus) + '</td>', '<td>' + escapeInboundHtml(order.grossWeight) + '</td>', '<td>' + escapeInboundHtml(order.netWeight) + '</td>',
'<td>' + escapeInboundHtml(order.volume) + '</td>', '<td>' + order.quantity + '</td>', '<td>' + escapeInboundHtml(order.unitPrice) + '</td>',
'<td>' + escapeInboundHtml(order.billableVolume) + '</td>', '<td>' + escapeInboundHtml(order.billableWeight) + '</td>', '<td>' + escapeInboundHtml(order.remark) + '</td>',
'<td>' + escapeInboundHtml(order.shipmentNo) + '</td>', '<td>' + escapeInboundHtml(order.trackingNo) + '</td>', '<td>' + escapeInboundHtml(order.receivedAt) + '</td>', '<td>' + escapeInboundHtml(order.businessDate) + '</td>'
];
return '<tr>' + cells.join('') + '</tr>';
}).join('') : '<tr class="empty-state"><td colspan="23"><div class="empty-icon">📦</div><div class="empty-text">暂无数据</div></td></tr>';
const total = document.getElementById('inbound-total');
if (total) total.textContent = orders.length;
}
function filterInboundOrders() {
const value = id => (document.getElementById(id)?.value || '').trim();
const orderNo = value('inbound-filter-order-no').toLowerCase();
const warehouse = value('inbound-filter-warehouse');
const channel = value('inbound-filter-channel');
const customer = value('inbound-filter-customer').toLowerCase();
const start = value('inbound-filter-created-start');
const end = value('inbound-filter-created-end');
const paymentStatus = value('inbound-filter-payment');
const shippingStatus = value('inbound-filter-shipping');
const status = value('inbound-filter-status');
const destinationType = value('inbound-filter-destination-type');
inboundFilteredOrders = INBOUND_ORDER_SEED.filter(order => {
const createdDate = order.createdAt.slice(0, 10);
return (!orderNo || order.orderNo.toLowerCase().includes(orderNo)) &&
(!warehouse || order.warehouse === warehouse) && (!channel || order.channel === channel) &&
(!customer || order.customer.toLowerCase().includes(customer)) && (!start || createdDate >= start) &&
(!end || createdDate <= end) && (!paymentStatus || order.paymentStatus === paymentStatus) &&
(!shippingStatus || order.shippingStatus === shippingStatus) && (!status || order.status === status) &&
(!destinationType || order.destinationType === destinationType);
});
renderInboundOrders();
}
function resetInboundOrders() {
['inbound-filter-order-no', 'inbound-filter-warehouse', 'inbound-filter-channel', 'inbound-filter-customer', 'inbound-filter-created-start', 'inbound-filter-created-end', 'inbound-filter-payment', 'inbound-filter-shipping', 'inbound-filter-status', 'inbound-filter-destination-type'].forEach(id => {
const element = document.getElementById(id);
if (element) element.value = '';
});
inboundFilteredOrders = null;
renderInboundOrders();
}
document.addEventListener('DOMContentLoaded', renderInboundOrders);
function showSeaBusinessPage(type) { const page=document.querySelector('.sea-business-page[data-business="'+type+'"]'),panel=document.querySelector('[data-order-panel="'+type+'"]');document.querySelectorAll('[data-business-detail]').forEach(detail=>detail.hidden=true);if(page)page.classList.remove('hidden');if(panel)panel.hidden=true;renderSeaOrders(type);window.scrollTo(0,0); }
function showSeaBusinessOrderForm(type) { const page=document.querySelector('.sea-business-page[data-business="'+type+'"]'),panel=document.querySelector('[data-order-panel="'+type+'"]');document.querySelectorAll('[data-business-detail]').forEach(detail=>detail.hidden=true);if(page)page.classList.add('hidden');if(panel)panel.hidden=false;window.scrollTo(0,0); }
function openSeaBusiness(type, action, el) { const link=el&&el.classList?.contains('dash-nav-link')?el:document.querySelector('.dash-nav-link[data-business="'+type+'"]');dashNavigate(type,link);if(action==='new')showSeaBusinessOrderForm(type);else showSeaBusinessPage(type); }
function seaOrderCells(type, order) { const fields=order.fields||{},total=fields['预算总价']||((Number(fields['总重量(kg)']||0)*0.3).toFixed(2));if(type==='Procurement')return [order.orderNo,fields['采购品类'],fields['商品规格'],fields['采购数量'],fields['预算单价($)']?('$'+fields['预算单价($)']):'-',order.status];if(type==='Truck')return [order.orderNo,fields['运输类型'],fields['起运地'],fields['目的地'],fields['柜型'],fields['总重量(kg)']?Number(fields['总重量(kg)']).toLocaleString():'-',total?('$'+total):'-',fields['要求时间'],order.status];return [order.orderNo,fields['运输方式'],fields['是否打盘'],fields['运输类型'],fields['起运地'],fields['目的地'],fields['总重量(kg)']?Number(fields['总重量(kg)']).toLocaleString():'-',total?('$'+total):'-',fields['要求时间'],order.status]; }
function paginateSeaOrders(orders, currentPage, pageSize) {
const total = orders.length;
const safePageSize = [10, 20, 50, 100].includes(Number(pageSize)) ? Number(pageSize) : 10;
const pages = Math.max(1, Math.ceil(total / safePageSize));
const safeCurrentPage = Math.min(Math.max(1, Number(currentPage) || 1), pages);
const start = (safeCurrentPage - 1) * safePageSize;
return { total, pages, currentPage: safeCurrentPage, pageSize: safePageSize, rows: orders.slice(start, start + safePageSize) };
}
function renderSeaOrders(type) {
const slug=seaBusinessSlug(type),body=document.getElementById('sea-'+slug+'-orders-body');
if(!body||!seaOrders[type]||!seaPageState[type]||!seaPaginationState[type])return;
const state=seaPageState[type];
const filteredRows=seaOrders[type].filter(order=>{const fields=order.fields||{},route=((fields['起运地']||'')+' '+(fields['目的地']||'')).toLowerCase();return (!state.orderNo||order.orderNo.toLowerCase().includes(state.orderNo))&&(!state.customer||route.includes(state.customer))&&(!state.status||order.status===state.status)});
const paginationState=seaPaginationState[type],pagination=paginateSeaOrders(filteredRows,paginationState.currentPage,paginationState.pageSize);
paginationState.currentPage=pagination.currentPage;
paginationState.pageSize=pagination.pageSize;
const colspan=type==='Procurement'?8:(type==='Truck'?11:12);
body.innerHTML=pagination.rows.length?pagination.rows.map((order,index)=>{const actions='<div class="sea-table-actions"><button type="button" class="sea-table-action" onclick="showSeaOrderDetail(\''+type+'\',\''+order.id+'\')">查看</button><button type="button" class="sea-table-action" onclick="openSeaOrderEditor(\''+type+'\',\''+order.id+'\')">编辑</button></div>';const cells='<td>'+((pagination.currentPage-1)*pagination.pageSize+index+1)+'</td>'+seaOrderCells(type,order).map(value=>'<td>'+escapeSeaOrderHtml(value||'-')+'</td>').join('');return '<tr>'+cells+'<td>'+actions+'</td></tr>';}).join(''):'<tr><td class="sea-order-empty" colspan="'+colspan+'">暂无'+seaBusinessName(type)+'头程订单</td></tr>';
document.getElementById('sea-'+slug+'-total').textContent=pagination.total;
document.getElementById('sea-'+slug+'-page-size').value=String(pagination.pageSize);
document.getElementById('sea-'+slug+'-page-number').textContent=pagination.currentPage+' / '+pagination.pages;
document.getElementById('sea-'+slug+'-prev').disabled=pagination.currentPage<=1;
document.getElementById('sea-'+slug+'-next').disabled=pagination.currentPage>=pagination.pages;
}
function changeSeaOrderPage(type, delta) { const state=seaPaginationState[type];if(!state)return;state.currentPage+=Number(delta)||0;renderSeaOrders(type); }
function changeSeaOrderPageSize(type, value) { const state=seaPaginationState[type];if(!state)return;state.pageSize=Number(value);state.currentPage=1;renderSeaOrders(type); }
function collectSeaOrderFields(form) { const fields={};new FormData(form).forEach((value,key)=>{fields[key]=String(value)});return fields; }
function fillSeaOrderForm(form, fields) { Object.entries(fields||{}).forEach(([name,value])=>{const controls=form.querySelectorAll('[name="'+CSS.escape(name)+'"]');controls.forEach(control=>{if(control.type==='radio'||control.type==='checkbox')control.checked=control.value===value;else control.value=value})}); }
function openSeaOrderEditor(type, id) { const order=seaOrders[type].find(item=>item.id===id),source=document.querySelector('[data-order-form="'+type+'"]'),form=document.getElementById('sea-order-edit-form');if(!order||!source||!form)return;editingSeaOrder={type,id};form.innerHTML=source.innerHTML;form.querySelectorAll('[type="submit"],button[type="button"]').forEach(button=>button.closest('.flex.gap-3')?.remove()||button.remove());fillSeaOrderForm(form,order.fields);document.getElementById('sea-order-edit-title').textContent='编辑'+seaBusinessName(type)+'订单';document.getElementById('sea-order-edit-modal').hidden=false;document.body.style.overflow='hidden'; }
function closeSeaOrderEditor() { document.getElementById('sea-order-edit-modal').hidden=true;document.getElementById('sea-order-edit-form').innerHTML='';editingSeaOrder=null;document.body.style.overflow=''; }
function saveSeaOrderEdit(event) { event.preventDefault();if(!editingSeaOrder)return;const {type,id}=editingSeaOrder,order=seaOrders[type].find(item=>item.id===id),form=event.currentTarget;if(!order||!form.reportValidity())return;order.fields=collectSeaOrderFields(form);order.customer=order.fields['联系人']||order.fields['联系方式']||'客户';order.summary=type==='Procurement'?(order.fields['采购品类']||'采购订单'):((order.fields['起运地']||'')+' → '+(order.fields['目的地']||'')).replace(/^\s*→\s*$/,'运输订单');closeSeaOrderEditor();renderSeaOrders(type);showToast('订单修改已保存'); }
function showSeaOrderDetail(type, id) { const order=seaOrders[type].find(item=>item.id===id),page=document.querySelector('.sea-business-page[data-business="'+type+'"]'),panel=document.querySelector('[data-order-panel="'+type+'"]'),detail=document.querySelector('[data-business-detail="'+type+'"]');if(!order||!detail)return;if(page)page.classList.add('hidden');if(panel)panel.hidden=true;detail.dataset.orderId=id;const fields=order.fields||{};detail.querySelectorAll('[data-detail-content]').forEach(element=>{const key=element.dataset.detailContent;let value=key==='orderNo'?order.orderNo:key==='status'?order.status:key==='carrier'?(order.carrier||'待分配'):key==='预算总价'&&fields['采购数量']&&fields['预算单价($)']?('$'+(Number(fields['采购数量'])*Number(fields['预算单价($)'])).toFixed(2)):(fields[key]||'-');if(key==='预算单价($)'&&value!=='-')value='$'+value;element.textContent=value});const currentStep=({'待确认':0,'处理中':1,'已完成':2})[order.status]??0;detail.querySelectorAll('.sea-progress-step').forEach((step,index)=>{step.classList.toggle('is-current',index===currentStep);step.classList.toggle('is-done',index<currentStep)});switchBusinessDetailTab(detail.id, 'transport');detail.hidden=false;window.scrollTo(0,0); }
function querySeaOrders(type) { const page=document.querySelector('.sea-business-page[data-business="'+type+'"]');if(!page)return;seaPageState[type]={orderNo:(page.querySelector('[data-filter="orderNo"]')?.value||'').trim().toLowerCase(),customer:(page.querySelector('[data-filter="route"]')?.value||page.querySelector('[data-filter="customer"]')?.value||'').trim().toLowerCase(),status:page.querySelector('[data-filter="status"]')?.value||''};seaPaginationState[type].currentPage=1;renderSeaOrders(type); }
function resetSeaOrders(type) { const page=document.querySelector('.sea-business-page[data-business="'+type+'"]');if(page)page.querySelectorAll('[data-filter]').forEach(control=>control.value='');seaPageState[type]={orderNo:'',customer:'',status:''};seaPaginationState[type].currentPage=1;renderSeaOrders(type); }
const CLIENT_PAGES = ['order', 'bulk', 'quick', 'labeling-list', 'labeling', 'labeling-detail', 'storage-transfer-list', 'storage-transfer-create', 'storage-transfer-detail', 'cargo-disposal-list', 'cargo-disposal-create', 'cargo-disposal-detail', 'return-relabel-list', 'return-relabel-create', 'return-relabel-detail', 'inbound', 'inbound-detail', 'delivery', 'exception-orders', 'exception-order-detail', 'biz-fee', 'company', 'profile'];
let currentGenericEl = null;
function dashNavigate(name, el) {
document.querySelectorAll('#workbench .dash-view').forEach(v => v.classList.add('hidden'));
if (BUILTIN_VIEWS.indexOf(name) !== -1) {
const view = document.getElementById('dashView' + name);
if (view) view.classList.remove('hidden');
if (['Procurement','Truck','Pallet'].includes(name)) showSeaBusinessPage(name);
} else if (CLIENT_PAGES.indexOf(name) !== -1) {
const cv = document.getElementById('dashViewClient');
if (cv) cv.classList.remove('hidden');
if (typeof switchPage === 'function') switchPage(name);
if (name === 'biz-fee') renderClientBills();
if (name === 'inbound') renderInboundOrders();
if (name === 'exception-orders') renderClientExceptionOrders();
} else {
if (el) { populateGeneric(el); currentGenericEl = el; }
const gv = document.getElementById('dashViewGeneric');
if (gv) gv.classList.remove('hidden');
}
document.querySelectorAll('.dash-nav-link').forEach(l => l.classList.remove('active'));
if (el) el.classList.add('active');
if (!el && name === 'exception-order-detail') {
const exceptionLink = document.querySelector('.dash-nav-link[onclick*="exception-orders"]');
if (exceptionLink) exceptionLink.classList.add('active');
}
const right = document.getElementById('dashRight');
if (right) right.classList.toggle('dash-right-hidden', name !== 'Home');
window.scrollTo(0, 0);
closeDashSidebar();
}
function goHome() {
const link = document.querySelector('.dash-nav-link[data-view="Home"]');
dashNavigate('Home', link);
}
function toggleDashGroup(header) {
header.classList.toggle('collapsed');
const sub = header.parentElement.querySelector('.dash-menu-sub');
if (sub) sub.classList.toggle('collapsed');
}
function populateGeneric(el) {
const icon = el.getAttribute('data-gicon') || 'fa-layer-group';
const title = t(el.getAttribute('data-gtitle') || 'm_coming');
const sub = t(el.getAttribute('data-gsub') || 'm_coming_sub');
const ic = document.getElementById('genericIcon');
if (ic) ic.className = 'fas ' + icon;
const ti = document.getElementById('genericTitle');
if (ti) ti.textContent = title;
const su = document.getElementById('genericSub');
if (su) su.textContent = sub;
}
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) {
openSeaBusiness(type,'new',null);
}
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();
const type=this.dataset.orderForm;
if(type&&seaOrders[type]){const fields=collectSeaOrderFields(this),now=new Date(),customer=fields['联系人']||fields['联系方式']||'客户',summary=type==='Procurement'?(fields['采购品类']||'采购订单'):((fields['起运地']||'')+' → '+(fields['目的地']||'')).replace(/^\s*→\s*$/,'运输订单');seaOrders[type].unshift({id:type+'-'+now.getTime(),orderNo:'HC'+String(now.getFullYear())+String(now.getMonth()+1).padStart(2,'0')+String(now.getDate()).padStart(2,'0')+String(seaOrders[type].length+1).padStart(4,'0'),customer,summary,fields,carrier:'待分配',createdAt:now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-'+String(now.getDate()).padStart(2,'0')+' '+String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0'),status:'待确认'});}
showToast(t('toast_order_ok'));
this.reset();
if(type&&seaOrders[type])setTimeout(()=>showSeaBusinessPage(type),400);else setTimeout(() => goHome(), 1200);
});
});
document.getElementById('sea-order-edit-form')?.addEventListener('submit', saveSeaOrderEdit);
// ====== 多语言 i18n ======
const i18n = {
zh: {
dash_home_link: "返回官网", dash_logout: "退出登录", m_g_service: "客户服务",
dash_topup: "充值", m_balance: "账户余额", dash_topup_title: "账户充值", dash_topup_amount: "充值金额",
dash_topup_step_amount: "填写金额", dash_topup_step_proof: "上传凭证", dash_topup_step_success: "提交成功", dash_topup_close_label: "关闭充值弹窗", dash_topup_progress_label: "充值进度", dash_topup_remove_label: "移除凭证文件",
dash_topup_amount_help: "请输入本次转账充值金额。", dash_topup_next: "下一步", dash_topup_proof_title: "上传转账凭证", dash_topup_proof_help: "请上传清晰的银行或支付转账凭证,以便财务核对。", dash_topup_qr_title: "收款二维码", dash_topup_qr_hint: "请扫码完成转账,转账后上传打款凭证", dash_topup_qr_placeholder: "待替换", dash_topup_qr_alt: "收款二维码占位图,待替换", dash_topup_upload_title: "点击或拖拽上传凭证", dash_topup_upload_hint: "支持 JPG、PNG、PDF,文件不超过 10MB", dash_topup_back: "上一步", dash_topup_submit: "提交申请",
dash_topup_success_title: "充值申请已提交", dash_topup_pending: "待财务审核", dash_topup_result_amount: "充值金额", dash_topup_result_proof: "转账凭证", dash_topup_arrival_notice: "预计 24 小时到账,财务审核完成后余额会自动更新", dash_topup_sms_notice: "已自动发送短信提醒财务", dash_topup_finish: "完成",
dash_topup_amount_required: "请输入充值金额", dash_topup_amount_invalid: "请输入大于 0 的有效金额", dash_topup_proof_required: "请上传转账凭证", dash_topup_proof_type: "仅支持 JPG、PNG 或 PDF 文件", dash_topup_proof_size: "文件大小不能超过 10MB", dash_topup_submitted: "充值申请已提交,等待财务审核",
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", m_g_service: "Customer Service",
dash_topup: "Top Up", m_balance: "Account Balance", dash_topup_title: "Account Top Up", dash_topup_amount: "Top-up amount",
dash_topup_step_amount: "Enter Amount", dash_topup_step_proof: "Upload Proof", dash_topup_step_success: "Submitted", dash_topup_close_label: "Close top-up dialog", dash_topup_progress_label: "Top-up progress", dash_topup_remove_label: "Remove proof file",
dash_topup_amount_help: "Enter the amount transferred for this top-up.", dash_topup_next: "Next", dash_topup_proof_title: "Upload Transfer Proof", dash_topup_proof_help: "Upload a clear bank or payment transfer receipt for finance review.", dash_topup_qr_title: "Payment QR Code", dash_topup_qr_hint: "Scan to complete the transfer, then upload your payment proof", dash_topup_qr_placeholder: "To be replaced", dash_topup_qr_alt: "Payment QR code placeholder, to be replaced", dash_topup_upload_title: "Click or drag a file here", dash_topup_upload_hint: "JPG, PNG, or PDF up to 10MB", dash_topup_back: "Back", dash_topup_submit: "Submit Request",
dash_topup_success_title: "Top-up Request Submitted", dash_topup_pending: "Pending Finance Review", dash_topup_result_amount: "Top-up Amount", dash_topup_result_proof: "Transfer Proof", dash_topup_arrival_notice: "Funds are expected within 24 hours. Your balance will update automatically after finance approval.", dash_topup_sms_notice: "Finance has been notified automatically by SMS", dash_topup_finish: "Finish",
dash_topup_amount_required: "Enter a top-up amount", dash_topup_amount_invalid: "Enter a valid amount greater than 0", dash_topup_proof_required: "Upload transfer proof", dash_topup_proof_type: "Only JPG, PNG, or PDF files are supported", dash_topup_proof_size: "File size cannot exceed 10MB", dash_topup_submitted: "Top-up request submitted for finance review",
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", m_g_service: "Servicio al cliente",
dash_topup: "Recargar", m_balance: "Saldo de cuenta", dash_topup_title: "Recarga de cuenta", dash_topup_amount: "Monto de recarga",
dash_topup_step_amount: "Ingresar monto", dash_topup_step_proof: "Subir comprobante", dash_topup_step_success: "Enviado", dash_topup_close_label: "Cerrar el diálogo de recarga", dash_topup_progress_label: "Progreso de recarga", dash_topup_remove_label: "Eliminar el comprobante",
dash_topup_amount_help: "Ingresa el monto transferido para esta recarga.", dash_topup_next: "Siguiente", dash_topup_proof_title: "Subir comprobante de transferencia", dash_topup_proof_help: "Sube un comprobante bancario o de pago claro para revisión financiera.", dash_topup_qr_title: "Código QR de pago", dash_topup_qr_hint: "Escanea para completar la transferencia y luego sube el comprobante", dash_topup_qr_placeholder: "Pendiente de reemplazo", dash_topup_qr_alt: "Marcador de posición del código QR de pago, pendiente de reemplazo", dash_topup_upload_title: "Haz clic o arrastra un archivo", dash_topup_upload_hint: "JPG, PNG o PDF de hasta 10MB", dash_topup_back: "Atrás", dash_topup_submit: "Enviar solicitud",
dash_topup_success_title: "Solicitud de recarga enviada", dash_topup_pending: "Pendiente de revisión financiera", dash_topup_result_amount: "Monto de recarga", dash_topup_result_proof: "Comprobante", dash_topup_arrival_notice: "Los fondos se acreditarán en un plazo estimado de 24 horas. El saldo se actualizará automáticamente después de la aprobación.", dash_topup_sms_notice: "Finanzas recibió automáticamente un aviso por SMS", dash_topup_finish: "Finalizar",
dash_topup_amount_required: "Ingresa el monto de recarga", dash_topup_amount_invalid: "Ingresa un monto válido mayor que 0", dash_topup_proof_required: "Sube el comprobante de transferencia", dash_topup_proof_type: "Solo se admiten archivos JPG, PNG o PDF", dash_topup_proof_size: "El archivo no puede superar los 10MB", dash_topup_submitted: "Solicitud de recarga enviada para revisión financiera",
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('[data-i18n-aria-label]').forEach(el => {
const k = el.getAttribute('data-i18n-aria-label');
if (i18n[lang][k] !== undefined) el.setAttribute('aria-label', i18n[lang][k]);
});
document.querySelectorAll('[data-i18n-alt]').forEach(el => {
const k = el.getAttribute('data-i18n-alt');
if (i18n[lang][k] !== undefined) el.setAttribute('alt', i18n[lang][k]);
});
document.querySelectorAll('#langSwitcherDash [data-lang]').forEach(b => {
b.classList.toggle('active', b.getAttribute('data-lang') === lang);
});
if (currentGenericEl) populateGeneric(currentGenericEl);
if (typeof refreshTopupLocale === 'function') refreshTopupLocale();
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);
}
// 充值申请弹窗
const TOPUP_MAX_FILE_SIZE = 10 * 1024 * 1024;
const TOPUP_ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'pdf'];
const TOPUP_ALLOWED_TYPES = ['image/jpeg', 'image/png', 'application/pdf'];
function validateTopupAmount(value) {
if (value === null || value === undefined || String(value).trim() === '') return 'required';
const amount = Number(value);
return Number.isFinite(amount) && amount > 0 ? '' : 'invalid';
}
function validateTopupProof(file) {
if (!file) return 'required';
const fileName = String(file.name || '');
const extension = fileName.includes('.') ? fileName.split('.').pop().toLowerCase() : '';
if (!TOPUP_ALLOWED_EXTENSIONS.includes(extension)) return 'type';
if (file.type && !TOPUP_ALLOWED_TYPES.includes(file.type)) return 'type';
if (file.size > TOPUP_MAX_FILE_SIZE) return 'size';
return '';
}
let topupCurrentStep = 1;
let topupSelectedProof = null;
let topupAmountErrorCode = '';
let topupProofErrorCode = '';
let topupPreviousFocus = null;
let topupBackgroundState = [];
function setTopupBackgroundInert(modal) {
topupBackgroundState = [];
let branch = modal;
while (branch && branch.parentElement) {
const parent = branch.parentElement;
Array.from(parent.children).forEach(sibling => {
if (sibling === branch || sibling.id === 'toast') return;
topupBackgroundState.push({ element: sibling, inert: Boolean(sibling.inert) });
sibling.inert = true;
});
if (parent === document.body) break;
branch = parent;
}
}
function restoreTopupBackground() {
topupBackgroundState.forEach(state => { state.element.inert = state.inert; });
topupBackgroundState = [];
}
function getTopupFocusableElements() {
const modal = document.querySelector('#topupModal .topup-modal');
if (!modal) return [];
return Array.from(modal.querySelectorAll('button, input, [href], [tabindex]:not([tabindex="-1"])')).filter(element => {
return !element.disabled && !element.closest('[hidden]');
});
}
function handleTopupDialogKeydown(event) {
const overlay = document.getElementById('topupModal');
if (!overlay || overlay.classList.contains('hidden')) return;
if (event.key === 'Escape') {
event.preventDefault();
closeTopup();
return;
}
if (event.key !== 'Tab') return;
const focusable = getTopupFocusableElements();
if (!focusable.length) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
}
function resetTopupFlow() {
topupCurrentStep = 1;
topupSelectedProof = null;
topupAmountErrorCode = '';
topupProofErrorCode = '';
const amountInput = document.getElementById('topupAmount');
const proofInput = document.getElementById('topupProof');
if (amountInput) amountInput.value = '';
if (proofInput) proofInput.value = '';
document.querySelectorAll('#topupPresets .topup-preset').forEach(button => button.classList.remove('active'));
const successAmount = document.getElementById('topupSuccessAmount');
const successFile = document.getElementById('topupSuccessFile');
if (successAmount) successAmount.textContent = '-';
if (successFile) successFile.textContent = '-';
renderTopupProof();
renderTopupStep();
refreshTopupLocale();
}
function renderTopupStep() {
[1, 2, 3].forEach(step => {
const panel = document.getElementById('topupStep' + step);
const footer = document.getElementById('topupFoot' + step);
if (panel) panel.hidden = step !== topupCurrentStep;
if (footer) footer.hidden = step !== topupCurrentStep;
});
document.querySelectorAll('[data-topup-step]').forEach(item => {
const step = Number(item.dataset.topupStep);
item.classList.toggle('active', step === topupCurrentStep);
item.classList.toggle('done', step < topupCurrentStep);
item.setAttribute('aria-current', step === topupCurrentStep ? 'step' : 'false');
});
}
function openTopup() {
topupPreviousFocus = document.activeElement;
resetTopupFlow();
const modal = document.getElementById('topupModal');
modal.classList.remove('hidden');
setTopupBackgroundInert(modal);
document.addEventListener('keydown', handleTopupDialogKeydown);
setTimeout(() => document.getElementById('topupAmount')?.focus(), 0);
}
function closeTopup() {
const modal = document.getElementById('topupModal');
if (modal.classList.contains('hidden')) return;
modal.classList.add('hidden');
document.removeEventListener('keydown', handleTopupDialogKeydown);
restoreTopupBackground();
resetTopupFlow();
const focusTarget = topupPreviousFocus;
topupPreviousFocus = null;
setTimeout(() => focusTarget?.focus(), 0);
}
function setTopupAmount(value, element) {
const input = document.getElementById('topupAmount');
if (input && input.value !== String(value)) input.value = value;
topupAmountErrorCode = '';
document.getElementById('topupAmountField')?.classList.remove('has-error');
document.querySelectorAll('#topupPresets .topup-preset').forEach(button => {
button.classList.toggle('active', button === element || Number(button.textContent.replace(/[^0-9.]/g, '')) === Number(value));
});
refreshTopupLocale();
}
function goToTopupProof() {
topupAmountErrorCode = validateTopupAmount(document.getElementById('topupAmount').value);
document.getElementById('topupAmountField').classList.toggle('has-error', Boolean(topupAmountErrorCode));
refreshTopupLocale();
if (topupAmountErrorCode) {
document.getElementById('topupAmount').focus();
return;
}
topupCurrentStep = 2;
renderTopupStep();
setTimeout(() => document.getElementById('topupUpload')?.focus(), 0);
}
function goBackToTopupAmount() {
topupCurrentStep = 1;
renderTopupStep();
setTimeout(() => document.getElementById('topupAmount')?.focus(), 0);
}
function handleTopupProof(file) {
topupProofErrorCode = validateTopupProof(file);
topupSelectedProof = topupProofErrorCode ? null : file;
if (topupProofErrorCode) {
const proofInput = document.getElementById('topupProof');
if (proofInput) proofInput.value = '';
}
renderTopupProof();
refreshTopupLocale();
}
function handleTopupDragOver(event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
document.getElementById('topupUpload').classList.add('dragging');
}
function handleTopupDragLeave(event) {
event.preventDefault();
document.getElementById('topupUpload').classList.remove('dragging');
}
function handleTopupDrop(event) {
event.preventDefault();
document.getElementById('topupUpload').classList.remove('dragging');
handleTopupProof(event.dataTransfer.files[0] || null);
}
function removeTopupProof() {
topupSelectedProof = null;
topupProofErrorCode = '';
const proofInput = document.getElementById('topupProof');
if (proofInput) proofInput.value = '';
renderTopupProof();
refreshTopupLocale();
document.getElementById('topupUpload')?.focus();
}
function renderTopupProof() {
const summary = document.getElementById('topupProofSummary');
const upload = document.getElementById('topupUpload');
if (!summary || !upload) return;
summary.hidden = !topupSelectedProof;
upload.classList.toggle('has-error', Boolean(topupProofErrorCode));
if (topupSelectedProof) {
document.getElementById('topupProofName').textContent = topupSelectedProof.name;
document.getElementById('topupProofSize').textContent = formatTopupFileSize(topupSelectedProof.size);
}
}
function formatTopupFileSize(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
function submitTopupRequest() {
topupProofErrorCode = validateTopupProof(topupSelectedProof);
renderTopupProof();
refreshTopupLocale();
if (topupProofErrorCode) {
document.getElementById('topupUpload').focus();
return;
}
topupCurrentStep = 3;
const amount = Number(document.getElementById('topupAmount').value);
const locale = currentLang === 'zh' ? 'zh-CN' : (currentLang === 'es' ? 'es-MX' : 'en-US');
document.getElementById('topupSuccessAmount').textContent = '¥' + amount.toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
document.getElementById('topupSuccessFile').textContent = topupSelectedProof.name;
renderTopupStep();
showToast(t('dash_topup_submitted'));
setTimeout(() => document.getElementById('topupSuccessTitle')?.focus({ preventScroll: true }), 0);
}
function finishTopup() {
closeTopup();
}
function refreshTopupLocale() {
const amountErrors = { required: 'dash_topup_amount_required', invalid: 'dash_topup_amount_invalid' };
const proofErrors = { required: 'dash_topup_proof_required', type: 'dash_topup_proof_type', size: 'dash_topup_proof_size' };
const amountError = document.getElementById('topupAmountError');
const proofError = document.getElementById('topupProofError');
if (amountError) amountError.textContent = topupAmountErrorCode ? t(amountErrors[topupAmountErrorCode]) : '';
if (proofError) proofError.textContent = topupProofErrorCode ? t(proofErrors[topupProofErrorCode]) : '';
document.getElementById('topupAmount')?.setAttribute('aria-invalid', String(Boolean(topupAmountErrorCode)));
document.getElementById('topupProof')?.setAttribute('aria-invalid', String(Boolean(topupProofErrorCode)));
document.getElementById('topupUpload')?.setAttribute('aria-invalid', String(Boolean(topupProofErrorCode)));
}
function openBalanceDetails() {
document.getElementById('balanceDetailsModal').classList.remove('hidden');
if (!document.getElementById('balanceStartDate').value) {
const defaultPeriod = document.querySelector('#balanceQuickPeriods .balance-period-btn.active');
setBalancePeriod(30, defaultPeriod);
}
}
function closeBalanceDetails() {
document.getElementById('balanceDetailsModal').classList.add('hidden');
}
function formatBalanceDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return year + '-' + month + '-' + day;
}
function setBalancePeriod(period, el) {
const end = new Date();
const start = new Date(end);
if (period === 'today') {
// 开始和结束均为今天
} else if (period === 'month') {
start.setDate(1);
} else {
start.setDate(end.getDate() - Number(period) + 1);
}
document.getElementById('balanceStartDate').value = formatBalanceDate(start);
document.getElementById('balanceEndDate').value = formatBalanceDate(end);
document.querySelectorAll('#balanceQuickPeriods .balance-period-btn').forEach(btn => btn.classList.remove('active'));
if (el) el.classList.add('active');
filterBalanceDetails();
}
function filterBalanceDetails() {
const start = document.getElementById('balanceStartDate').value;
const end = document.getElementById('balanceEndDate').value;
if (start && end && start > end) {
showToast('开始日期不能晚于结束日期');
return;
}
let visibleCount = 0;
document.querySelectorAll('#balanceDetailRows tr[data-balance-date]').forEach(row => {
const date = row.dataset.balanceDate;
const visible = (!start || date >= start) && (!end || date <= end);
row.hidden = !visible;
if (visible) visibleCount++;
});
document.getElementById('balanceEmptyRow').hidden = visibleCount !== 0;
document.getElementById('balanceDetailNote').textContent = start || end
? (start || '最早') + ' 至 ' + (end || '今天') + ',共 ' + visibleCount + ' 条流水'
: '全部时间,共 ' + visibleCount + ' 条流水';
}
function resetBalanceFilter() {
const buttons = document.querySelectorAll('#balanceQuickPeriods .balance-period-btn');
setBalancePeriod(30, buttons[buttons.length - 1]);
}
let accountBalance = 12850.00;
function renderBalance() {
const el = document.getElementById('profileBalance');
if (el) el.textContent = accountBalance.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
// 初始化
document.addEventListener('DOMContentLoaded', function() {
applyLanguage(currentLang);
renderBalance();
// 尝试从官网登录带入用户名(URL ?user=xxx)
const params = new URLSearchParams(window.location.search);
const userParam = params.get('user');
if (userParam) {
setDashUser(decodeURIComponent(userParam));
}
// 默认首页显示右侧信息栏,其他页面隐藏
const right = document.getElementById('dashRight');
if (right) right.classList.remove('dash-right-hidden');
});
applyLanguage(currentLang);
/* ===== 柏悦客户端交互逻辑(原样并入)===== */
// ===== Page switching =====
function switchPage(pageId, clickedEl) {
// Hide all pages
document.querySelectorAll('.page-content').forEach(p => p.classList.remove('active'));
// Show target
const target = document.getElementById('page-' + pageId);
if (target) target.classList.add('active');
// Update sidebar active state
document.querySelectorAll('.nav-item, .nav-sub-item').forEach(el => el.classList.remove('active'));
document.querySelectorAll('.nav-group-header').forEach(h => h.classList.remove('active'));
if (clickedEl) {
clickedEl.classList.add('active');
} else {
// Find by data-page
const el = document.querySelector('[data-page="' + pageId + '"]');
if (el) el.classList.add('active');
}
// For 美线头程下单 sub-items, highlight parent group header
if (pageId === 'order' || pageId === 'bulk' || pageId === 'quick') {
const orderGroup = document.querySelector('.nav-group:first-of-type');
if (orderGroup) {
const header = orderGroup.querySelector('.nav-group-header');
const sub = orderGroup.querySelector('.nav-sub');
if (header) header.classList.add('active');
if (header && !header.classList.contains('expanded')) {
header.classList.add('expanded');
sub.classList.add('expanded');
}
}
}
// For 订单管理 sub-items, expand the group
if (pageId === 'inbound' || pageId === 'inbound-detail' || pageId === 'delivery' || pageId === 'exception-orders' || pageId === 'exception-order-detail') {
const groups = document.querySelectorAll('.nav-group');
groups.forEach(g => {
const header = g.querySelector('.nav-group-header');
const sub = g.querySelector('.nav-sub');
if (header && header.textContent.includes('订单管理')) {
header.classList.add('active');
if (!header.classList.contains('expanded')) {
header.classList.add('expanded');
sub.classList.add('expanded');
}
}
});
}
// For 海外仓下单, expand the group
if (pageId === 'labeling-list' || pageId === 'labeling' || pageId === 'storage-transfer-list' || pageId === 'storage-transfer-create' || pageId === 'storage-transfer-detail' || pageId === 'cargo-disposal-list' || pageId === 'cargo-disposal-create' || pageId === 'cargo-disposal-detail' || pageId === 'return-relabel-list' || pageId === 'return-relabel-create' || pageId === 'return-relabel-detail') {
const groups = document.querySelectorAll('.nav-group');
groups.forEach(g => {
const header = g.querySelector('.nav-group-header');
const sub = g.querySelector('.nav-sub');
if (header && header.textContent.includes('海外仓下单')) {
header.classList.add('active');
if (!header.classList.contains('expanded')) {
header.classList.add('expanded');
sub.classList.add('expanded');
}
}
});
}
}
// ===== 客户端异常订单 =====
const CLIENT_EXCEPTION_ORDER_SEED = [
{ id:'client-exception-1', exceptionNo:'EXC20260812001', lastMileNo:'LMO20260810004', dispatchNo:'YWBHK200615004', inboundNo:'FBI20260804001', outboundNo:'FBO20260807001', abnormalType:'拒收', abnormalStatus:'待处理', customer:'深圳市璞智信达科技有限公司', warehouse:'义乌仓', rejectionReason:'收件方拒绝签收', rejectionTime:'2026-08-12 14:30', rejectedAt:'2026-08-12 14:30', rejectedBy:'系统示例', updatedAt:'2026-08-12 14:30' },
{ id:'client-exception-2', exceptionNo:'EXC20260812002', lastMileNo:'LMO20260810005', dispatchNo:'YWBHK200615005', inboundNo:'FBI20260805001', outboundNo:'FBO20260808001', abnormalType:'退货', abnormalStatus:'处理中', customer:'美森正班海卡包税有限公司', warehouse:'青岛仓库', rejectionReason:'', rejectionTime:'', returnReason:'收件地址无法投递', returnRoute:'退回海外仓', returnTrackingNo:'USRET20260812005', returnHandledAt:'2026-08-12 15:10', returnHandledBy:'王客服', updatedAt:'2026-08-12 15:10' }
];
let clientExceptionFilteredOrders = null;
function escapeClientExceptionHtml(value) {
return String(value == null ? '' : value).replace(/[&<>"']/g, char => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char]));
}
function clientExceptionReason(record) { return record.returnReason || record.rejectionReason || '—'; }
function clientExceptionTime(record) { return record.returnHandledAt || record.rejectedAt || record.rejectionTime || record.exceptionTime || '—'; }
function clientExceptionHandler(record) { return record.returnHandledBy || record.rejectedBy || '—'; }
function clientExceptionUpdatedAt(record) { return record.updatedAt || clientExceptionTime(record); }
function clientExceptionRelatedOrders(record) {
return [['入仓码',record.inboundNo]].filter(item => item[1]);
}
function clientExceptionStatusTag(status) {
const className = status === '待处理' ? 'status-rejected' : status === '处理中' ? 'status-pending' : status === '已完成' ? 'status-done' : 'status-default';
return '<span class="status-badge '+className+'">'+escapeClientExceptionHtml(status || '—')+'</span>';
}
function clientExceptionRows(records) {
return records.map((record, index) => {
const typeClass = record.abnormalType === '拒收' ? 'is-rejected' : 'is-returned';
const relations = clientExceptionRelatedOrders(record).map(item => '<div class="client-exception-relation"><span class="client-exception-relation-no">'+escapeClientExceptionHtml(item[1])+'</span></div>').join('');
return '<tr><td>'+(index+1)+'</td><td><span class="client-exception-no">'+escapeClientExceptionHtml(record.exceptionNo)+'</span></td><td><div class="client-exception-relations">'+relations+'</div></td><td><span class="client-exception-type '+typeClass+'">'+escapeClientExceptionHtml(record.abnormalType || '—')+'</span></td><td>'+escapeClientExceptionHtml(record.customer || '—')+'</td><td>'+escapeClientExceptionHtml(record.warehouse || '—')+'</td><td><span class="client-exception-reason" title="'+escapeClientExceptionHtml(clientExceptionReason(record))+'">'+escapeClientExceptionHtml(clientExceptionReason(record))+'</span></td><td>'+escapeClientExceptionHtml(clientExceptionTime(record))+'</td><td>'+clientExceptionStatusTag(record.abnormalStatus)+'</td><td>'+escapeClientExceptionHtml(record.returnRoute || record.exceptionAction || '—')+'</td><td>'+escapeClientExceptionHtml(record.returnTrackingNo || '—')+'</td><td>'+escapeClientExceptionHtml(clientExceptionHandler(record))+'</td><td>'+escapeClientExceptionHtml(clientExceptionUpdatedAt(record))+'</td><td><button type="button" class="btn-text" onclick="openClientExceptionOrderDetail(\''+escapeClientExceptionHtml(record.id)+'\')">查看</button></td></tr>';
}).join('');
}
function renderClientExceptionOrders() {
const body = document.getElementById('client-exception-orders-body');
if (!body) return;
const orders = clientExceptionFilteredOrders === null ? CLIENT_EXCEPTION_ORDER_SEED : clientExceptionFilteredOrders;
body.innerHTML = orders.length ? clientExceptionRows(orders) : '<tr class="empty-state"><td colspan="14"><div class="empty-icon">⚠</div><div class="empty-text">暂无异常订单</div></td></tr>';
const total = document.getElementById('client-exception-total');
if (total) total.textContent = String(orders.length);
}
function queryClientExceptionOrders() {
const keyword = String(document.getElementById('client-exception-keyword')?.value || '').trim().toLowerCase();
const type = document.getElementById('client-exception-type')?.value || '';
const status = document.getElementById('client-exception-status')?.value || '';
clientExceptionFilteredOrders = CLIENT_EXCEPTION_ORDER_SEED.filter(record => {
const related = clientExceptionRelatedOrders(record).map(item => item[1]).join(' ');
const haystack = [record.exceptionNo, related, record.customer, record.rejectionReason, record.returnReason].filter(Boolean).join(' ').toLowerCase();
return (!keyword || haystack.includes(keyword)) && (!type || record.abnormalType === type) && (!status || record.abnormalStatus === status);
});
renderClientExceptionOrders();
}
function resetClientExceptionOrders() {
const keyword = document.getElementById('client-exception-keyword');
const type = document.getElementById('client-exception-type');
const status = document.getElementById('client-exception-status');
if (keyword) keyword.value = '';
if (type) type.value = '';
if (status) status.value = '';
clientExceptionFilteredOrders = null;
renderClientExceptionOrders();
}
function openClientExceptionOrderDetail(id) {
const record = CLIENT_EXCEPTION_ORDER_SEED.find(item => String(item.id) === String(id));
if (!record) {
showToast('未找到异常订单');
dashNavigate('exception-orders');
return;
}
const values = {
exceptionNo: record.exceptionNo,
abnormalType: record.abnormalType,
abnormalStatus: record.abnormalStatus,
customer: record.customer,
warehouse: record.warehouse,
lastMileNo: record.lastMileNo,
dispatchNo: record.dispatchNo,
inboundNo: record.inboundNo,
outboundNo: record.outboundNo,
reason: clientExceptionReason(record),
exceptionTime: clientExceptionTime(record),
action: record.returnRoute || record.exceptionAction || '—',
returnTrackingNo: record.returnTrackingNo || '—',
handler: clientExceptionHandler(record),
updatedAt: clientExceptionUpdatedAt(record)
};
document.querySelectorAll('[data-client-exception-detail]').forEach(element => {
const key = element.getAttribute('data-client-exception-detail');
element.textContent = values[key] == null || values[key] === '' ? '—' : values[key];
});
dashNavigate('exception-order-detail');
}
// ===== 仓储转运单 =====
const STORAGE_TRANSFER_KEY='by_storage_transfer_orders_0805';
const STORAGE_TRANSFER_PAGE_SIZE = 10;
const STORAGE_TRANSFER_SEED = [
{
id: 'storage-transfer-seed-1', orderNo: 'ZY202608050001', inboundNo: 'RK20260805018',
boxCount: 12, warehouse: '美西洛杉矶仓', fbaCode: 'ONT8',
address: '24300 Nandina Ave, Moreno Valley, CA 92551', deliveryMethod: '卡车派送',
expectedArrival: '2026-08-12T10:00', shipmentId: 'FBA18Q7K2L9P', trackingId: 'PRO80465021',
boxId: 'U001-U012', note: '预约送仓', relabel: 'no', tailOrderNo: 'LC202608050001',
relabelOrderNo: '', status: '待处理', createdAt: '2026-08-05 09:20',
cargo: [{ sku: 'SKU-CC-1001', name: '蓝牙耳机套件', boxes: 12, relabelSku: '', requirement: '', fileName: '' }]
},
{
id: 'storage-transfer-seed-2', orderNo: 'ZY202608050002', inboundNo: 'RK20260805023',
boxCount: 8, warehouse: '美东新泽西仓', fbaCode: 'ABE8',
address: '309 Cedar Ln, Florence, NJ 08518', deliveryMethod: '快递派送',
expectedArrival: '2026-08-13T14:00', shipmentId: 'FBA18R2M6X4D', trackingId: '1Z80526039',
boxId: 'E001-E008', note: '', relabel: 'yes', tailOrderNo: 'LC202608050002',
relabelOrderNo: 'HB202608050001', status: '处理中', createdAt: '2026-08-05 14:35',
cargo: [{ sku: 'SKU-CC-2002', name: '手机保护壳', boxes: 8, relabelSku: 'SKU-CC-2002-N', requirement: '覆盖原标签', fileName: 'FNSKU-2002.pdf' }]
}
];
let storageTransferFilteredOrders = null;
let storageTransferCurrentPage = 1;
let storageTransferSubmitting = false;
let storageTransferEditingId = null;
let storageTransferEditingSubmitting = false;
function cloneStorageTransferSeed() {
return JSON.parse(JSON.stringify(STORAGE_TRANSFER_SEED));
}
function loadStorageTransferOrders() {
try {
const raw = localStorage.getItem(STORAGE_TRANSFER_KEY);
if (!raw) {
const seeded = cloneStorageTransferSeed();
saveStorageTransferOrders(seeded);
return seeded;
}
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) throw new Error('仓储转运数据格式无效');
return parsed.filter(order => order && typeof order === 'object');
} catch (error) {
console.warn('读取仓储转运单失败,已使用示例数据。', error);
return cloneStorageTransferSeed();
}
}
function saveStorageTransferOrders(orders) {
if (!Array.isArray(orders)) return false;
try {
localStorage.setItem(STORAGE_TRANSFER_KEY, JSON.stringify(orders));
return true;
} catch (error) {
console.warn('保存仓储转运单失败。', error);
return false;
}
}
function escapeStorageTransferHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}
function getStorageTransferNavLink() {
return Array.from(document.querySelectorAll('.dash-nav-link')).find(link =>
(link.getAttribute('onclick') || '').includes("dashNavigate('storage-transfer-list', this)")) || null;
}
function navigateStorageTransferPage(pageId) {
dashNavigate(pageId, getStorageTransferNavLink());
}
// ===== 退货换标 =====
const RETURN_RELABEL_KEY = 'by_return_relabel_orders_0812';
const RETURN_RELABEL_PAGE_SIZE = 10;
const RETURN_RELABEL_SEED = [
{id:'return-relabel-seed-1',orderNo:'THHB202608120001',sourceFba:'ONT8',destinationFba:'LAX9',orderId:'RM123456789',carrier:'UPS',trackingNo:'1Z999AA101',reason:'客户退货',relabel:true,labelCase:'Case1',cartonLabel:'carton-label.pdf',skuLabel:'',address:'123 Main St, Los Angeles CA',destinationCode:'LAX9',expectedArrival:'2026-08-16T10:00',returnRemark:'优先处理',tailRemark:'',backendTailOrderNo:'LC202608120001',backendRelabelOrderNo:'HB202608120001',relabelStatus:'换标中',tailStatus:'待入库',createdAt:'2026-08-12 09:15',skus:[{sku:'SKU-A1001',name:'蓝牙耳机套件',fnsku:'X001ABC',quantity:24}]},
{id:'return-relabel-seed-2',orderNo:'THHB202608120002',sourceFba:'ABE8',destinationFba:'ONT8',orderId:'RM987654321',carrier:'FedEx',trackingNo:'794123456',reason:'亚马逊移除',relabel:false,address:'456 Harbor Rd, Ontario CA',destinationCode:'ONT8',expectedArrival:'2026-08-17T14:00',returnRemark:'',tailRemark:'',backendTailOrderNo:'LC202608120002',tailStatus:'已入库待出库',createdAt:'2026-08-12 11:20',skus:[{sku:'SKU-B2002',name:'手机保护壳',fnsku:'X002DEF',quantity:12}]},
{id:'return-relabel-status-seed',orderNo:'THHB202608120003',sourceFba:'CLT2',destinationFba:'ONT8',orderId:'RM555666777',carrier:'UPS',trackingNo:'1ZSTATUS003',reason:'不可售',relabel:true,labelCase:'Case3',cartonLabel:'carton-status.pdf',skuLabel:'sku-status.pdf',relabelStatus:'已完成',tailStatus:'出库中',createdAt:'2026-08-12 12:00',backendTailOrderNo:'LC202608120003',backendRelabelOrderNo:'HB202608120003',address:'789 Trade Center Dr, Charlotte NC',destinationCode:'ONT8',expectedArrival:'2026-08-18T09:30',returnRemark:'状态示例',tailRemark:'按预约送达',skus:[{sku:'SKU-C3003',name:'智能家居配件',fnsku:'X003GHI',quantity:8}]}
];
let returnRelabelFilteredOrders = null, returnRelabelCurrentPage = 1, returnRelabelSubmitting = false, returnRelabelDetailId = null, returnRelabelEditingId = null, returnRelabelEditingSubmitting = false;
function loadReturnRelabelOrders(){try{const raw=localStorage.getItem(RETURN_RELABEL_KEY);if(!raw){return JSON.parse(JSON.stringify(RETURN_RELABEL_SEED));}const parsed=JSON.parse(raw);if(!Array.isArray(parsed)){showToast('退货换标数据格式异常,已恢复示例数据');return JSON.parse(JSON.stringify(RETURN_RELABEL_SEED));}const validLocal=parsed.filter(item=>{if(!item||typeof item!=='object'||typeof item.id!=='string'||typeof item.orderNo!=='string'||!Array.isArray(item.skus)||!item.skus.length)return false;return item.skus.every(s=>s&&typeof s==='object'&&typeof s.sku==='string'&&typeof s.name==='string'&&typeof s.fnsku==='string'&&Number.isFinite(Number(s.quantity))&&Number(s.quantity)>0);});if(validLocal.length!==parsed.length)showToast('部分退货换标数据异常,已忽略损坏记录');const localIds=new Set(validLocal.map(item=>item.id));const missingSeeds=RETURN_RELABEL_SEED.filter(item=>!localIds.has(item.id));return JSON.parse(JSON.stringify(validLocal.concat(missingSeeds)));}catch(e){showToast('读取退货换标数据失败,已恢复示例数据');return JSON.parse(JSON.stringify(RETURN_RELABEL_SEED));}}
function saveReturnRelabelOrders(orders){try{const localOrders=(Array.isArray(orders)?orders:[]).filter(item=>item&&typeof item==='object');localStorage.setItem(RETURN_RELABEL_KEY,JSON.stringify(localOrders));return true;}catch(e){showToast('保存退货换标数据失败');return false;}}
function escapeReturnRelabelHtml(value){return String(value==null?'':value).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');}
function returnRelabelLabelText(value){return ({Case1:'换箱唛',Case2:'换SKU标',Case3:'既换箱唛,又换SKU标'}[value]||value||'—');}
function returnRelabelNav(){return Array.from(document.querySelectorAll('.dash-nav-link')).find(l=>(l.getAttribute('onclick')||'').includes("dashNavigate('return-relabel-list'"))||null;}
function openReturnRelabelList(){dashNavigate('return-relabel-list',returnRelabelNav());}
function openReturnRelabelCreate(){dashNavigate('return-relabel-create',returnRelabelNav());setTimeout(()=>{const form=document.getElementById('return-relabel-form');if(form)form.reset();const body=document.getElementById('return-relabel-sku-body');if(body)body.innerHTML='';addReturnRelabelSkuRow();toggleReturnRelabel();updateFileRequirements();},0);}
function setReturnRelabelEditExistingFile(kind,fileName){const value=String(fileName||'');const hidden=document.getElementById('return-relabel-edit-'+kind+'-existing');const current=document.getElementById('return-relabel-edit-'+kind+'-current');if(hidden)hidden.value=value;if(current)current.innerHTML='当前文件:<strong>'+escapeReturnRelabelHtml(value||'无')+'</strong>';}
function captureReturnRelabelEditFileName(input,labelId){const label=document.getElementById(labelId);const fileName=input&&input.files&&input.files[0]?input.files[0].name:'';if(label&&fileName)label.innerHTML='新文件:<strong>'+escapeReturnRelabelHtml(fileName)+'</strong>';updateReturnRelabelEditFileRequirements();}
function addReturnRelabelEditSkuRow(item){const tbody=document.getElementById('return-relabel-edit-sku-body');if(!tbody)return;item=item||{};const row=document.createElement('tr');row.innerHTML='<td><input class="return-relabel-input return-relabel-edit-sku" value="'+escapeReturnRelabelHtml(item.sku||'')+'" aria-label="商品SKU" required></td><td><input class="return-relabel-input return-relabel-edit-name" value="'+escapeReturnRelabelHtml(item.name||'')+'" aria-label="中文品名" required></td><td><input class="return-relabel-input return-relabel-edit-fnsku" value="'+escapeReturnRelabelHtml(item.fnsku||'')+'" aria-label="FNSKU" required></td><td><input class="return-relabel-input return-relabel-edit-new-sku" value="'+escapeReturnRelabelHtml(item.newSku||'')+'" aria-label="换标新SKU"></td><td><input class="return-relabel-input return-relabel-edit-quantity" type="number" min="1" value="'+escapeReturnRelabelHtml(item.quantity||1)+'" aria-label="件数" required></td><td><button type="button" class="btn-text" onclick="removeReturnRelabelEditSkuRow(this)">删除</button></td>';tbody.appendChild(row);}
function removeReturnRelabelEditSkuRow(button){const row=button&&button.closest?button.closest('tr'):null;if(row)row.remove();if(!document.querySelector('#return-relabel-edit-sku-body tr'))addReturnRelabelEditSkuRow();}
function openReturnRelabelEdit(orderId){const order=loadReturnRelabelOrders().find(item=>String(item.id)===String(orderId));const modal=document.getElementById('return-relabel-edit-modal');const form=document.getElementById('return-relabel-edit-form');if(!order||!modal||!form){showToast('未找到退货换标单');return;}returnRelabelEditingId=String(order.id);returnRelabelEditingSubmitting=false;form.reset();['inboundCode','sourceFba','reason','receivingWarehouse','carrier','trackingNo','returnRemark','destinationFba','address','expectedArrival','tailRemark'].forEach(name=>{if(form.elements[name])form.elements[name].value=order[name]||'';});form.elements.relabel.checked=!!order.relabel;form.elements.labelCase.value=order.labelCase||'Case1';const body=document.getElementById('return-relabel-edit-sku-body');if(body)body.innerHTML='';(Array.isArray(order.skus)&&order.skus.length?order.skus:[{}]).forEach(addReturnRelabelEditSkuRow);setReturnRelabelEditExistingFile('carton',order.cartonLabel||'');setReturnRelabelEditExistingFile('sku',order.skuLabel||'');const error=document.getElementById('return-relabel-edit-error');if(error)error.textContent='';toggleReturnRelabelEdit();const title=document.getElementById('return-relabel-edit-title');if(title)title.textContent='编辑退货换标单 · '+(order.orderNo||'');modal.classList.add('show');const first=form.elements.inboundCode;if(first&&first.focus)setTimeout(()=>first.focus(),0);}
function closeReturnRelabelEdit(){const modal=document.getElementById('return-relabel-edit-modal');if(modal)modal.classList.remove('show');returnRelabelEditingId=null;returnRelabelEditingSubmitting=false;}
function toggleReturnRelabelEdit(){const form=document.getElementById('return-relabel-edit-form');if(!form)return;const enabled=!!form.elements.relabel.checked;const fields=document.getElementById('return-relabel-edit-settings-fields'),empty=document.getElementById('return-relabel-edit-settings-empty'),status=document.getElementById('return-relabel-edit-settings-status');if(fields)fields.hidden=!enabled;if(empty)empty.hidden=enabled;if(status){status.textContent=enabled?'已开启':'可选操作';status.classList.toggle('is-on',enabled);}updateReturnRelabelEditFileRequirements();}
function updateReturnRelabelEditFileRequirements(){const form=document.getElementById('return-relabel-edit-form');if(!form)return;const enabled=!!form.elements.relabel.checked,requirement=form.elements.labelCase?.value||'Case1',carton=form.elements.cartonLabel,sku=form.elements.skuLabel,cartonExisting=document.getElementById('return-relabel-edit-carton-existing')?.value||'',skuExisting=document.getElementById('return-relabel-edit-sku-existing')?.value||'',cartonEnabled=enabled&&requirement!=='Case2',skuEnabled=enabled&&requirement!=='Case1';if(carton){carton.disabled=!cartonEnabled;carton.required=cartonEnabled&&!cartonExisting;}if(sku){sku.disabled=!skuEnabled;sku.required=skuEnabled&&!skuExisting;}const cartonCard=document.getElementById('return-relabel-edit-carton-card'),skuCard=document.getElementById('return-relabel-edit-sku-card');if(cartonCard){cartonCard.classList.toggle('is-disabled',!cartonEnabled);cartonCard.classList.toggle('is-ready',cartonEnabled&&!!(cartonExisting||carton.files?.length));}if(skuCard){skuCard.classList.toggle('is-disabled',!skuEnabled);skuCard.classList.toggle('is-ready',skuEnabled&&!!(skuExisting||sku.files?.length));}const hint=document.getElementById('return-relabel-edit-file-hint');if(hint)hint.textContent=enabled?({Case1:'换箱唛:需要上传箱唛',Case2:'换 SKU 标:需要上传 SKU 标签',Case3:'既换箱唛,又换 SKU 标:需要上传箱唛和 SKU 标签'}[requirement]||''):'未勾选换标,无需上传标签文件';}
function getReturnRelabelEditFiles(form){const relabel=!!form.elements.relabel.checked;if(!relabel)return{relabel:false,labelCase:'',cartonLabel:'',skuLabel:''};const labelCase=form.elements.labelCase?.value||'Case1';const fileName=(control,existingId)=>control&&control.files&&control.files[0]?control.files[0].name:(document.getElementById(existingId)?.value||'');return{relabel:true,labelCase,cartonLabel:labelCase==='Case2'?'':fileName(form.elements.cartonLabel,'return-relabel-edit-carton-existing'),skuLabel:labelCase==='Case1'?'':fileName(form.elements.skuLabel,'return-relabel-edit-sku-existing')};}
function validateReturnRelabelEditForm(){const form=document.getElementById('return-relabel-edit-form'),error=document.getElementById('return-relabel-edit-error');if(error)error.textContent='';updateReturnRelabelEditFileRequirements();if(!form||!form.checkValidity()){if(form)form.reportValidity();return false;}for(const field of ['inboundCode','receivingWarehouse','carrier','trackingNo','address']){if(!String(form.elements[field]?.value||'').trim()){if(error)error.textContent='请完整填写退货和尾程信息';return false;}}const rows=Array.from(document.querySelectorAll('#return-relabel-edit-sku-body tr'));if(!rows.length){if(error)error.textContent='请至少添加一条 SKU';return false;}for(const row of rows){if(!row.querySelector('.return-relabel-edit-sku').value.trim()||!row.querySelector('.return-relabel-edit-name').value.trim()||!row.querySelector('.return-relabel-edit-fnsku').value.trim()||Number(row.querySelector('.return-relabel-edit-quantity').value)<1){if(error)error.textContent='请完整填写 SKU、中文品名、FNSKU 和件数';return false;}}return true;}
function mergeReturnRelabelOrder(original,values){const editable=['inboundCode','sourceFba','reason','receivingWarehouse','destinationFba','carrier','trackingNo','returnRemark','relabel','labelCase','cartonLabel','skuLabel','address','expectedArrival','tailRemark','skus'];const merged=Object.assign({},original);editable.forEach(name=>{if(Object.prototype.hasOwnProperty.call(values,name))merged[name]=values[name];});return merged;}
function submitReturnRelabelEdit(event){if(event)event.preventDefault();if(returnRelabelEditingSubmitting)return false;const form=document.getElementById('return-relabel-edit-form'),submitButton=document.getElementById('return-relabel-edit-submit');if(!form||!returnRelabelEditingId)return false;returnRelabelEditingSubmitting=true;if(submitButton)submitButton.disabled=true;try{if(!validateReturnRelabelEditForm())return false;const orders=loadReturnRelabelOrders(),index=orders.findIndex(item=>String(item.id)===String(returnRelabelEditingId));if(index<0){showToast('未找到退货换标单');return false;}const files=getReturnRelabelEditFiles(form),values={inboundCode:form.elements.inboundCode.value.trim(),sourceFba:form.elements.sourceFba.value,reason:form.elements.reason.value,receivingWarehouse:form.elements.receivingWarehouse.value,destinationFba:form.elements.destinationFba.value,carrier:form.elements.carrier.value.trim(),trackingNo:form.elements.trackingNo.value.trim(),returnRemark:form.elements.returnRemark.value.trim(),relabel:files.relabel,labelCase:files.labelCase,cartonLabel:files.cartonLabel,skuLabel:files.skuLabel,address:form.elements.address.value.trim(),expectedArrival:form.elements.expectedArrival.value,tailRemark:form.elements.tailRemark.value.trim(),skus:Array.from(document.querySelectorAll('#return-relabel-edit-sku-body tr')).map(row=>({sku:row.querySelector('.return-relabel-edit-sku').value.trim(),name:row.querySelector('.return-relabel-edit-name').value.trim(),fnsku:row.querySelector('.return-relabel-edit-fnsku').value.trim(),newSku:row.querySelector('.return-relabel-edit-new-sku').value.trim(),quantity:Number(row.querySelector('.return-relabel-edit-quantity').value)}))};const original=Object.assign({},orders[index]);if(files.relabel&&!original.backendRelabelOrderNo){original.backendRelabelOrderNo='HB'+String(original.orderNo||'').replace(/^THHB/,'');original.relabelStatus='待换标';}orders[index]=mergeReturnRelabelOrder(original,values);if(!saveReturnRelabelOrders(orders)){const error=document.getElementById('return-relabel-edit-error');if(error)error.textContent='保存失败,请检查浏览器存储设置';return false;}returnRelabelFilteredOrders=null;renderReturnRelabelList();closeReturnRelabelEdit();showToast('退货换标单修改成功');return false;}finally{returnRelabelEditingSubmitting=false;if(submitButton)submitButton.disabled=false;}}
function changeReturnRelabelPage(delta){const orders=returnRelabelFilteredOrders===null?loadReturnRelabelOrders():returnRelabelFilteredOrders;const pages=Math.max(1,Math.ceil(orders.length/RETURN_RELABEL_PAGE_SIZE));const next=returnRelabelCurrentPage+Number(delta||0);if(next<1||next>pages)return;returnRelabelCurrentPage=next;renderReturnRelabelList();}
function renderReturnRelabelList(){const body=document.getElementById('return-relabel-list-body');if(!body)return;const orders=returnRelabelFilteredOrders===null?loadReturnRelabelOrders():returnRelabelFilteredOrders;const pages=Math.max(1,Math.ceil(orders.length/RETURN_RELABEL_PAGE_SIZE));returnRelabelCurrentPage=Math.min(returnRelabelCurrentPage,pages);const start=(returnRelabelCurrentPage-1)*RETURN_RELABEL_PAGE_SIZE;const rows=orders.slice(start,start+RETURN_RELABEL_PAGE_SIZE);body.innerHTML=rows.length?rows.map((o,index)=>{const skuCount=(o.skus||[]).length;const sku=skuCount?(o.skus[0].sku+(skuCount>1?' 等 '+skuCount+' 项':'')):'—';const qty=(o.skus||[]).reduce((n,s)=>n+Number(s.quantity||0),0);const labelCase=o.relabel?returnRelabelLabelText(o.labelCase):'—';const relabelOrder=o.relabel?(o.backendRelabelOrderNo||'—'):'—';const relabelStatus=o.relabel?(o.relabelStatus||'—'):'—';const id=escapeReturnRelabelHtml(o.id);return '<tr><td>'+(start+index+1)+'</td><td>'+escapeReturnRelabelHtml(o.orderNo)+'</td><td>'+escapeReturnRelabelHtml(o.inboundCode||'—')+'</td><td>'+escapeReturnRelabelHtml(o.sourceFba)+'</td><td>'+escapeReturnRelabelHtml(o.destinationFba)+'</td><td>'+escapeReturnRelabelHtml(sku)+'</td><td>'+qty+'</td><td>'+(o.relabel?'是':'否')+'</td><td>'+escapeReturnRelabelHtml(labelCase)+'</td><td>'+escapeReturnRelabelHtml(relabelOrder)+'</td><td>'+escapeReturnRelabelHtml(relabelStatus)+'</td><td>'+escapeReturnRelabelHtml(o.createdAt)+'</td><td><span class="return-relabel-actions"><button type="button" class="btn-text" data-return-relabel-id="'+id+'">查看</button><button type="button" class="btn-text" data-return-relabel-edit-id="'+id+'" onclick="openReturnRelabelEdit(this.dataset.returnRelabelEditId)">编辑</button></span></td></tr>';}).join(''):'<tr class="empty-state"><td colspan="13">暂无退货换标订单</td></tr>';document.getElementById('return-relabel-total').textContent=orders.length;document.getElementById('return-relabel-page-number').textContent=returnRelabelCurrentPage+' / '+pages;document.getElementById('return-relabel-prev').disabled=returnRelabelCurrentPage<=1;document.getElementById('return-relabel-next').disabled=returnRelabelCurrentPage>=pages;}
function ensureReturnRelabelAssociatedFields(){const fields=document.getElementById('return-relabel-settings-fields');if(!fields||fields.dataset.associatedUiReady)return;fields.dataset.associatedUiReady='1';const requirementGrid=fields.querySelector('.return-relabel-form-grid');if(requirementGrid&&!requirementGrid.querySelector('.return-relabel-associated-template')){const template=document.createElement('div');template.className='return-relabel-associated-template';template.innerHTML='<span class="return-relabel-associated-template-label">关联文件模板</span><button type="button" class="btn btn-default" onclick="downloadReturnRelabelRelationTemplate()"><i class="fas fa-download" aria-hidden="true"></i> 下载模板</button>';requirementGrid.appendChild(template);}const uploadGrid=fields.querySelector('.return-relabel-file-grid'),hint=document.getElementById('return-relabel-file-hint');if(uploadGrid&&!fields.querySelector('.return-relabel-associated-fields')){const associated=document.createElement('div');associated.className='return-relabel-associated-fields';associated.innerHTML='<div class="return-relabel-associated-field"><label for="return-relabel-relation-file">上传关联文件</label><input id="return-relabel-relation-file" type="file" name="relationFile" accept=".xlsx,.xls,.pdf,.png,.jpg,.jpeg" onchange="updateFileRequirements()"></div><div class="return-relabel-associated-field"><label for="return-relabel-relation-remark">备注</label><textarea id="return-relabel-relation-remark" name="relationRemark" placeholder="请输入关联文件备注"></textarea></div>';fields.insertBefore(associated,hint||null);}}
function downloadReturnRelabelRelationTemplate(){if(typeof downloadLabelingRelationTemplate==='function'){downloadLabelingRelationTemplate();return;}showToast('模板组件加载失败,请刷新页面后重试');}
function getReturnRelabelRelationValues(form,existingId){const enabled=!!form?.elements?.relabel?.checked;if(!enabled)return{relationFile:'',relationRemark:''};const fileControl=form.elements.relationFile;const relationFile=fileControl?.files?.[0]?.name||document.getElementById(existingId||'')?.value||'';return{relationFile,relationRemark:String(form.elements.relationRemark?.value||'').trim()};}
function syncReturnRelabelAssociatedControls(form,enabled){const relationFile=form?.elements?.relationFile,relationRemark=form?.elements?.relationRemark;if(relationFile)relationFile.disabled=!enabled;if(relationRemark)relationRemark.disabled=!enabled;}
const returnRelabelOriginalUpdateFileRequirements=updateFileRequirements;
updateFileRequirements=function(){ensureReturnRelabelAssociatedFields();returnRelabelOriginalUpdateFileRequirements();const form=document.getElementById('return-relabel-form');syncReturnRelabelAssociatedControls(form,!!form?.elements?.relabel?.checked);};
const returnRelabelOriginalToggle=toggleReturnRelabel;
toggleReturnRelabel=function(){ensureReturnRelabelAssociatedFields();returnRelabelOriginalToggle();const form=document.getElementById('return-relabel-form');syncReturnRelabelAssociatedControls(form,!!form?.elements?.relabel?.checked);};
const returnRelabelOriginalUpdateEditFileRequirements=updateReturnRelabelEditFileRequirements;
updateReturnRelabelEditFileRequirements=function(){returnRelabelOriginalUpdateEditFileRequirements();const form=document.getElementById('return-relabel-edit-form');syncReturnRelabelAssociatedControls(form,!!form?.elements?.relabel?.checked);};
const returnRelabelOriginalOpenEdit=openReturnRelabelEdit;
openReturnRelabelEdit=function(orderId){returnRelabelOriginalOpenEdit(orderId);const order=loadReturnRelabelOrders().find(item=>String(item.id)===String(orderId)),form=document.getElementById('return-relabel-edit-form');if(!order||!form)return;if(form.elements.relationRemark)form.elements.relationRemark.value=order.relationRemark||'';setReturnRelabelEditExistingFile('relation',order.relationFile||'');updateReturnRelabelEditFileRequirements();};
const returnRelabelOriginalSubmit=submitReturnRelabelOrder;
submitReturnRelabelOrder=function(event){const form=document.getElementById('return-relabel-form'),relationValues=getReturnRelabelRelationValues(form);const before=loadReturnRelabelOrders(),result=returnRelabelOriginalSubmit(event),after=loadReturnRelabelOrders();if(after[0]&&(!before[0]||String(after[0].id)!==String(before[0].id))){after[0].relationFile=relationValues.relationFile;after[0].relationRemark=relationValues.relationRemark;saveReturnRelabelOrders(after);}return result;};
const returnRelabelOriginalEditSubmit=submitReturnRelabelEdit;
submitReturnRelabelEdit=function(event){const form=document.getElementById('return-relabel-edit-form'),editingId=returnRelabelEditingId,relationValues=getReturnRelabelRelationValues(form,'return-relabel-edit-relation-existing'),result=returnRelabelOriginalEditSubmit(event);if(editingId&&returnRelabelEditingId===null){const orders=loadReturnRelabelOrders(),order=orders.find(item=>String(item.id)===String(editingId));if(order){order.relationFile=relationValues.relationFile;order.relationRemark=relationValues.relationRemark;saveReturnRelabelOrders(orders);}}return result;};
function returnRelabelDateValue(value){const match=String(value||'').match(/(\d{4})[-/]?(\d{2})[-/]?(\d{2})/);return match?match.slice(1).join('-'):'';}
function filterReturnRelabelOrders(){const v=id=>(document.getElementById(id)?.value||'').trim().toLowerCase();const start=v('return-relabel-filter-created-start'),end=v('return-relabel-filter-created-end'),source=v('return-relabel-filter-source'),destination=v('return-relabel-filter-destination'),sku=v('return-relabel-filter-sku'),enabled=v('return-relabel-filter-enabled'),relabelStatus=v('return-relabel-filter-status'),tailStatus=v('return-relabel-filter-tail-status');if(start&&end&&start>end){showToast('创建开始时间不能晚于结束时间');return;}returnRelabelFilteredOrders=loadReturnRelabelOrders().filter(o=>(!v('return-relabel-filter-order-no')||String(o.orderNo).toLowerCase().includes(v('return-relabel-filter-order-no')))&&(!source||String(o.sourceFba).toLowerCase()===source)&&(!destination||String(o.destinationFba).toLowerCase()===destination)&&(!sku||(o.skus||[]).some(item=>String(item.sku||'').toLowerCase().includes(sku)))&&(!enabled||(enabled==='yes'?!!o.relabel:!o.relabel))&&(!relabelStatus||o.relabel&&String(o.relabelStatus).toLowerCase()===relabelStatus)&&(!tailStatus||String(o.tailStatus).toLowerCase()===tailStatus)&&(!start||returnRelabelDateValue(o.createdAt)>=start)&&(!end||returnRelabelDateValue(o.createdAt)<=end));returnRelabelCurrentPage=1;renderReturnRelabelList();}
function resetReturnRelabelFilters(){['return-relabel-filter-order-no','return-relabel-filter-source','return-relabel-filter-destination','return-relabel-filter-sku','return-relabel-filter-enabled','return-relabel-filter-tail-status','return-relabel-filter-status','return-relabel-filter-created-start','return-relabel-filter-created-end'].forEach(id=>{const el=document.getElementById(id);if(el)el.value='';});returnRelabelFilteredOrders=null;returnRelabelCurrentPage=1;renderReturnRelabelList();}
function addReturnRelabelSkuRow(item){const tbody=document.getElementById('return-relabel-sku-body');if(!tbody)return;item=item||{};const tr=document.createElement('tr');tr.innerHTML='<td><input class="return-relabel-input return-relabel-carrier" value="'+escapeReturnRelabelHtml(item.carrier||'')+'" aria-label="承运商"></td><td><input class="return-relabel-input return-relabel-tracking-no" value="'+escapeReturnRelabelHtml(item.trackingNo||'')+'" aria-label="运单号"></td><td><input class="return-relabel-input return-relabel-sku" value="'+escapeReturnRelabelHtml(item.sku||'')+'" aria-label="商品SKU" required></td><td><input class="return-relabel-input return-relabel-name" value="'+escapeReturnRelabelHtml(item.name||'')+'" aria-label="中文品名" required></td><td><input class="return-relabel-input return-relabel-fnsku" value="'+escapeReturnRelabelHtml(item.fnsku||'')+'" aria-label="FNSKU" required></td><td><input class="return-relabel-input return-relabel-new-sku" value="'+escapeReturnRelabelHtml(item.newSku||'')+'" aria-label="换标新SKU"></td><td><input class="return-relabel-input return-relabel-quantity" type="number" min="1" value="'+(item.quantity||1)+'" aria-label="件数" required></td><td><button type="button" class="btn-text" onclick="removeReturnRelabelSkuRow(this)">删除</button></td>';tbody.appendChild(tr);}
function removeReturnRelabelSkuRow(button){const row=button&&button.closest?button.closest('tr'):null;if(row)row.remove();if(!document.querySelector('#return-relabel-sku-body tr'))addReturnRelabelSkuRow();}
function ensureReturnRelabelSettingsUi(){const form=document.getElementById('return-relabel-form'),fields=document.getElementById('return-relabel-settings-fields');if(!form||!fields||fields.dataset.uiReady)return;fields.dataset.uiReady='1';const section=fields.closest('.card.return-relabel-section');if(section)section.classList.add('return-relabel-settings-panel');const cardBody=section?.querySelector('.card-body'),title=section?.querySelector('.return-relabel-section-title');if(cardBody&&title){const header=document.createElement('div');header.className='return-relabel-settings-header';const body=document.createElement('div');body.className='return-relabel-settings-body';cardBody.insertBefore(header,title);header.appendChild(title);cardBody.appendChild(body);Array.from(cardBody.children).filter(child=>child!==header&&child!==body).forEach(child=>body.appendChild(child));const status=document.createElement('span');status.id='return-relabel-settings-status';status.className='return-relabel-settings-status';status.textContent='可选操作';header.appendChild(status);}const toggle=fields.previousElementSibling;if(toggle)toggle.classList.add('return-relabel-settings-toggle');const empty=document.createElement('div');empty.id='return-relabel-settings-empty';empty.className='return-relabel-settings-empty';empty.textContent='开启后配置贴标要求和上传文件';fields.parentElement.insertBefore(empty,fields);fields.classList.add('return-relabel-settings-fields');const grid=fields.querySelector('.return-relabel-file-grid');if(grid){grid.classList.add('return-relabel-upload-grid');grid.querySelectorAll(':scope > div').forEach(card=>card.classList.add('return-relabel-upload-card'));}const hint=document.getElementById('return-relabel-file-hint');if(hint)hint.classList.add('return-relabel-file-hint');}
function toggleReturnRelabel(){ensureReturnRelabelSettingsUi();const enabled=!!document.querySelector('#return-relabel-form input[name="relabel"]:checked');const settings=document.getElementById('return-relabel-settings-fields'),empty=document.getElementById('return-relabel-settings-empty'),status=document.getElementById('return-relabel-settings-status');if(settings)settings.hidden=!enabled;if(empty)empty.hidden=enabled;if(status){status.textContent=enabled?'已开启':'可选操作';status.classList.toggle('is-on',enabled);}document.querySelectorAll('#return-relabel-form select[name="labelCase"],#return-relabel-form input[name="cartonLabel"],#return-relabel-form input[name="skuLabel"]').forEach(el=>el.disabled=!enabled);updateFileRequirements();}
function updateFileRequirements(){ensureReturnRelabelSettingsUi();const form=document.getElementById('return-relabel-form');const enabled=!!form&&!!form.querySelector('input[name="relabel"]:checked');const c=form?.elements.labelCase?.value||'Case1';const carton=form?.elements.cartonLabel,sku=form?.elements.skuLabel;if(carton)carton.disabled=!enabled||c==='Case2';if(sku)sku.disabled=!enabled||c==='Case1';const cartonCard=carton?.closest('.return-relabel-upload-card'),skuCard=sku?.closest('.return-relabel-upload-card');if(cartonCard)cartonCard.classList.toggle('is-ready',!!carton.files?.length);if(skuCard)skuCard.classList.toggle('is-ready',!!sku.files?.length);const hint=document.getElementById('return-relabel-file-hint');if(hint)hint.textContent=enabled?({Case1:'换箱唛:需要上传箱唛',Case2:'换 SKU 标:需要上传 SKU 标签',Case3:'既换箱唛,又换 SKU 标:需要上传箱唛和 SKU 标签'}[c]||''):'未勾选换标,无需上传标签文件';}
function validateReturnRelabelForm(){const form=document.getElementById('return-relabel-form');const error=document.getElementById('return-relabel-error');if(error)error.textContent='';if(!form||!form.checkValidity()){if(form)form.reportValidity();return false;}for(const field of ['inboundCode','receivingWarehouse','address']){if(!String(form.elements[field]?.value||'').trim()){if(error)error.textContent='请完整填写退货和尾程信息';return false;}}const rows=Array.from(document.querySelectorAll('#return-relabel-sku-body tr'));if(!rows.length){if(error)error.textContent='请至少添加一条 SKU';return false;}for(const row of rows){if(!row.querySelector('.return-relabel-sku').value.trim()||!row.querySelector('.return-relabel-fnsku').value.trim()||Number(row.querySelector('.return-relabel-quantity').value)<1){if(error)error.textContent='请完整填写每条 SKU 的 SKU、FNSKU 和件数';return false;}}const relabel=form.elements.relabel.checked, c=form.elements.labelCase?.value||'Case1';if(relabel){const carton=form.elements.cartonLabel.files?.[0],sku=form.elements.skuLabel.files?.[0];if(c==='Case1'&&!carton){if(error)error.textContent='换箱唛需要上传箱唛';return false;}if(c==='Case2'&&!sku){if(error)error.textContent='换 SKU 标需要上传 SKU 标签';return false;}if(c==='Case3'&&(!carton||!sku)){if(error)error.textContent='既换箱唛,又换 SKU 标需要上传箱唛和 SKU 标签';return false;}}return true;}
function summarizeReturnRelabelSkuField(skus,field){return Array.from(new Set((skus||[]).map(item=>String(item?.[field]||'').trim()).filter(Boolean))).join('、');}
function submitReturnRelabelOrder(event){if(event)event.preventDefault();if(returnRelabelSubmitting)return false;returnRelabelSubmitting=true;const submitButton=document.getElementById('return-relabel-submit');if(submitButton)submitButton.disabled=true;try{if(!validateReturnRelabelForm())return false;const form=document.getElementById('return-relabel-form'),orders=loadReturnRelabelOrders(),now=new Date(),datePart=now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-'+String(now.getDate()).padStart(2,'0'),stamp=datePart.replace(/-/g,''),nextSeq=orders.reduce((max,item)=>{const match=String(item.orderNo||'').match(new RegExp('^THHB'+stamp+'(\\d+)$'));return match?Math.max(max,Number(match[1])):max;},0)+1,seq=String(nextSeq).padStart(4,'0'),relabel=form.elements.relabel.checked,skus=Array.from(document.querySelectorAll('#return-relabel-sku-body tr')).map(r=>({carrier:r.querySelector('.return-relabel-carrier').value.trim(),trackingNo:r.querySelector('.return-relabel-tracking-no').value.trim(),sku:r.querySelector('.return-relabel-sku').value.trim(),name:r.querySelector('.return-relabel-name').value.trim(),fnsku:r.querySelector('.return-relabel-fnsku').value.trim(),newSku:r.querySelector('.return-relabel-new-sku').value.trim(),quantity:Number(r.querySelector('.return-relabel-quantity').value)}));const order={id:'return-relabel-'+now.getTime(),orderNo:'THHB'+stamp+seq,inboundCode:form.elements.inboundCode.value.trim(),sourceFba:form.elements.sourceFba.value,reason:form.elements.reason.value,receivingWarehouse:form.elements.receivingWarehouse.value,destinationFba:form.elements.destinationFba.value,carrier:summarizeReturnRelabelSkuField(skus,'carrier'),trackingNo:summarizeReturnRelabelSkuField(skus,'trackingNo'),relabel,address:form.elements.address.value.trim(),expectedArrival:form.elements.expectedArrival.value,returnRemark:form.elements.returnRemark.value.trim(),tailRemark:form.elements.tailRemark.value.trim(),backendTailOrderNo:'LC'+stamp+seq,tailStatus:'待入库',createdAt:datePart+' '+String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0'),skus};if(relabel){order.labelCase=form.elements.labelCase?.value||'Case1';order.cartonLabel=order.labelCase==='Case2'?'':(form.elements.cartonLabel.files?.[0]?.name||'');order.skuLabel=order.labelCase==='Case1'?'':(form.elements.skuLabel.files?.[0]?.name||'');order.backendRelabelOrderNo='HB'+stamp+seq;order.relabelStatus='待换标';}else{order.backendRelabelOrderNo='-';}if(!saveReturnRelabelOrders([order].concat(orders))){const error=document.getElementById('return-relabel-error');if(error)error.textContent='保存失败';return false;}returnRelabelFilteredOrders=null;renderReturnRelabelList();showToast(relabel?'退货换标单提交成功,已生成换标单和尾程订单':'退货换标单提交成功,已生成尾程订单');openReturnRelabelList();return false;}finally{returnRelabelSubmitting=false;if(submitButton)submitButton.disabled=false;}}
function openReturnRelabelDetail(orderId){
const order=loadReturnRelabelOrders().find(o=>String(o.id)===String(orderId));
if(!order){showToast('未找到退货换标单');return;}
const total=(order.skus||[]).reduce((sum,item)=>sum+Number(item.quantity||0),0);
document.getElementById('return-relabel-detail-order-no').textContent=order.orderNo||'—';
const statusText=order.relabel?(order.relabelStatus||'待换标'):(order.tailStatus||'待入库');
const status=document.getElementById('return-relabel-detail-status');
if(status){status.textContent=statusText;status.classList.remove('is-processing','is-done');if(statusText.includes('中'))status.classList.add('is-processing');if(statusText.includes('完成'))status.classList.add('is-done');}
const summary=document.getElementById('return-relabel-detail-summary');
if(summary)summary.innerHTML=[['退货换标单号',order.orderNo],['入仓码',order.inboundCode||'—'],['来源FBA仓',order.sourceFba],['退货原因',order.reason],['收货仓',order.receivingWarehouse||'—'],['目的FBA仓',order.destinationFba],['物流承运商',order.carrier],['物流跟踪号',order.trackingNo],['期望到达时间',order.expectedArrival||'—'],['退件总数',total],['退货备注',order.returnRemark||order.remark||'—'],['尾程备注',order.tailRemark||'—'],['地址',order.address],['创建时间',order.createdAt]].map(f=>'<div class="labeling-detail-field"><dt>'+f[0]+'</dt><dd>'+escapeReturnRelabelHtml(f[1])+'</dd></div>').join('');
const body=document.getElementById('return-relabel-detail-sku-body');
if(body)body.innerHTML=(order.skus||[]).map(s=>'<tr><td>'+escapeReturnRelabelHtml(s.sku)+'</td><td>'+escapeReturnRelabelHtml(s.name)+'</td><td>'+escapeReturnRelabelHtml(s.fnsku)+'</td><td>'+escapeReturnRelabelHtml(s.newSku||'—')+'</td><td>'+s.quantity+'</td></tr>').join('');
document.getElementById('return-relabel-detail-relabel-order-no').textContent=order.relabel?(order.backendRelabelOrderNo||'—'):'—';
document.getElementById('return-relabel-detail-relabel-status').textContent=order.relabel?(order.relabelStatus||'—'):'—';
document.getElementById('return-relabel-detail-relabel-enabled').textContent=order.relabel?'是':'否';
document.getElementById('return-relabel-detail-label-case').textContent=order.relabel?returnRelabelLabelText(order.labelCase):'—';
document.getElementById('return-relabel-detail-carton-label').textContent=order.relabel?(order.cartonLabel||'—'):'—';
document.getElementById('return-relabel-detail-sku-label').textContent=order.relabel?(order.skuLabel||'—'):'—';
document.getElementById('return-relabel-detail-relation-file').textContent=order.relabel?(order.relationFile||'—'):'—';
document.getElementById('return-relabel-detail-relation-remark').textContent=order.relabel?(order.relationRemark||'—'):'—';
dashNavigate('return-relabel-detail',returnRelabelNav());
}
document.addEventListener('DOMContentLoaded',()=>{ensureReturnRelabelSettingsUi();const body=document.getElementById('return-relabel-list-body');if(body&&!body.dataset.returnRelabelBound){body.dataset.returnRelabelBound='1';body.addEventListener('click',event=>{const button=event.target.closest('[data-return-relabel-id]');if(button)openReturnRelabelDetail(button.getAttribute('data-return-relabel-id'));});}renderReturnRelabelList();});
document.addEventListener('DOMContentLoaded',()=>{ensureReturnRelabelAssociatedFields();toggleReturnRelabel();});
// ===== 货物废弃 =====
const CARGO_DISPOSAL_KEY = 'by_cargo_disposal_orders_0805';
const CARGO_DISPOSAL_PAGE_SIZE = 10;
const CARGO_DISPOSAL_FEE_PER_BOX = 5;
const CARGO_DISPOSAL_INBOUND_SEED = [
{ id:'inbound-disposal-1', inboundNo:'RK20260808018', warehouse:'美西洛杉矶仓', receivedAt:'2026-08-08', availableBoxes:24, expanded:true, items:[{id:'rk18-1',sku:'SKU-LM-2048',name:'折叠收纳盒',availableBoxes:12},{id:'rk18-2',sku:'SKU-KT-9912',name:'厨房置物架',availableBoxes:12}] },
{ id:'inbound-disposal-2', inboundNo:'RK20260806023', warehouse:'美东新泽西仓', receivedAt:'2026-08-06', availableBoxes:16, expanded:false, items:[{id:'rk23-1',sku:'SKU-PH-3301',name:'手机保护壳',availableBoxes:8},{id:'rk23-2',sku:'SKU-CB-4410',name:'收纳篮',availableBoxes:8}] }
];
const CARGO_DISPOSAL_ORDER_SEED = [
{id:'cargo-disposal-seed-1',orderNo:'FH202608120001',inboundNo:'RK20260808018',warehouse:'美西洛杉矶仓',disposalBoxes:6,feeAmount:30,currency:'USD',tailOrderNo:'LC202608120001',outboundOrderNo:'CK202608120001',billingNo:'JF202608120001',status:'待处理',createdAt:'2026-08-12 09:20',items:[{sku:'SKU-LM-2048',name:'折叠收纳盒',availableBoxes:12,disposalBoxes:4},{sku:'SKU-KT-9912',name:'厨房置物架',availableBoxes:12,disposalBoxes:2}]},
{id:'cargo-disposal-seed-2',orderNo:'FH202608110002',inboundNo:'RK20260806023',warehouse:'美东新泽西仓',disposalBoxes:3,feeAmount:15,currency:'USD',tailOrderNo:'LC202608110002',outboundOrderNo:'CK202608110002',billingNo:'JF202608110002',status:'处理中',createdAt:'2026-08-11 14:35',items:[{sku:'SKU-PH-3301',name:'手机保护壳',availableBoxes:8,disposalBoxes:3}]},
{id:'cargo-disposal-seed-3',orderNo:'FH202608100003',inboundNo:'RK20260808018',warehouse:'美西洛杉矶仓',disposalBoxes:8,feeAmount:40,currency:'USD',tailOrderNo:'LC202608100003',outboundOrderNo:'CK202608100003',billingNo:'JF202608100003',status:'已完成',createdAt:'2026-08-10 16:10',items:[{sku:'SKU-KT-9912',name:'厨房置物架',availableBoxes:12,disposalBoxes:8}]}
];
let cargoDisposalFilteredOrders = null;
let cargoDisposalCurrentPage = 1;
let cargoDisposalSubmitting = false;
let cargoDisposalEditingId = '';
let cargoDisposalEditSubmitting = false;
function cloneCargoDisposalInboundSeed() { return JSON.parse(JSON.stringify(CARGO_DISPOSAL_INBOUND_SEED)); }
function cloneCargoDisposalOrderSeed() { return JSON.parse(JSON.stringify(CARGO_DISPOSAL_ORDER_SEED)); }
function loadCargoDisposalOrders() {
try { const raw = localStorage.getItem(CARGO_DISPOSAL_KEY); if (!raw) return cloneCargoDisposalOrderSeed(); const parsed = JSON.parse(raw); if (!Array.isArray(parsed)) throw new Error('货物废弃数据格式无效'); const validOrders=parsed.filter(item=>item&&typeof item==='object'); const storedIds=new Set(validOrders.map(item=>item.id)); return validOrders.concat(cloneCargoDisposalOrderSeed().filter(item=>!storedIds.has(item.id))); }
catch (error) { console.warn('读取货物废弃订单失败,已恢复示例数据。', error); return cloneCargoDisposalOrderSeed(); }
}
function saveCargoDisposalOrders(orders) { try { const validOrders=(Array.isArray(orders)?orders:[]).filter(item=>item&&typeof item==='object'); localStorage.setItem(CARGO_DISPOSAL_KEY, JSON.stringify(validOrders)); return true; } catch (error) { console.warn('保存货物废弃订单失败。', error); return false; } }
function escapeCargoDisposalHtml(value) { return String(value == null ? '' : value).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;'); }
function getCargoDisposalNavLink() { return Array.from(document.querySelectorAll('.dash-nav-link')).find(link => (link.getAttribute('onclick') || '').includes("dashNavigate('cargo-disposal-list', this)")) || null; }
function navigateCargoDisposalPage(pageId) { dashNavigate(pageId, getCargoDisposalNavLink()); }
function generateCargoDisposalNumber(prefix, orders) { const date = new Date(); const stamp = date.getFullYear() + String(date.getMonth()+1).padStart(2,'0') + String(date.getDate()).padStart(2,'0'); const used = new Set(orders.flatMap(item => [item.orderNo,item.tailOrderNo,item.outboundOrderNo,item.billingNo])); let i=orders.length+1; let value; do { value = prefix + stamp + String(i++).padStart(4,'0'); } while (used.has(value)); return value; }
function calculateCargoDisposalFee(boxes) { return Math.max(0, Number(boxes) || 0) * CARGO_DISPOSAL_FEE_PER_BOX; }
function getCargoDisposalSelection() { const card = document.querySelector('.cargo-disposal-inbound-card.is-selected'); if (!card) return null; const inbound = CARGO_DISPOSAL_INBOUND_SEED.find(item => item.id === card.dataset.inboundId); if (!inbound) return null; return { inbound, items: Array.from(card.querySelectorAll('.cargo-disposal-quantity')).map(input => ({ id:input.dataset.itemId, disposalBoxes:Number(input.value) || 0 })) }; }
function renderCargoDisposalFee() { const selection = getCargoDisposalSelection(); const total = selection ? selection.items.reduce((sum,item) => sum + item.disposalBoxes, 0) : 0; const fee = document.getElementById('cargo-disposal-fee'); if (fee) fee.textContent = '$' + calculateCargoDisposalFee(total).toFixed(2); }
function toggleCargoDisposalInbound(card) { document.querySelectorAll('.cargo-disposal-inbound-card').forEach(item => { item.classList.remove('is-selected'); if (item !== card) item.classList.remove('expanded'); }); card.classList.add('is-selected','expanded'); renderCargoDisposalFee(); }
function renderCargoDisposalInboundCards(query) { const container = document.getElementById('cargo-disposal-inbound-cards'); if (!container) return; const normalizedQuery=String(query==null?(document.getElementById('cargo-disposal-inbound-search')?.value||''):query).trim().toLowerCase(); const inboundCodes=cloneCargoDisposalInboundSeed().filter(inbound=>!normalizedQuery||inbound.inboundNo.toLowerCase().includes(normalizedQuery)); if(!inboundCodes.length){container.innerHTML='<div class="empty-text">未找到匹配的入仓码,请检查后重新搜索</div>';renderCargoDisposalFee();return;} container.innerHTML = inboundCodes.map(inbound => '<div class="cargo-disposal-inbound-card ' + (inbound.expanded ? 'expanded is-selected' : '') + '" data-inbound-id="' + inbound.id + '"><div class="cargo-disposal-inbound-summary" role="button" tabindex="0" onclick="toggleCargoDisposalInbound(this.parentElement)" onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();toggleCargoDisposalInbound(this.parentElement)}"><div><strong>' + escapeCargoDisposalHtml(inbound.inboundNo) + '</strong><div class="cargo-disposal-inbound-meta">' + escapeCargoDisposalHtml(inbound.warehouse) + ' · ' + escapeCargoDisposalHtml(inbound.receivedAt) + ' · 可用 ' + inbound.availableBoxes + ' 箱</div></div><span aria-hidden="true">' + (inbound.expanded ? '⌃' : '⌄') + '</span></div><div class="cargo-disposal-inbound-body"><div class="cargo-disposal-table-wrap"><table class="cargo-disposal-table"><thead><tr><th>商品 SKU</th><th>中文品名</th><th>可用箱数</th><th><span class="required">*</span>废弃箱数</th></tr></thead><tbody>' + inbound.items.map(item => '<tr><td>' + escapeCargoDisposalHtml(item.sku) + '</td><td>' + escapeCargoDisposalHtml(item.name) + '</td><td>' + item.availableBoxes + '</td><td><input class="cargo-disposal-quantity" type="number" min="0" max="' + item.availableBoxes + '" value="0" data-item-id="' + item.id + '" aria-label="' + escapeCargoDisposalHtml(item.sku) + '废弃箱数" oninput="renderCargoDisposalFee()"></td></tr>').join('') + '</tbody></table></div></div></div>').join(''); renderCargoDisposalFee(); }
function filterCargoDisposalInboundCards() { const query=(document.getElementById('cargo-disposal-inbound-search')?.value||'').trim().toLowerCase(); const matches=CARGO_DISPOSAL_INBOUND_SEED.filter(inbound=>inbound.inboundNo.toLowerCase().includes(query)); renderCargoDisposalInboundCards(query); return matches.length; }
function renderCargoDisposalList() { const body=document.getElementById('cargo-disposal-list-body'); if (!body) return; const orders=cargoDisposalFilteredOrders===null?loadCargoDisposalOrders():cargoDisposalFilteredOrders; const pages=Math.max(1,Math.ceil(orders.length/CARGO_DISPOSAL_PAGE_SIZE)); cargoDisposalCurrentPage=Math.min(Math.max(1,cargoDisposalCurrentPage),pages); const start=(cargoDisposalCurrentPage-1)*CARGO_DISPOSAL_PAGE_SIZE; const rows=orders.slice(start,start+CARGO_DISPOSAL_PAGE_SIZE); body.innerHTML=rows.length?rows.map((order,index)=>{const id=escapeCargoDisposalHtml(order.id);return '<tr><td>'+(start+index+1)+'</td><td>'+escapeCargoDisposalHtml(order.orderNo)+'</td><td>'+escapeCargoDisposalHtml(order.inboundNo)+'</td><td>'+escapeCargoDisposalHtml(order.inboundNo)+'</td><td>'+order.disposalBoxes+'</td><td>'+escapeCargoDisposalHtml(order.warehouse)+'</td><td>$'+Number(order.feeAmount).toFixed(2)+'</td><td>'+escapeCargoDisposalHtml(order.outboundOrderNo)+'</td><td><span class="status-badge status-processing">'+escapeCargoDisposalHtml(order.status)+'</span></td><td>'+escapeCargoDisposalHtml(order.createdAt)+'</td><td><div class="cargo-disposal-row-actions"><button type="button" class="btn-text" data-cargo-disposal-id="'+id+'" onclick="openCargoDisposalDetail(this.dataset.cargoDisposalId)">查看</button><button type="button" class="btn-text" data-cargo-disposal-id="'+id+'" onclick="openCargoDisposalEdit(this.dataset.cargoDisposalId)">编辑</button></div></td></tr>';}).join(''):'<tr class="empty-state"><td colspan="11"><div class="empty-text">暂无货物废弃订单</div></td></tr>'; document.getElementById('cargo-disposal-total').textContent=orders.length; document.getElementById('cargo-disposal-page-number').textContent=cargoDisposalCurrentPage+' / '+pages; document.getElementById('cargo-disposal-prev').disabled=cargoDisposalCurrentPage<=1; document.getElementById('cargo-disposal-next').disabled=cargoDisposalCurrentPage>=pages; }
function filterCargoDisposalOrders() { const val=id=>(document.getElementById(id)?.value||'').trim().toLowerCase(); const start=val('cargo-disposal-filter-created-start'), end=val('cargo-disposal-filter-created-end'); if(start&&end&&start>end){showToast('创建开始时间不能晚于结束时间');return;} cargoDisposalFilteredOrders=loadCargoDisposalOrders().filter(order=>(!val('cargo-disposal-filter-order-no')||order.orderNo.toLowerCase().includes(val('cargo-disposal-filter-order-no')))&&(!val('cargo-disposal-filter-inbound-no')||order.inboundNo.toLowerCase().includes(val('cargo-disposal-filter-inbound-no')))&&(!val('cargo-disposal-filter-warehouse')||order.warehouse===document.getElementById('cargo-disposal-filter-warehouse').value)&&(!val('cargo-disposal-filter-status')||order.status===document.getElementById('cargo-disposal-filter-status').value)&&(!start||order.createdAt.slice(0,10)>=start)&&(!end||order.createdAt.slice(0,10)<=end)); cargoDisposalCurrentPage=1; renderCargoDisposalList(); }
function resetCargoDisposalFilters() { ['cargo-disposal-filter-order-no','cargo-disposal-filter-inbound-no','cargo-disposal-filter-warehouse','cargo-disposal-filter-status','cargo-disposal-filter-created-start','cargo-disposal-filter-created-end'].forEach(id=>{const el=document.getElementById(id);if(el)el.value='';}); cargoDisposalFilteredOrders=null; cargoDisposalCurrentPage=1; renderCargoDisposalList(); }
function changeCargoDisposalPage(delta) { const orders=cargoDisposalFilteredOrders===null?loadCargoDisposalOrders():cargoDisposalFilteredOrders; const pages=Math.max(1,Math.ceil(orders.length/CARGO_DISPOSAL_PAGE_SIZE)); const next=cargoDisposalCurrentPage+Number(delta||0); if(next<1||next>pages)return; cargoDisposalCurrentPage=next; renderCargoDisposalList(); }
function openCargoDisposalList() { renderCargoDisposalList(); navigateCargoDisposalPage('cargo-disposal-list'); }
function openCargoDisposalCreate() { const form=document.getElementById('cargo-disposal-form'); if(form)form.reset(); renderCargoDisposalInboundCards(); cargoDisposalSubmitting=false; navigateCargoDisposalPage('cargo-disposal-create'); }
function validateCargoDisposalForm() { const selection=getCargoDisposalSelection(); const error=document.getElementById('cargo-disposal-error'); if(error)error.textContent=''; if(!selection){if(error)error.textContent='请选择一个入仓码';return false;} const inboundById=new Map(selection.inbound.items.map(item=>[item.id,item])); let total=0; for(const item of selection.items){ const availableBoxes=inboundById.get(item.id)?.availableBoxes||0; if(!Number.isInteger(item.disposalBoxes)||item.disposalBoxes<0||item.disposalBoxes>availableBoxes){if(error)error.textContent='废弃箱数必须是整数且不能超过可用箱数';return false;} total+=item.disposalBoxes; } if(total<1){if(error)error.textContent='请至少填写一条大于 0 的废弃箱数';return false;} return true; }
function submitCargoDisposalOrder(event) { if(event)event.preventDefault(); if(cargoDisposalSubmitting)return false; cargoDisposalSubmitting=true; const submit=document.getElementById('cargo-disposal-submit'); if(submit)submit.disabled=true; try{if(!validateCargoDisposalForm())return false; const selection=getCargoDisposalSelection(), orders=loadCargoDisposalOrders(), now=new Date(), disposalBoxes=selection.items.reduce((sum,item)=>sum+item.disposalBoxes,0); const order={id:'cargo-disposal-'+now.getTime(),orderNo:generateCargoDisposalNumber('FH',orders),inboundNo:selection.inbound.inboundNo,warehouse:selection.inbound.warehouse,disposalBoxes,feeAmount:calculateCargoDisposalFee(disposalBoxes),currency:'USD',tailOrderNo:generateCargoDisposalNumber('LC',orders),outboundOrderNo:generateCargoDisposalNumber('CK',orders),billingNo:generateCargoDisposalNumber('JF',orders),status:'待处理',createdAt:now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-'+String(now.getDate()).padStart(2,'0')+' '+String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0'),items:selection.items.filter(item=>item.disposalBoxes>0).map(item=>{const source=selection.inbound.items.find(sourceItem=>sourceItem.id===item.id);return {sku:source.sku,name:source.name,availableBoxes:source.availableBoxes,disposalBoxes:item.disposalBoxes};})}; if(!saveCargoDisposalOrders([order].concat(orders))){showToast('保存失败,请检查浏览器存储设置');return false;} cargoDisposalFilteredOrders=null;cargoDisposalCurrentPage=1;renderCargoDisposalList();showToast('货物废弃单提交成功,已生成尾程单、废弃出库单和弃货费计费单');navigateCargoDisposalPage('cargo-disposal-list');return false;} finally{cargoDisposalSubmitting=false;if(submit)submit.disabled=false;} }
function getCargoDisposalEditSelection() {
const modal=document.getElementById('cargo-disposal-edit-modal');
const card=modal&&modal.querySelector('.cargo-disposal-inbound-card.is-selected');
if(!card)return null;
const inbound=CARGO_DISPOSAL_INBOUND_SEED.find(item=>item.id===card.dataset.inboundId);
if(!inbound)return null;
return {inbound,items:Array.from(card.querySelectorAll('.cargo-disposal-edit-quantity')).map(input=>({id:input.dataset.itemId,disposalBoxes:Number(input.value)||0}))};
}
function renderCargoDisposalEditFee() {
const selection=getCargoDisposalEditSelection();
const total=selection?selection.items.reduce((sum,item)=>sum+item.disposalBoxes,0):0;
const fee=document.getElementById('cargo-disposal-edit-fee');
if(fee)fee.textContent='$'+calculateCargoDisposalFee(total).toFixed(2);
}
function toggleCargoDisposalEditInbound(card) {
const modal=document.getElementById('cargo-disposal-edit-modal');
if(!modal)return;
modal.querySelectorAll('.cargo-disposal-inbound-card').forEach(item=>{item.classList.remove('is-selected');if(item!==card)item.classList.remove('expanded');});
card.classList.add('is-selected','expanded');
renderCargoDisposalEditFee();
}
function renderCargoDisposalEditInboundCards(query) {
const container=document.getElementById('cargo-disposal-edit-inbound-cards');
if(!container)return;
const order=loadCargoDisposalOrders().find(item=>String(item.id)===String(cargoDisposalEditingId));
const normalizedQuery=String(query==null?(document.getElementById('cargo-disposal-edit-inbound-search')?.value||''):query).trim().toLowerCase();
const inboundCodes=cloneCargoDisposalInboundSeed().filter(inbound=>!normalizedQuery||inbound.inboundNo.toLowerCase().includes(normalizedQuery));
if(!inboundCodes.length){container.innerHTML='<div class="empty-text">未找到匹配的入仓码,请检查后重新搜索</div>';renderCargoDisposalEditFee();return;}
container.innerHTML=inboundCodes.map(inbound=>{const selected=Boolean(order&&order.inboundNo===inbound.inboundNo);return '<div class="cargo-disposal-inbound-card '+(selected?'expanded is-selected':'')+'" data-inbound-id="'+inbound.id+'"><div class="cargo-disposal-inbound-summary" role="button" tabindex="0" onclick="toggleCargoDisposalEditInbound(this.parentElement)" onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();toggleCargoDisposalEditInbound(this.parentElement)}"><div><strong>'+escapeCargoDisposalHtml(inbound.inboundNo)+'</strong><div class="cargo-disposal-inbound-meta">'+escapeCargoDisposalHtml(inbound.warehouse)+' · '+escapeCargoDisposalHtml(inbound.receivedAt)+' · 可用 '+inbound.availableBoxes+' 箱</div></div><span aria-hidden="true">'+(selected?'⌃':'⌄')+'</span></div><div class="cargo-disposal-inbound-body"><div class="cargo-disposal-table-wrap"><table class="cargo-disposal-table"><thead><tr><th>商品 SKU</th><th>中文品名</th><th>可用箱数</th><th><span class="required">*</span>废弃箱数</th></tr></thead><tbody>'+inbound.items.map(item=>{const existing=selected?(order.items||[]).find(orderItem=>orderItem.sku===item.sku):null;return '<tr><td>'+escapeCargoDisposalHtml(item.sku)+'</td><td>'+escapeCargoDisposalHtml(item.name)+'</td><td>'+item.availableBoxes+'</td><td><input class="cargo-disposal-quantity cargo-disposal-edit-quantity" type="number" min="0" max="'+item.availableBoxes+'" value="'+Number(existing?.disposalBoxes||0)+'" data-item-id="'+item.id+'" aria-label="'+escapeCargoDisposalHtml(item.sku)+'废弃箱数" oninput="renderCargoDisposalEditFee()"></td></tr>';}).join('')+'</tbody></table></div></div></div>';}).join('');
renderCargoDisposalEditFee();
}
function filterCargoDisposalEditInboundCards() {
const query=(document.getElementById('cargo-disposal-edit-inbound-search')?.value||'').trim().toLowerCase();
renderCargoDisposalEditInboundCards(query);
}
function openCargoDisposalEdit(orderId) {
const order=loadCargoDisposalOrders().find(item=>String(item.id)===String(orderId));
if(!order){showToast('未找到货物废弃订单');return;}
cargoDisposalEditingId=String(order.id);
cargoDisposalEditSubmitting=false;
const search=document.getElementById('cargo-disposal-edit-inbound-search');
if(search)search.value='';
const error=document.getElementById('cargo-disposal-edit-error');
if(error)error.textContent='';
renderCargoDisposalEditInboundCards();
const modal=document.getElementById('cargo-disposal-edit-modal');
if(modal)modal.classList.add('show');
if(search)search.focus();
}
function closeCargoDisposalEdit() {
const modal=document.getElementById('cargo-disposal-edit-modal');
if(modal)modal.classList.remove('show');
cargoDisposalEditingId='';
cargoDisposalEditSubmitting=false;
}
function validateCargoDisposalEdit() {
const selection=getCargoDisposalEditSelection();
const error=document.getElementById('cargo-disposal-edit-error');
if(error)error.textContent='';
if(!selection){if(error)error.textContent='请选择一个入仓码';return false;}
const inboundById=new Map(selection.inbound.items.map(item=>[item.id,item]));
let total=0;
for(const item of selection.items){const availableBoxes=inboundById.get(item.id)?.availableBoxes||0;if(!Number.isInteger(item.disposalBoxes)||item.disposalBoxes<0||item.disposalBoxes>availableBoxes){if(error)error.textContent='废弃箱数必须是整数且不能超过可用箱数';return false;}total+=item.disposalBoxes;}
if(total<1){if(error)error.textContent='请至少填写一条大于 0 的废弃箱数';return false;}
return true;
}
function mergeCargoDisposalOrder(original,selection,feePerBox) {
const disposalBoxes=selection.items.reduce((sum,item)=>sum+item.disposalBoxes,0);
return Object.assign({},original,{inboundNo:selection.inbound.inboundNo,warehouse:selection.inbound.warehouse,disposalBoxes,feeAmount:disposalBoxes*feePerBox,items:selection.items.filter(item=>item.disposalBoxes>0).map(item=>{const source=selection.inbound.items.find(sourceItem=>sourceItem.id===item.id);return {sku:source.sku,name:source.name,availableBoxes:source.availableBoxes,disposalBoxes:item.disposalBoxes};})});
}
function submitCargoDisposalEdit(event) {
if(event)event.preventDefault();
if(cargoDisposalEditSubmitting)return false;
cargoDisposalEditSubmitting=true;
const submit=document.getElementById('cargo-disposal-edit-submit');
if(submit)submit.disabled=true;
try{
if(!validateCargoDisposalEdit())return false;
const selection=getCargoDisposalEditSelection();
const orders=loadCargoDisposalOrders();
const original=orders.find(item=>String(item.id)===String(cargoDisposalEditingId));
if(!original){showToast('未找到货物废弃订单');return false;}
const updated=mergeCargoDisposalOrder(original,selection,CARGO_DISPOSAL_FEE_PER_BOX);
if(!saveCargoDisposalOrders(orders.map(item=>String(item.id)===String(cargoDisposalEditingId)?updated:item))){showToast('保存失败,请检查浏览器存储设置');return false;}
cargoDisposalFilteredOrders=null;
renderCargoDisposalList();
closeCargoDisposalEdit();
showToast('货物废弃单修改成功');
return false;
}finally{cargoDisposalEditSubmitting=false;if(submit)submit.disabled=false;}
}
function openCargoDisposalDetail(orderId) {
const order=loadCargoDisposalOrders().find(item=>String(item.id)===String(orderId));
if(!order){showToast('未找到货物废弃订单');return;}
document.getElementById('cargo-disposal-detail-order-no').textContent=order.orderNo||'—';
const status=document.getElementById('cargo-disposal-detail-status');
if(status){status.textContent=order.status||'待处理';status.classList.remove('is-processing','is-done');if(order.status==='处理中')status.classList.add('is-processing');if(order.status==='已完成')status.classList.add('is-done');}
const documentFields=document.getElementById('cargo-disposal-detail-document');
if(documentFields)documentFields.innerHTML=[['货物废弃单号',order.orderNo,true],['关联入库单',order.inboundNo,true],['操作仓',order.warehouse],['订单状态',order.status,true],['创建时间',order.createdAt]].map(field=>'<div class="labeling-detail-field"><dt>'+escapeCargoDisposalHtml(field[0])+'</dt><dd class="'+(field[2]?'is-emphasis':'')+'">'+escapeCargoDisposalHtml(field[1]||'—')+'</dd></div>').join('');
const items=document.getElementById('cargo-disposal-detail-items');
if(items)items.innerHTML=(order.items||[]).map(item=>'<tr><td>'+escapeCargoDisposalHtml(item.sku)+'</td><td>'+escapeCargoDisposalHtml(item.name)+'</td><td>'+Number(item.availableBoxes||0)+'</td><td>'+Number(item.disposalBoxes||0)+'</td></tr>').join('');
const fees=document.getElementById('cargo-disposal-detail-fees');
if(fees)fees.innerHTML=[['计费箱数',order.disposalBoxes+' 箱'],['弃货费单价','$'+Number(CARGO_DISPOSAL_FEE_PER_BOX).toFixed(2)+' / 箱'],['弃货费总计','$'+Number(order.feeAmount).toFixed(2)+' '+(order.currency||'USD'),true]].map(field=>'<div class="labeling-detail-field"><dt>'+field[0]+'</dt><dd class="'+(field[2]?'is-emphasis':'')+'">'+escapeCargoDisposalHtml(field[1])+'</dd></div>').join('');
document.getElementById('cargo-disposal-detail-tail-order-no').textContent=order.tailOrderNo||'—';
document.getElementById('cargo-disposal-detail-outbound-order-no').textContent=order.outboundOrderNo||'—';
document.getElementById('cargo-disposal-detail-billing-no').textContent=order.billingNo||'—';
navigateCargoDisposalPage('cargo-disposal-detail');
window.scrollTo(0,0);
}
document.addEventListener('DOMContentLoaded', function(){ renderCargoDisposalList(); document.addEventListener('keydown',event=>{if(event.key==='Escape'&&document.getElementById('cargo-disposal-edit-modal')?.classList.contains('show'))closeCargoDisposalEdit();}); });
function renderStorageTransferList() {
const tbody = document.getElementById('storage-transfer-list-body');
if (!tbody) return;
const orders = storageTransferFilteredOrders === null
? loadStorageTransferOrders()
: storageTransferFilteredOrders;
const totalPages = Math.max(1, Math.ceil(orders.length / STORAGE_TRANSFER_PAGE_SIZE));
storageTransferCurrentPage = Math.min(Math.max(1, storageTransferCurrentPage), totalPages);
const start = (storageTransferCurrentPage - 1) * STORAGE_TRANSFER_PAGE_SIZE;
const rows = orders.slice(start, start + STORAGE_TRANSFER_PAGE_SIZE);
tbody.innerHTML = rows.length ? rows.map((order, index) => {
const id = escapeStorageTransferHtml(order.id);
return '<tr>' +
'<td>' + (start + index + 1) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.orderNo) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.inboundNo) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.boxCount) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.warehouse) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.fbaCode) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.shipmentId) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.deliveryMethod) + '</td>' +
'<td>' + (order.relabel === 'yes' ? '是' : '否') + '</td>' +
'<td>' + escapeStorageTransferHtml(order.tailOrderNo) + '</td>' +
'<td>' + escapeStorageTransferHtml(order.relabelOrderNo || '-') + '</td>' +
'<td><span class="status-badge status-processing">' + escapeStorageTransferHtml(order.status) + '</span></td>' +
'<td>' + escapeStorageTransferHtml(order.createdAt) + '</td>' +
'<td><span class="storage-transfer-actions"><button type="button" class="btn-text" data-storage-transfer-id="' + id + '" onclick="openStorageTransferDetail(this.dataset.storageTransferId)">查看</button><button type="button" class="btn-text" data-storage-transfer-id="' + id + '" onclick="openStorageTransferEdit(this.dataset.storageTransferId)">编辑</button></span></td>' +
'</tr>';
}).join('') : '<tr class="empty-state"><td colspan="14"><div class="empty-text">暂无符合条件的仓储转运订单</div></td></tr>';
const total = document.getElementById('storage-transfer-total');
const pageNumber = document.getElementById('storage-transfer-page-number');
const previous = document.getElementById('storage-transfer-prev');
const next = document.getElementById('storage-transfer-next');
if (total) total.textContent = orders.length;
if (pageNumber) pageNumber.textContent = storageTransferCurrentPage + ' / ' + totalPages;
if (previous) previous.disabled = storageTransferCurrentPage <= 1;
if (next) next.disabled = storageTransferCurrentPage >= totalPages;
}
function filterStorageTransferOrders() {
const value = id => (document.getElementById(id)?.value || '').trim().toLowerCase();
const orderNo = value('storage-transfer-filter-order-no');
const inboundNo = value('storage-transfer-filter-inbound-no');
const warehouse = value('storage-transfer-filter-warehouse');
const fbaCode = value('storage-transfer-filter-fba');
const relabel = value('storage-transfer-filter-relabel');
const status = value('storage-transfer-filter-status');
const createdStart = value('storage-transfer-filter-created-start');
const createdEnd = value('storage-transfer-filter-created-end');
if (createdStart && createdEnd && createdStart > createdEnd) {
showToast('创建开始时间不能晚于结束时间');
return;
}
storageTransferFilteredOrders = loadStorageTransferOrders().filter(order => {
const createdDate = String(order.createdAt || '').slice(0, 10);
return (!orderNo || String(order.orderNo || '').toLowerCase().includes(orderNo)) &&
(!inboundNo || String(order.inboundNo || '').toLowerCase().includes(inboundNo)) &&
(!warehouse || String(order.warehouse || '').toLowerCase() === warehouse) &&
(!fbaCode || String(order.fbaCode || '').toLowerCase().includes(fbaCode)) &&
(!relabel || String(order.relabel || '').toLowerCase() === relabel) &&
(!status || String(order.status || '').toLowerCase() === status) &&
(!createdStart || createdDate >= createdStart) && (!createdEnd || createdDate <= createdEnd);
});
storageTransferCurrentPage = 1;
renderStorageTransferList();
}
function resetStorageTransferFilters() {
[
'storage-transfer-filter-order-no', 'storage-transfer-filter-inbound-no',
'storage-transfer-filter-warehouse', 'storage-transfer-filter-fba',
'storage-transfer-filter-relabel', 'storage-transfer-filter-status',
'storage-transfer-filter-created-start', 'storage-transfer-filter-created-end'
].forEach(id => {
const control = document.getElementById(id);
if (control) control.value = '';
});
storageTransferFilteredOrders = null;
storageTransferCurrentPage = 1;
renderStorageTransferList();
}
function changeStorageTransferPage(delta) {
const orders = storageTransferFilteredOrders === null ? loadStorageTransferOrders() : storageTransferFilteredOrders;
const totalPages = Math.max(1, Math.ceil(orders.length / STORAGE_TRANSFER_PAGE_SIZE));
const nextPage = storageTransferCurrentPage + Number(delta || 0);
if (nextPage < 1 || nextPage > totalPages) return;
storageTransferCurrentPage = nextPage;
renderStorageTransferList();
}
function openStorageTransferCreate() {
const form = document.getElementById('storage-transfer-form');
if (form) {
form.reset();
form.elements.relabel.checked = true;
}
toggleStorageTransferRelabel();
storageTransferSubmitting = false;
navigateStorageTransferPage('storage-transfer-create');
}
function openStorageTransferList() {
renderStorageTransferList();
navigateStorageTransferPage('storage-transfer-list');
}
function cancelStorageTransferCreate() {
openStorageTransferList();
}
function setStorageTransferFormValue(form, name, value) {
const control = form && form.elements ? form.elements[name] : null;
if (control) control.value = value == null ? '' : String(value);
}
function setStorageTransferEditExistingFile(kind, fileName) {
const hidden = document.getElementById('storage-transfer-edit-' + kind + '-existing');
const label = document.getElementById('storage-transfer-edit-' + kind + '-current');
const value = String(fileName || '');
if (hidden) hidden.value = value;
if (label) label.innerHTML = '当前文件:<strong>' + escapeStorageTransferHtml(value || '无') + '</strong>';
}
function getStorageTransferLegacyRelabelFile(order) {
const cargo = Array.isArray(order && order.cargo) ? order.cargo : [];
const item = cargo.find(entry => entry && entry.fileName);
return item ? String(item.fileName || '') : '';
}
function getStorageTransferEditExistingFiles(order, requirement) {
const carton = String(order && order.relabelCartonLabel || '');
const sku = String(order && order.relabelSkuLabel || '');
if (carton || sku) return { carton: carton, sku: sku };
const legacy = getStorageTransferLegacyRelabelFile(order);
return requirement === 'Case2'
? { carton: '', sku: legacy }
: { carton: legacy, sku: '' };
}
function openStorageTransferEdit(orderId) {
const order = loadStorageTransferOrders().find(item => String(item.id) === String(orderId));
const modal = document.getElementById('storage-transfer-edit-modal');
const form = document.getElementById('storage-transfer-edit-form');
if (!order || !modal || !form) {
showToast('未找到仓储转运单');
return;
}
storageTransferEditingId = String(order.id);
storageTransferEditingSubmitting = false;
form.reset();
['inboundNo', 'warehouse', 'fbaCode', 'address', 'boxCount', 'deliveryMethod',
'expectedArrival', 'shipmentId', 'trackingId', 'boxId', 'note', 'relabelRemark'].forEach(name => {
setStorageTransferFormValue(form, name, order[name]);
});
form.elements.relabel.checked = order.relabel === 'yes';
const requirement = order.relabelRequirement || 'Case1';
form.elements.relabelRequirement.value = requirement;
const existingFiles = getStorageTransferEditExistingFiles(order, requirement);
setStorageTransferEditExistingFile('carton', existingFiles.carton);
setStorageTransferEditExistingFile('sku', existingFiles.sku);
setStorageTransferEditExistingFile('relation', order.relabelRelationFile || '');
toggleStorageTransferEditRelabel();
modal.classList.add('show');
const title = document.getElementById('storage-transfer-edit-title');
if (title) title.textContent = '编辑仓储转运单 · ' + (order.orderNo || '');
const first = form.elements.inboundNo;
if (first && first.focus) setTimeout(() => first.focus(), 0);
}
function closeStorageTransferEdit() {
const modal = document.getElementById('storage-transfer-edit-modal');
if (modal) modal.classList.remove('show');
storageTransferEditingId = null;
storageTransferEditingSubmitting = false;
}
function captureStorageTransferEditFileName(input, labelId) {
const label = document.getElementById(labelId);
const fileName = input && input.files && input.files[0] ? input.files[0].name : '';
if (label && fileName) label.innerHTML = '新文件:<strong>' + escapeStorageTransferHtml(fileName) + '</strong>';
updateStorageTransferEditRelabelRequirements();
}
function toggleStorageTransferEditRelabel() {
const form = document.getElementById('storage-transfer-edit-form');
if (!form) return;
const enabled = Boolean(form.elements.relabel && form.elements.relabel.checked);
const fields = document.getElementById('storage-transfer-edit-relabel-fields');
const empty = document.getElementById('storage-transfer-edit-relabel-empty');
const status = document.getElementById('storage-transfer-edit-relabel-status');
if (fields) fields.hidden = !enabled;
if (empty) empty.hidden = enabled;
if (status) {
status.textContent = enabled ? '已开启' : '未开启';
status.classList.toggle('is-on', enabled);
}
updateStorageTransferEditRelabelRequirements();
}
function updateStorageTransferEditRelabelRequirements() {
const form = document.getElementById('storage-transfer-edit-form');
if (!form) return;
const enabled = Boolean(form.elements.relabel && form.elements.relabel.checked);
const requirement = form.elements.relabelRequirement ? form.elements.relabelRequirement.value : 'Case1';
const carton = form.elements.relabelCartonLabel;
const sku = form.elements.relabelSkuLabel;
const relation = form.elements.relabelRelationFile;
const remark = form.elements.relabelRemark;
const cartonExisting = document.getElementById('storage-transfer-edit-carton-existing')?.value || '';
const skuExisting = document.getElementById('storage-transfer-edit-sku-existing')?.value || '';
const cartonEnabled = enabled && requirement !== 'Case2';
const skuEnabled = enabled && requirement !== 'Case1';
if (carton) {
carton.disabled = !cartonEnabled;
carton.required = cartonEnabled && !cartonExisting;
}
if (sku) {
sku.disabled = !skuEnabled;
sku.required = skuEnabled && !skuExisting;
}
if (relation) relation.disabled = !enabled;
if (remark) remark.disabled = !enabled;
const cartonCard = document.getElementById('storage-transfer-edit-carton-upload-card');
const skuCard = document.getElementById('storage-transfer-edit-sku-upload-card');
if (cartonCard) {
cartonCard.classList.toggle('is-disabled', !cartonEnabled);
cartonCard.classList.toggle('is-ready', cartonEnabled && Boolean(cartonExisting || (carton.files && carton.files.length)));
}
if (skuCard) {
skuCard.classList.toggle('is-disabled', !skuEnabled);
skuCard.classList.toggle('is-ready', skuEnabled && Boolean(skuExisting || (sku.files && sku.files.length)));
}
const relationExisting = document.getElementById('storage-transfer-edit-relation-existing')?.value || '';
const relationCard = document.getElementById('storage-transfer-edit-relation-upload-card');
const remarkCard = document.getElementById('storage-transfer-edit-relabel-remark-card');
if (relationCard) {
relationCard.classList.toggle('is-disabled', !enabled);
relationCard.classList.toggle('is-ready', enabled && Boolean(relationExisting || (relation.files && relation.files.length)));
}
if (remarkCard) remarkCard.classList.toggle('is-disabled', !enabled);
const hint = document.getElementById('storage-transfer-edit-relabel-hint');
if (hint) {
hint.textContent = enabled
? ({ Case1: '换箱唛:需要上传箱唛', Case2: '换 SKU 标:需要上传 SKU 标签', Case3: '既换箱唛,又换 SKU 标:需要上传箱唛和 SKU 标签' }[requirement] || '')
: '未开启换标,无需上传标签文件';
}
}
function getStorageTransferEditRelabelConfig(form) {
const enabled = Boolean(form && form.elements.relabel && form.elements.relabel.checked);
if (!enabled) return { relabel: 'no', relabelRequirement: '', relabelCartonLabel: '', relabelSkuLabel: '', relabelRelationFile: '', relabelRemark: '' };
const requirement = form.elements.relabelRequirement ? form.elements.relabelRequirement.value : 'Case1';
const fileName = (control, existingId) => control && control.files && control.files[0]
? control.files[0].name
: (document.getElementById(existingId)?.value || '');
return {
relabel: 'yes',
relabelRequirement: requirement,
relabelCartonLabel: requirement === 'Case2' ? '' : fileName(form.elements.relabelCartonLabel, 'storage-transfer-edit-carton-existing'),
relabelSkuLabel: requirement === 'Case1' ? '' : fileName(form.elements.relabelSkuLabel, 'storage-transfer-edit-sku-existing'),
relabelRelationFile: fileName(form.elements.relabelRelationFile, 'storage-transfer-edit-relation-existing'),
relabelRemark: form.elements.relabelRemark ? form.elements.relabelRemark.value.trim() : ''
};
}
function mergeStorageTransferOrder(order, values) {
const editable = ['inboundNo', 'warehouse', 'fbaCode', 'address', 'boxCount', 'deliveryMethod',
'expectedArrival', 'shipmentId', 'trackingId', 'boxId', 'note', 'relabel',
'relabelRequirement', 'relabelCartonLabel', 'relabelSkuLabel', 'relabelRelationFile', 'relabelRemark', 'relabelOrderNo'];
const merged = Object.assign({}, order);
editable.forEach(name => {
if (Object.prototype.hasOwnProperty.call(values, name)) merged[name] = name === 'boxCount' ? Number(values[name]) : values[name];
});
return merged;
}
function refreshStorageTransferListAfterEdit() {
const filterIds = [
'storage-transfer-filter-order-no', 'storage-transfer-filter-inbound-no',
'storage-transfer-filter-warehouse', 'storage-transfer-filter-fba',
'storage-transfer-filter-relabel', 'storage-transfer-filter-status',
'storage-transfer-filter-created-start', 'storage-transfer-filter-created-end'
];
const hasActiveFilter = filterIds.some(id => String(document.getElementById(id)?.value || '').trim());
if (hasActiveFilter) {
filterStorageTransferOrders();
return;
}
storageTransferFilteredOrders = null;
renderStorageTransferList();
}
function submitStorageTransferEdit(event) {
if (event) event.preventDefault();
if (storageTransferEditingSubmitting) return false;
const form = document.getElementById('storage-transfer-edit-form');
const submitButton = document.getElementById('storage-transfer-edit-submit');
if (!form || !storageTransferEditingId) return false;
storageTransferEditingSubmitting = true;
if (submitButton) submitButton.disabled = true;
try {
if (!form.checkValidity()) {
form.reportValidity();
return false;
}
const orders = loadStorageTransferOrders();
const index = orders.findIndex(item => String(item.id) === String(storageTransferEditingId));
if (index < 0) {
showToast('未找到仓储转运单');
return false;
}
const original = orders[index];
const relabelConfig = getStorageTransferEditRelabelConfig(form);
const values = {
inboundNo: form.elements.inboundNo.value.trim(),
warehouse: form.elements.warehouse.value,
fbaCode: form.elements.fbaCode.value.trim(),
address: form.elements.address.value.trim(),
boxCount: Number(form.elements.boxCount.value),
deliveryMethod: form.elements.deliveryMethod.value,
expectedArrival: form.elements.expectedArrival.value,
shipmentId: form.elements.shipmentId.value.trim(),
trackingId: form.elements.trackingId.value.trim(),
boxId: form.elements.boxId.value.trim(),
note: form.elements.note.value.trim(),
relabel: relabelConfig.relabel,
relabelRequirement: relabelConfig.relabelRequirement,
relabelCartonLabel: relabelConfig.relabelCartonLabel,
relabelSkuLabel: relabelConfig.relabelSkuLabel,
relabelRelationFile: relabelConfig.relabelRelationFile,
relabelRemark: relabelConfig.relabelRemark,
relabelOrderNo: relabelConfig.relabel === 'yes' ? (original.relabelOrderNo || generateStorageTransferNumber('HB', orders)) : ''
};
orders[index] = mergeStorageTransferOrder(original, values);
if (!saveStorageTransferOrders(orders)) {
showToast('保存失败,请检查浏览器存储设置');
return false;
}
refreshStorageTransferListAfterEdit();
closeStorageTransferEdit();
showToast('仓储转运单修改成功');
return false;
} finally {
storageTransferEditingSubmitting = false;
if (submitButton) submitButton.disabled = false;
}
}
function addStorageTransferCargoRow(cargo) {
const tbody = document.getElementById('storage-transfer-cargo-body');
if (!tbody) return;
const item = cargo || {};
const row = document.createElement('tr');
row.innerHTML =
'<td><input class="cell-input storage-transfer-cargo-sku" type="text" value="' + escapeStorageTransferHtml(item.sku) + '" aria-label="商品SKU"></td>' +
'<td><input class="cell-input storage-transfer-cargo-name" type="text" value="' + escapeStorageTransferHtml(item.name) + '" aria-label="中文品名"></td>' +
'<td><input class="cell-input storage-transfer-cargo-boxes" type="number" min="1" value="' + escapeStorageTransferHtml(item.boxes || '') + '" aria-label="箱数"></td>' +
'<td class="storage-transfer-relabel-column"><input class="cell-input storage-transfer-cargo-relabel-input storage-transfer-cargo-relabel-sku" type="text" value="' + escapeStorageTransferHtml(item.relabelSku) + '" aria-label="换标后SKU"></td>' +
'<td class="storage-transfer-relabel-column"><input class="cell-input storage-transfer-cargo-relabel-input storage-transfer-cargo-requirement" type="text" value="' + escapeStorageTransferHtml(item.requirement) + '" aria-label="贴标要求"></td>' +
'<td class="storage-transfer-relabel-column"><input class="storage-transfer-cargo-relabel-input storage-transfer-cargo-file" type="file" aria-label="换标文件" onchange="captureStorageTransferFileName(this)"><input class="storage-transfer-cargo-file-name" type="hidden" value="' + escapeStorageTransferHtml(item.fileName) + '"><span class="storage-transfer-file-name">' + escapeStorageTransferHtml(item.fileName || '未选择文件') + '</span></td>' +
'<td><button type="button" class="btn-text" style="color:#ff4d4f;" onclick="removeStorageTransferCargoRow(this)">删除</button></td>';
tbody.appendChild(row);
toggleStorageTransferRelabel();
}
function removeStorageTransferCargoRow(button) {
const row = button.closest('tr');
if (row) row.remove();
if (!document.querySelector('#storage-transfer-cargo-body tr')) addStorageTransferCargoRow();
}
function captureStorageTransferFileName(input) {
const cell = input.closest('td');
if (!cell) return;
const fileName = input.files && input.files[0] ? input.files[0].name : '';
const hidden = cell.querySelector('.storage-transfer-cargo-file-name');
const label = cell.querySelector('.storage-transfer-file-name');
if (hidden) hidden.value = fileName;
if (label) label.textContent = fileName || '未选择文件';
}
function getStorageTransferRelationTemplateRows() {
return [['箱唛', '需贴箱唛', '需贴SKU标', '备注']];
}
function downloadStorageTransferRelationTemplate() {
if (typeof XLSX === 'undefined') {
showToast('模板组件加载失败,请刷新页面后重试');
return;
}
const worksheet = XLSX.utils.aoa_to_sheet(getStorageTransferRelationTemplateRows());
worksheet['!cols'] = [{ wch: 24 }, { wch: 14 }, { wch: 14 }, { wch: 32 }];
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '关联明细');
XLSX.writeFile(workbook, '仓储转运关联模板.xlsx');
}
function toggleStorageTransferRelabel() {
const toggle = document.getElementById('storage-transfer-relabel-toggle');
const enabled = Boolean(toggle && toggle.checked);
const fields = document.getElementById('storage-transfer-relabel-fields');
const empty = document.getElementById('storage-transfer-relabel-empty');
const status = document.getElementById('storage-transfer-relabel-status');
if (fields) fields.hidden = !enabled;
if (empty) empty.hidden = enabled;
if (status) {
status.textContent = enabled ? '已开启' : '未开启';
status.classList.toggle('is-on', enabled);
}
updateStorageTransferRelabelRequirements();
}
function updateStorageTransferRelabelRequirements() {
const form = document.getElementById('storage-transfer-form');
if (!form) return;
const enabled = Boolean(form.elements.relabel && form.elements.relabel.checked);
const requirement = form.elements.relabelRequirement ? form.elements.relabelRequirement.value : 'Case1';
const carton = form.elements.relabelCartonLabel;
const sku = form.elements.relabelSkuLabel;
const relation = form.elements.relabelRelationFile;
const remark = form.elements.relabelRemark;
const cartonEnabled = enabled && requirement !== 'Case2';
const skuEnabled = enabled && requirement !== 'Case1';
if (carton) {
carton.disabled = !cartonEnabled;
carton.required = cartonEnabled;
}
if (sku) {
sku.disabled = !skuEnabled;
sku.required = skuEnabled;
}
if (relation) relation.disabled = !enabled;
if (remark) remark.disabled = !enabled;
const cartonCard = carton && carton.closest ? carton.closest('.storage-transfer-upload-card') : null;
const skuCard = sku && sku.closest ? sku.closest('.storage-transfer-upload-card') : null;
if (cartonCard) {
cartonCard.classList.toggle('is-disabled', !cartonEnabled);
cartonCard.classList.toggle('is-ready', cartonEnabled && Boolean(carton.files && carton.files.length));
}
if (skuCard) {
skuCard.classList.toggle('is-disabled', !skuEnabled);
skuCard.classList.toggle('is-ready', skuEnabled && Boolean(sku.files && sku.files.length));
}
const relationCard = document.getElementById('storage-transfer-relation-upload-card');
const remarkCard = document.getElementById('storage-transfer-relabel-remark-card');
if (relationCard) {
relationCard.classList.toggle('is-disabled', !enabled);
relationCard.classList.toggle('is-ready', enabled && Boolean(relation.files && relation.files.length));
}
if (remarkCard) remarkCard.classList.toggle('is-disabled', !enabled);
const hint = document.getElementById('storage-transfer-relabel-hint');
if (hint) {
hint.textContent = enabled
? ({
Case1: '换箱唛:需要上传箱唛',
Case2: '换 SKU 标:需要上传 SKU 标签',
Case3: '既换箱唛,又换 SKU 标:需要上传箱唛和 SKU 标签'
}[requirement] || '')
: '未开启换标,无需上传标签文件';
}
}
function getStorageTransferRelabelConfig(form) {
const enabled = Boolean(form && form.elements.relabel && form.elements.relabel.checked);
if (!enabled) {
return { relabel: 'no', relabelRequirement: '', relabelCartonLabel: '', relabelSkuLabel: '', relabelRelationFile: '', relabelRemark: '' };
}
const requirement = form.elements.relabelRequirement ? form.elements.relabelRequirement.value : 'Case1';
const carton = form.elements.relabelCartonLabel;
const sku = form.elements.relabelSkuLabel;
return {
relabel: 'yes',
relabelRequirement: requirement,
relabelCartonLabel: requirement === 'Case2' ? '' : (carton && carton.files && carton.files[0] ? carton.files[0].name : ''),
relabelSkuLabel: requirement === 'Case1' ? '' : (sku && sku.files && sku.files[0] ? sku.files[0].name : ''),
relabelRelationFile: form.elements.relabelRelationFile && form.elements.relabelRelationFile.files && form.elements.relabelRelationFile.files[0]
? form.elements.relabelRelationFile.files[0].name
: '',
relabelRemark: form.elements.relabelRemark ? form.elements.relabelRemark.value.trim() : ''
};
}
function validateStorageTransferForm() {
const form = document.getElementById('storage-transfer-form');
if (!form) return false;
if (!form.checkValidity()) {
form.reportValidity();
return false;
}
return true;
}
function collectStorageTransferCargo() {
return Array.from(document.querySelectorAll('#storage-transfer-cargo-body tr')).map(row => ({
sku: row.querySelector('.storage-transfer-cargo-sku').value.trim(),
name: row.querySelector('.storage-transfer-cargo-name').value.trim(),
boxes: Number(row.querySelector('.storage-transfer-cargo-boxes').value),
relabelSku: row.querySelector('.storage-transfer-cargo-relabel-sku').value.trim(),
requirement: row.querySelector('.storage-transfer-cargo-requirement').value.trim(),
fileName: row.querySelector('.storage-transfer-cargo-file-name').value.trim()
}));
}
function generateStorageTransferNumber(prefix, orders) {
const now = new Date();
const date = now.getFullYear() + String(now.getMonth() + 1).padStart(2, '0') + String(now.getDate()).padStart(2, '0');
const existing = new Set(orders.flatMap(order => [order.orderNo, order.tailOrderNo, order.relabelOrderNo]));
let sequence = String(orders.length + 1).padStart(4, '0');
let candidate = prefix + date + sequence;
while (existing.has(candidate)) {
sequence = String(Number(sequence) + 1).padStart(4, '0');
candidate = prefix + date + sequence;
}
return candidate;
}
function submitStorageTransferOrder(event) {
if (event) event.preventDefault();
if (storageTransferSubmitting) return false;
storageTransferSubmitting = true;
const submitButton = document.getElementById('storage-transfer-submit');
if (submitButton) submitButton.disabled = true;
try {
if (!validateStorageTransferForm()) return false;
const form = document.getElementById('storage-transfer-form');
const orders = loadStorageTransferOrders();
const now = new Date();
const relabelConfig = getStorageTransferRelabelConfig(form);
const relabel = relabelConfig.relabel;
const order = {
id: 'storage-transfer-' + now.getTime(),
orderNo: generateStorageTransferNumber('ZY', orders),
inboundNo: form.elements.inboundNo.value.trim(),
boxCount: Number(form.elements.boxCount.value),
warehouse: form.elements.warehouse.value,
fbaCode: form.elements.fbaCode.value.trim(),
address: form.elements.address.value.trim(),
deliveryMethod: form.elements.deliveryMethod.value,
expectedArrival: form.elements.expectedArrival.value,
shipmentId: form.elements.shipmentId.value.trim(),
trackingId: form.elements.trackingId.value.trim(),
boxId: form.elements.boxId.value.trim(),
note: form.elements.note.value.trim(),
relabel: relabel,
relabelRequirement: relabelConfig.relabelRequirement,
relabelCartonLabel: relabelConfig.relabelCartonLabel,
relabelSkuLabel: relabelConfig.relabelSkuLabel,
relabelRelationFile: relabelConfig.relabelRelationFile,
relabelRemark: relabelConfig.relabelRemark,
tailOrderNo: generateStorageTransferNumber('LC', orders),
relabelOrderNo: relabel === 'yes' ? generateStorageTransferNumber('HB', orders) : '',
status: '待处理',
createdAt: now.getFullYear() + '-' + String(now.getMonth() + 1).padStart(2, '0') + '-' + String(now.getDate()).padStart(2, '0') + ' ' + String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0'),
cargo: []
};
if (!saveStorageTransferOrders([order].concat(orders))) {
showToast('保存失败,请检查浏览器存储设置');
return false;
}
storageTransferFilteredOrders = null;
storageTransferCurrentPage = 1;
renderStorageTransferList();
showToast('仓储转运单提交成功');
navigateStorageTransferPage('storage-transfer-list');
return false;
} finally {
storageTransferSubmitting = false;
if (submitButton) submitButton.disabled = false;
}
}
function storageTransferRelabelRequirementText(value) {
return ({
Case1: '换箱唛',
Case2: '换SKU标',
Case3: '既换箱唛,又换SKU标'
})[value] || value || '-';
}
function getStorageTransferDetailSections(order) {
order = order || {};
return {
document: [
['转运单号', order.orderNo || '-', true],
['进仓编号', order.inboundNo || '-'],
['状态', order.status || '-', true],
['创建时间', order.createdAt || '-']
],
cargo: [
['发货箱数', order.boxCount == null || order.boxCount === '' ? '-' : order.boxCount, true],
['FBA 货件编号', order.shipmentId || '-'],
['FBA 追踪编号', order.trackingId || '-'],
['FBA BOX 编号', order.boxId || '-']
],
delivery: [
['操作仓', order.warehouse || '-'],
['FBA 目的仓代码', order.fbaCode || '-', true],
['派送方式', order.deliveryMethod || '-'],
['期望到达时间', order.expectedArrival ? String(order.expectedArrival).replace('T', ' ') : '-'],
['详细派送地址', order.address || '-']
]
};
}
function getStorageTransferRelabelDetail(order) {
if (!order || order.relabel !== 'yes') return { enabled: false, rows: [['是否换标', '否', true]] };
const cargo = Array.isArray(order.cargo) ? order.cargo : [];
const legacy = cargo.find(item => item && (item.requirement || item.fileName)) || {};
const requirementCode = order.relabelRequirement || '';
const requirement = requirementCode
? storageTransferRelabelRequirementText(requirementCode)
: (legacy.requirement || '-');
const cartonFile = order.relabelCartonLabel || '';
const skuFile = order.relabelSkuLabel || '';
const rows = [['是否换标', '是', true], ['贴标要求', requirement, true]];
if (requirementCode === 'Case1') {
rows.push(['上传箱唛', cartonFile || legacy.fileName || '-', true, true]);
} else if (requirementCode === 'Case2') {
rows.push(['上传 SKU 标签', skuFile || legacy.fileName || '-', true, true]);
} else if (requirementCode === 'Case3') {
rows.push(['上传箱唛', cartonFile || '-', true, true]);
rows.push(['上传 SKU 标签', skuFile || '-', true, true]);
} else {
if (cartonFile) rows.push(['上传箱唛', cartonFile, true, true]);
if (skuFile) rows.push(['上传 SKU 标签', skuFile, true, true]);
if (!cartonFile && !skuFile) rows.push(['上传文件', legacy.fileName || '-', true, true]);
}
if (order.relabelRelationFile) rows.push(['上传关联文件', order.relabelRelationFile, true, true]);
rows.push(['备注', order.relabelRemark || order.note || '-']);
return { enabled: true, rows: rows };
}
function renderStorageTransferRelabelDetail(order) {
const list = document.getElementById('storage-transfer-detail-relabel');
const empty = document.getElementById('storage-transfer-detail-relabel-empty');
if (!list || !empty) return;
const detail = getStorageTransferRelabelDetail(order);
list.hidden = false;
empty.hidden = detail.enabled;
list.innerHTML = detail.rows.map(field => labelingDetailField(field[0], field[1], field[2], field[3])).join('');
}
function openStorageTransferDetail(orderId) {
const order = loadStorageTransferOrders().find(item => String(item.id) === String(orderId));
if (!order) {
showToast('未找到仓储转运单');
return;
}
const sections = getStorageTransferDetailSections(order);
const orderNumber = document.getElementById('storage-transfer-detail-order-no');
if (orderNumber) orderNumber.textContent = order.orderNo || '-';
const status = document.getElementById('storage-transfer-detail-status');
if (status) {
status.textContent = order.status || '待处理';
status.classList.remove('is-processing', 'is-done');
if (order.status === '处理中') status.classList.add('is-processing');
if (order.status === '已完成') status.classList.add('is-done');
}
labelingDetailRows('storage-transfer-detail-document', sections.document);
labelingDetailRows('storage-transfer-detail-cargo', sections.cargo);
labelingDetailRows('storage-transfer-detail-delivery', sections.delivery);
renderStorageTransferRelabelDetail(order);
navigateStorageTransferPage('storage-transfer-detail');
window.scrollTo(0, 0);
}
document.addEventListener('DOMContentLoaded', function() {
renderStorageTransferList();
});
// ===== 新建退货入库单弹窗 =====
function openReturnInModal() {
document.getElementById('return-in-modal').classList.add('show');
}
function closeReturnInModal() {
document.getElementById('return-in-modal').classList.remove('show');
}
function addReturnInProductRow() {
var tbody = document.getElementById('return-in-product-tbody');
var tr = document.createElement('tr');
tr.innerHTML = '<td><input type="text" class="cell-input" placeholder="物流承运商"></td>' +
'<td><input type="text" class="cell-input" placeholder="物流跟踪号"></td>' +
'<td>—</td><td>—</td>' +
'<td><input type="text" class="cell-input" placeholder="FNSKU"></td>' +
'<td><input type="number" class="cell-input" placeholder="数量"></td>' +
'<td><button class="btn-text" style="color:#ff4d4f;" onclick="this.closest(\'tr\').remove()">删除</button></td>';
tbody.appendChild(tr);
}
function submitReturnInModal() {
alert('退货入库单已提交!');
closeReturnInModal();
}
function saveReturnInDraft() {
alert('已保存为草稿');
closeReturnInModal();
}
// ===== Sidebar toggle =====
function toggleSidebar() {
document.querySelector('.sidebar').classList.toggle('collapsed');
document.querySelector('.main').classList.toggle('expanded');
}
// ===== Nav group toggle =====
function toggleNavGroup(header) {
header.classList.toggle('expanded');
const sub = header.nextElementSibling;
if (sub) sub.classList.toggle('expanded');
}
const CLIENT_BILL_DATA=[
{no:'Y2607085',status:'待确认',documentStatus:'待确认',createdAt:'2026-07-05 09:15:22',currency:'CNY',rate:6.8,invoiceDate:'2026-07-10',lastPaymentDate:'2026-07-15',period:7,payable:125600,paid:80000,remark:'',total:125600,returnResultDate:'2026-08-04',refundDueDate:'2026-07-05',confirmTime:''},
{no:'Y2607084',status:'已确认',documentStatus:'已确认',createdAt:'2026-08-13 13:42:00',currency:'CNY',rate:6.8,invoiceDate:'—',lastPaymentDate:'—',period:30,payable:98000,paid:0,remark:'客户已确认',total:98000,returnResultDate:'2026-07-31',refundDueDate:'2026-07-01',confirmTime:'2026-08-14 09:35:00'},
{no:'Y2407069',status:'已确认',documentStatus:'已确认',createdAt:'2024-07-04 13:53:13',currency:'CNY',rate:7.26,invoiceDate:'—',lastPaymentDate:'2024-08-03',period:30,payable:10160.5,paid:0,remark:'',total:10160.5,returnResultDate:'2024-08-03',refundDueDate:'2024-07-04',confirmTime:'2024-07-04 14:10:00'},
{no:'Y2506061',status:'已作废',documentStatus:'已作废',createdAt:'2025-05-02 09:09:57',currency:'CNY',rate:7.11,invoiceDate:'—',lastPaymentDate:'2025-06-01',period:30,payable:267769.75,paid:0,remark:'',total:267769.75,returnResultDate:'2025-06-08',refundDueDate:'2025-05-09',confirmTime:''},
{no:'Y2508047',status:'已确认',documentStatus:'已确认',createdAt:'2025-05-05 16:13:05',currency:'CNY',rate:7.22,invoiceDate:'—',lastPaymentDate:'—',period:30,payable:202858,paid:0,remark:'',total:202858,returnResultDate:'2025-06-05',refundDueDate:'2025-05-05',confirmTime:'2025-05-05 16:35:00'}
];
let confirmingClientBillNo='';
function getClientBillActionState(record){
return {canConfirm:!!record&&record.status==='待确认'};
}
function applyClientBillConfirmation(record,time){
if(!getClientBillActionState(record).canConfirm)return false;
record.status='已确认';
record.documentStatus='已确认';
record.confirmTime=time;
return true;
}
function formatClientBillAmount(value){
return Number(value||0).toLocaleString('zh-CN',{minimumFractionDigits:2,maximumFractionDigits:2});
}
function clientBillStatusClass(status){
return status==='已确认'?'tag-success':status==='待确认'?'tag-warning':'tag-error';
}
function renderClientBills(){
const body=document.getElementById('client-bill-table-body');
if(!body)return;
body.innerHTML=CLIENT_BILL_DATA.map((bill,index)=>{
const confirmAction=getClientBillActionState(bill).canConfirm
? '<button type="button" class="btn-text" onclick="openClientBillConfirm(\''+bill.no+'\')">确认</button>'
: '';
return '<tr><td>'+(index+1)+'</td><td>'+bill.no+'</td><td>'+bill.createdAt+'</td><td>'+bill.currency+'</td><td>'+bill.rate+'</td>'+
'<td>'+bill.invoiceDate+'</td><td>'+bill.lastPaymentDate+'</td><td>'+bill.period+'</td>'+
'<td>'+formatClientBillAmount(bill.payable)+'</td><td>'+formatClientBillAmount(bill.paid)+'</td>'+
'<td>'+(bill.remark||'—')+'</td><td>'+formatClientBillAmount(bill.total)+'</td>'+
'<td>'+bill.returnResultDate+'</td><td>'+bill.refundDueDate+'</td>'+
'<td><span class="tag '+clientBillStatusClass(bill.documentStatus)+'">'+bill.documentStatus+'</span></td>'+
'<td><div style="display:flex;gap:10px;white-space:nowrap"><button type="button" class="btn-text" onclick="viewBillDetail(\''+bill.no+'\')">查看明细</button>'+confirmAction+'</div></td></tr>';
}).join('');
const total=document.getElementById('client-bill-total');
if(total)total.textContent=String(CLIENT_BILL_DATA.length);
}
function openClientBillConfirm(billNo){
const bill=CLIENT_BILL_DATA.find(item=>item.no===billNo);
if(!bill||!getClientBillActionState(bill).canConfirm){
showToast('当前对账单不可确认');
return;
}
confirmingClientBillNo=billNo;
document.getElementById('client-bill-confirm-no').textContent=bill.no;
document.getElementById('client-bill-confirm-currency').textContent=bill.currency;
document.getElementById('client-bill-confirm-amount').textContent=bill.currency+' '+formatClientBillAmount(bill.payable);
document.getElementById('client-bill-confirm-modal').classList.add('show');
}
function closeClientBillConfirm(){
const modal=document.getElementById('client-bill-confirm-modal');
if(modal)modal.classList.remove('show');
confirmingClientBillNo='';
}
function confirmClientBill(){
const bill=CLIENT_BILL_DATA.find(item=>item.no===confirmingClientBillNo);
const now=new Date().toLocaleString('zh-CN',{hour12:false});
if(!bill||!applyClientBillConfirmation(bill,now)){
closeClientBillConfirm();
showToast('当前对账单不可确认');
return;
}
closeClientBillConfirm();
renderClientBills();
showToast('对账单确认成功');
}
document.addEventListener('DOMContentLoaded',renderClientBills);
// ===== View bill detail =====
function viewBillDetail(billNo) {
document.querySelectorAll('.page-content').forEach(p => p.classList.remove('active'));
const target = document.getElementById('page-bill-detail');
if (target) target.classList.add('active');
const label = document.getElementById('detail-bill-no');
if (label) label.textContent = '账单号:' + billNo;
}
// ===== Company info tab toggle =====
function setInfoTab(el, type) {
document.querySelectorAll('.info-tabs .info-tab').forEach(tab => tab.classList.remove('active'));
el.classList.add('active');
document.querySelectorAll('.info-tab-content').forEach(c => c.classList.remove('active'));
const target = document.getElementById('info-' + type);
if (target) target.classList.add('active');
}
// ===== 资质文件查看弹窗 =====
function viewDoc(type, expire, status) {
document.getElementById('doc-modal-title').textContent = type + ' - 文件查看';
document.getElementById('doc-modal-type').textContent = type;
document.getElementById('doc-modal-status').textContent = status;
document.getElementById('doc-modal-expire').textContent = expire;
document.getElementById('doc-placeholder-text').textContent = type + ' 预览';
document.getElementById('doc-modal').classList.add('show');
}
function closeDocModal() {
document.getElementById('doc-modal').classList.remove('show');
}
// ===== Profile tab toggle =====
function setProfileTab(el, type) {
document.querySelectorAll('.info-tabs .info-tab').forEach(tab => tab.classList.remove('active'));
el.classList.add('active');
document.querySelectorAll('.info-tab-content').forEach(c => c.classList.remove('active'));
const target = document.getElementById('profile-' + type);
if (target) target.classList.add('active');
if (type === 'messages') renderProfileMessages();
}
function toggleEditProfile(editing) {
document.getElementById('profile-view').style.display = editing ? 'none' : '';
document.getElementById('profile-edit').style.display = editing ? '' : 'none';
document.getElementById('btn-edit-profile').style.display = editing ? 'none' : '';
document.getElementById('profile-actions').style.display = editing ? '' : 'none';
if (editing) {
var firstInput = document.querySelector('#profile-edit input:not([disabled])');
if (firstInput) firstInput.focus();
}
}
function cancelEditProfile() {
// 恢复原始值
var fields = ['zhangwei','张伟','138****6789','zhangwei@huayuan.com','zhangwei_hy'];
var inputs = document.querySelectorAll('#profile-edit input:not([disabled])');
inputs.forEach(function(input, i) { if (fields[i]) input.value = fields[i]; });
toggleEditProfile(false);
}
function saveProfile() {
alert('保存成功!');
// 将编辑框的值同步到查看模式
var inputs = document.querySelectorAll('#profile-edit input:not([disabled])');
var viewValues = document.querySelectorAll('#profile-view .info-value');
inputs.forEach(function(input, i) {
if (viewValues[i]) viewValues[i].textContent = input.value;
});
toggleEditProfile(false);
}
function changePassword() { alert('密码修改成功!'); }
// ===== 地址管理 =====
const PROFILE_ADDRESS_KEY = 'by_profile_addresses_0826';
const PROFILE_ADDRESS_PAGE_SIZE = 10;
const PROFILE_ADDRESS_SEED = [
{id:'profile-address-la',addressName:'洛杉矶仓',warehouseCode:'',contact:'',phone:'',city:'',stateProvince:'',country:'美国',postalCode:'',enabled:true},
{id:'profile-address-ny',addressName:'纽约仓',warehouseCode:'',contact:'',phone:'',city:'',stateProvince:'',country:'美国',postalCode:'',enabled:true}
];
let profileAddressCurrentPage = 1;
let editingProfileAddressId = '';
let deletingProfileAddressId = '';
let profileAddressReturnFocus = null;
let pickupAutoFilledValues = null;
function cloneProfileAddressSeed() {
return JSON.parse(JSON.stringify(PROFILE_ADDRESS_SEED));
}
function escapeProfileAddressHtml(value) {
return String(value == null ? '' : value).replace(/[&<>"']/g, function(char) {
return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char];
});
}
function normalizeProfileAddress(item) {
if (!item || typeof item !== 'object') return null;
const addressName = typeof item.addressName === 'string' ? item.addressName : item.privateWarehouse;
if (typeof item.id !== 'string' || typeof addressName !== 'string' || typeof item.country !== 'string') return null;
const legacyWarehouseCode = typeof item.warehouseAddress === 'string' && item.warehouseAddress !== '原型未定义' ? item.warehouseAddress : '';
return {
id: item.id,
addressName: addressName,
warehouseCode: typeof item.warehouseCode === 'string' ? item.warehouseCode : legacyWarehouseCode,
detailAddress: typeof item.detailAddress === 'string' ? item.detailAddress : (typeof item.addressLine === 'string' ? item.addressLine : ''),
contact: typeof item.contact === 'string' ? item.contact : '',
phone: typeof item.phone === 'string' ? item.phone : '',
city: typeof item.city === 'string' ? item.city : '',
stateProvince: typeof item.stateProvince === 'string' ? item.stateProvince : '',
country: item.country,
postalCode: typeof item.postalCode === 'string' ? item.postalCode : '',
enabled: item.enabled !== false
};
}
function loadProfileAddresses() {
try {
const raw = localStorage.getItem(PROFILE_ADDRESS_KEY);
if (!raw) return cloneProfileAddressSeed();
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return cloneProfileAddressSeed();
return parsed.map(normalizeProfileAddress).filter(Boolean);
} catch (error) {
return cloneProfileAddressSeed();
}
}
function saveProfileAddresses(addresses) {
try {
localStorage.setItem(PROFILE_ADDRESS_KEY, JSON.stringify(addresses));
return true;
} catch (error) {
return false;
}
}
function paginateProfileAddresses(addresses, currentPage) {
const total = addresses.length;
const pages = Math.max(1, Math.ceil(total / PROFILE_ADDRESS_PAGE_SIZE));
const safePage = Math.min(Math.max(1, Number(currentPage) || 1), pages);
const start = (safePage - 1) * PROFILE_ADDRESS_PAGE_SIZE;
return {total:total,pages:pages,currentPage:safePage,rows:addresses.slice(start,start+PROFILE_ADDRESS_PAGE_SIZE)};
}
function renderProfileAddresses() {
const body = document.getElementById('profile-address-body');
if (!body) return;
const pagination = paginateProfileAddresses(loadProfileAddresses(), profileAddressCurrentPage);
profileAddressCurrentPage = pagination.currentPage;
body.innerHTML = pagination.rows.length ? pagination.rows.map(function(address) {
const id = escapeProfileAddressHtml(address.id);
const value = function(field) { return escapeProfileAddressHtml(field || '-'); };
return '<tr>'+
'<td><div class="profile-address-actions">'+
'<button type="button" class="profile-address-action" aria-label="编辑'+value(address.addressName)+'" onclick="openProfileAddressModal(\''+id+'\')">编辑</button>'+
'<button type="button" class="profile-address-action is-delete" aria-label="删除'+value(address.addressName)+'" onclick="openProfileAddressDeleteModal(\''+id+'\')">删除</button>'+
'</div></td>'+
'<td>'+value(address.addressName)+'</td>'+
'<td>'+value(address.warehouseCode)+'</td>'+
'<td>'+value(address.contact)+'</td>'+
'<td>'+value(address.phone)+'</td>'+
'<td>'+value(address.city)+'</td>'+
'<td>'+value(address.stateProvince)+'</td>'+
'<td>'+value(address.country)+'</td>'+
'<td>'+value(address.postalCode)+'</td>'+
'<td><span class="profile-address-status'+(address.enabled?'':' is-disabled')+'">'+(address.enabled?'启用':'停用')+'</span></td>'+
'</tr>';
}).join('') : '<tr><td class="profile-address-empty" colspan="10">暂无地址,请新增地址</td></tr>';
const total = document.getElementById('profile-address-total');
const pageNumber = document.getElementById('profile-address-page-number');
const previous = document.getElementById('profile-address-prev');
const next = document.getElementById('profile-address-next');
if (total) total.textContent = pagination.total;
if (pageNumber) pageNumber.textContent = pagination.currentPage+' / '+pagination.pages;
if (previous) previous.disabled = pagination.currentPage <= 1;
if (next) next.disabled = pagination.currentPage >= pagination.pages;
}
function changeProfileAddressPage(delta) {
profileAddressCurrentPage += Number(delta) || 0;
renderProfileAddresses();
}
function setProfileAddressFormValue(form, name, value) {
const control = form.elements[name];
if (control) control.value = value == null ? '' : String(value);
}
function openProfileAddressModal(addressId) {
const modal = document.getElementById('profile-address-modal');
const form = document.getElementById('profile-address-form');
if (!modal || !form) return;
const address = addressId ? loadProfileAddresses().find(function(item) { return item.id === addressId; }) : null;
if (addressId && !address) {
showToast('未找到该地址');
return;
}
profileAddressReturnFocus = document.activeElement && typeof document.activeElement.focus === 'function' ? document.activeElement : null;
editingProfileAddressId = address ? address.id : '';
form.reset();
['addressName','warehouseCode','country'].forEach(function(name) { if (form.elements[name]) form.elements[name].setCustomValidity(''); });
setProfileAddressFormValue(form, 'addressName', address ? address.addressName : '');
setProfileAddressFormValue(form, 'warehouseCode', address ? address.warehouseCode : '');
setProfileAddressFormValue(form, 'detailAddress', address ? address.detailAddress : '');
setProfileAddressFormValue(form, 'contact', address ? address.contact : '');
setProfileAddressFormValue(form, 'phone', address ? address.phone : '');
setProfileAddressFormValue(form, 'city', address ? address.city : '');
setProfileAddressFormValue(form, 'stateProvince', address ? address.stateProvince : '');
setProfileAddressFormValue(form, 'postalCode', address ? address.postalCode : '');
setProfileAddressFormValue(form, 'country', address ? address.country : '');
setProfileAddressFormValue(form, 'enabled', address && !address.enabled ? 'false' : 'true');
const title = document.getElementById('profile-address-modal-title');
const error = document.getElementById('profile-address-form-error');
if (title) title.textContent = address ? '编辑地址' : '新增地址';
if (error) error.textContent = '';
modal.classList.add('show');
setTimeout(function() { if (form.elements.addressName) form.elements.addressName.focus(); }, 0);
}
function closeProfileAddressModal() {
const modal = document.getElementById('profile-address-modal');
const error = document.getElementById('profile-address-form-error');
if (modal) modal.classList.remove('show');
if (error) error.textContent = '';
editingProfileAddressId = '';
const focusTarget = profileAddressReturnFocus;
profileAddressReturnFocus = null;
if (focusTarget && typeof focusTarget.focus === 'function') focusTarget.focus();
}
function profileAddressFormData(form) {
return {
addressName: form.elements.addressName.value.trim(),
warehouseCode: form.elements.warehouseCode.value.trim(),
detailAddress: form.elements.detailAddress.value.trim(),
contact: form.elements.contact.value.trim(),
phone: form.elements.phone.value.trim(),
city: form.elements.city.value.trim(),
stateProvince: form.elements.stateProvince.value.trim(),
postalCode: form.elements.postalCode.value.trim(),
country: form.elements.country.value.trim(),
enabled: form.elements.enabled.value === 'true'
};
}
function validateProfileAddressForm(form) {
const requiredFields = [
{name:'addressName',label:'地址名称'},
{name:'warehouseCode',label:'仓库代码'},
{name:'country',label:'国家'}
];
for (const field of requiredFields) {
const control = form.elements[field.name];
const valid = !!control.value.trim();
control.setCustomValidity(valid ? '' : '请输入'+field.label);
if (!valid) {
const error = document.getElementById('profile-address-form-error');
if (error) error.textContent = '请填写'+field.label;
control.reportValidity();
control.focus();
return false;
}
}
return form.reportValidity();
}
function submitProfileAddress(event) {
if (event) event.preventDefault();
const form = document.getElementById('profile-address-form');
if (!form || !validateProfileAddressForm(form)) return false;
const addresses = loadProfileAddresses();
const data = profileAddressFormData(form);
let nextAddresses;
if (editingProfileAddressId) {
const index = addresses.findIndex(function(item) { return item.id === editingProfileAddressId; });
if (index < 0) {
showToast('未找到该地址');
return false;
}
nextAddresses = addresses.slice();
nextAddresses[index] = Object.assign({}, addresses[index], data);
} else {
const id = 'profile-address-'+Date.now()+'-'+Math.random().toString(36).slice(2,7);
nextAddresses = [Object.assign({id:id}, data)].concat(addresses);
profileAddressCurrentPage = 1;
}
if (!saveProfileAddresses(nextAddresses)) {
const error = document.getElementById('profile-address-form-error');
if (error) error.textContent = '保存失败,请检查浏览器存储设置';
showToast('保存失败,请检查浏览器存储设置');
return false;
}
const wasEditing = !!editingProfileAddressId;
closeProfileAddressModal();
renderProfileAddresses();
renderPickupAddressOptions();
showToast(wasEditing ? '地址修改成功' : '地址新增成功');
return false;
}
function openProfileAddressDeleteModal(addressId) {
const address = loadProfileAddresses().find(function(item) { return item.id === addressId; });
const modal = document.getElementById('profile-address-delete-modal');
if (!address || !modal) {
showToast('未找到该地址');
return;
}
profileAddressReturnFocus = document.activeElement && typeof document.activeElement.focus === 'function' ? document.activeElement : null;
deletingProfileAddressId = address.id;
const message = document.getElementById('profile-address-delete-message');
if (message) message.textContent = '确认删除“'+address.addressName+'”吗?';
modal.classList.add('show');
setTimeout(function() { const button=modal.querySelector('.profile-address-delete-confirm');if(button)button.focus(); }, 0);
}
function closeProfileAddressDeleteModal() {
const modal = document.getElementById('profile-address-delete-modal');
if (modal) modal.classList.remove('show');
deletingProfileAddressId = '';
const focusTarget = profileAddressReturnFocus;
profileAddressReturnFocus = null;
if (focusTarget && typeof focusTarget.focus === 'function') focusTarget.focus();
}
function confirmDeleteProfileAddress() {
if (!deletingProfileAddressId) return;
const addresses = loadProfileAddresses();
const nextAddresses = addresses.filter(function(item) { return item.id !== deletingProfileAddressId; });
if (nextAddresses.length === addresses.length) {
closeProfileAddressDeleteModal();
showToast('未找到该地址');
return;
}
if (!saveProfileAddresses(nextAddresses)) {
showToast('保存失败,请检查浏览器存储设置');
return;
}
closeProfileAddressDeleteModal();
renderProfileAddresses();
renderPickupAddressOptions();
showToast('地址删除成功');
}
function formatPickupAddress(address) {
if (!address) return '';
const savedDetail = String(address.detailAddress || address.addressLine || '').trim();
if (savedDetail) return savedDetail;
const parts = [address.stateProvince, address.city, address.country, address.postalCode, address.warehouseCode]
.map(function(value) { return String(value || '').trim(); })
.filter(Boolean);
return parts.join(',') || String(address.addressName || '').trim();
}
function renderPickupAddressOptions(addresses) {
const select = document.getElementById('pickup-address-select');
if (!select) return;
const currentValue = select.value;
const available = (addresses || loadProfileAddresses()).filter(function(address) { return address && address.enabled !== false; });
const options = ['<option value="">请选择我的地址</option>'];
available.forEach(function(address) {
options.push('<option value="'+escapeProfileAddressHtml(address.id)+'">'+escapeProfileAddressHtml(address.addressName)+'</option>');
});
if (!available.length) options.push('<option value="" disabled>暂无可用地址,请先在地址管理中新增</option>');
select.innerHTML = options.join('');
if (available.some(function(address) { return address.id === currentValue; })) {
select.value = currentValue;
} else if (currentValue) {
select.value = '';
applyPickupAddress('');
}
}
function applyPickupAddress(addressId) {
const contact = document.getElementById('pickup-contact');
const phone = document.getElementById('pickup-phone');
const detail = document.getElementById('pickup-address-detail');
if (!addressId) {
if (pickupAutoFilledValues) {
if (contact && contact.value === pickupAutoFilledValues.contact) contact.value = '';
if (phone && phone.value === pickupAutoFilledValues.phone) phone.value = '';
if (detail && detail.value === pickupAutoFilledValues.detail) detail.value = '';
}
pickupAutoFilledValues = null;
return;
}
const address = loadProfileAddresses().find(function(item) { return item.id === addressId && item.enabled !== false; });
if (!address) return;
const values = {contact:address.contact || '', phone:address.phone || '', detail:formatPickupAddress(address)};
if (contact) contact.value = values.contact;
if (phone) phone.value = values.phone;
if (detail) detail.value = values.detail;
pickupAutoFilledValues = values;
}
document.addEventListener('DOMContentLoaded', function() {
renderProfileAddresses();
renderPickupAddressOptions();
});
document.addEventListener('keydown', function(event) {
if (event.key !== 'Escape') return;
const deleteModal = document.getElementById('profile-address-delete-modal');
const editModal = document.getElementById('profile-address-modal');
if (deleteModal && deleteModal.classList.contains('show')) closeProfileAddressDeleteModal();
else if (editModal && editModal.classList.contains('show')) closeProfileAddressModal();
});
// ===== 消息通知 =====
const PROFILE_MESSAGE_KEY = 'by_profile_messages_0831';
const PROFILE_MESSAGE_SEED = [
{id:'profile-message-1',category:'订单动态',title:'头程订单已进入运输环节',summary:'订单 TC202608290018 已完成装柜,正在安排离港。',content:'您的头程订单 TC202608290018 已完成装柜,当前正在安排离港。后续节点更新后,我们会继续通过系统通知告知您。',createdAt:'2026-08-31 09:36',read:false},
{id:'profile-message-2',category:'账单通知',title:'8 月账单已生成',summary:'本期应付账单已生成,请在账单页面核对费用明细。',content:'2026 年 8 月应付账单已生成。请前往“财务结算-应付账单”核对费用明细,如有疑问请联系您的客户经理。',createdAt:'2026-08-30 16:20',read:false},
{id:'profile-message-3',category:'异常提醒',title:'入仓资料需要补充',summary:'入仓单 RK20260828007 缺少箱唛文件,请及时补充。',content:'入仓单 RK20260828007 暂未检测到箱唛文件。为避免影响入仓进度,请尽快补充上传;资料齐全后仓库将继续处理。',createdAt:'2026-08-29 11:08',read:false},
{id:'profile-message-4',category:'充值通知',title:'充值申请审核完成',summary:'充值申请 CZ202608270006 已审核通过。',content:'充值申请 CZ202608270006 已审核通过,相关款项已计入账户余额。您可以在余额明细中查看本次记录。',createdAt:'2026-08-27 18:42',read:true}
];
let expandedProfileMessageId = '';
function cloneProfileMessageSeed() {
return JSON.parse(JSON.stringify(PROFILE_MESSAGE_SEED));
}
function escapeProfileMessageHtml(value) {
return String(value == null ? '' : value).replace(/[&<>"']/g, function(char) {
return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char];
});
}
function normalizeProfileMessage(item) {
if (!item || typeof item !== 'object' || typeof item.id !== 'string' || typeof item.title !== 'string') return null;
return {
id: item.id,
category: typeof item.category === 'string' ? item.category : '系统通知',
title: item.title,
summary: typeof item.summary === 'string' ? item.summary : '',
content: typeof item.content === 'string' ? item.content : '',
createdAt: typeof item.createdAt === 'string' ? item.createdAt : '',
read: item.read === true
};
}
function loadProfileMessages() {
try {
const raw = localStorage.getItem(PROFILE_MESSAGE_KEY);
if (!raw) return cloneProfileMessageSeed();
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return cloneProfileMessageSeed();
return parsed.map(normalizeProfileMessage).filter(Boolean);
} catch (error) {
return cloneProfileMessageSeed();
}
}
function saveProfileMessages(messages) {
try {
localStorage.setItem(PROFILE_MESSAGE_KEY, JSON.stringify(messages));
return true;
} catch (error) {
return false;
}
}
function countUnreadProfileMessages(messages) {
return (Array.isArray(messages) ? messages : []).filter(function(message) { return !message.read; }).length;
}
function profileMessageIcon(category) {
return ({'订单动态':'fa-ship','账单通知':'fa-file-invoice-dollar','异常提醒':'fa-triangle-exclamation','充值通知':'fa-wallet'})[category] || 'fa-bell';
}
function updateProfileMessageBadges(messages) {
const current = Array.isArray(messages) ? messages : loadProfileMessages();
const unread = countUnreadProfileMessages(current);
const topBadge = document.getElementById('dash-message-badge');
const topButton = document.getElementById('dash-message-button');
const tabBadge = document.getElementById('profile-message-unread-count');
const summary = document.getElementById('profile-message-summary');
const readAll = document.getElementById('profile-message-read-all');
if (topBadge) { topBadge.textContent = unread > 99 ? '99+' : String(unread); topBadge.hidden = unread === 0; }
if (topButton) topButton.setAttribute('aria-label', unread ? '我的消息,'+unread+'条未读' : '我的消息,无未读消息');
if (tabBadge) { tabBadge.textContent = unread > 99 ? '99+' : String(unread); tabBadge.hidden = unread === 0; }
if (summary) summary.textContent = unread+' 条未读';
if (readAll) readAll.disabled = unread === 0;
return unread;
}
function renderProfileMessages() {
const list = document.getElementById('profile-message-list');
const messages = loadProfileMessages();
updateProfileMessageBadges(messages);
if (!list) return;
if (!messages.length) {
list.innerHTML = '<div class="profile-message-empty"><span><i class="fas fa-inbox" aria-hidden="true"></i> 暂无系统通知</span></div>';
return;
}
list.innerHTML = messages.map(function(message) {
const id = escapeProfileMessageHtml(message.id);
const expanded = expandedProfileMessageId === message.id;
return '<article class="profile-message-item'+(message.read?'':' is-unread')+'">'+
'<button type="button" class="profile-message-toggle" data-message-id="'+id+'" onclick="openProfileMessage(this.dataset.messageId)" aria-expanded="'+expanded+'">'+
'<span class="profile-message-icon"><i class="fas '+profileMessageIcon(message.category)+'" aria-hidden="true"></i></span>'+
'<span class="profile-message-copy">'+
'<span class="profile-message-heading">'+(message.read?'':'<span class="profile-message-dot" aria-label="未读"></span>')+'<span class="profile-message-title">'+escapeProfileMessageHtml(message.title)+'</span></span>'+
'<span class="profile-message-preview">'+escapeProfileMessageHtml(message.summary)+'</span>'+
'</span>'+
'</button>'+
'<div class="profile-message-meta"><span>'+escapeProfileMessageHtml(message.createdAt)+'</span><span class="profile-message-state">'+(message.read?'已读':'未读')+'</span></div>'+
(expanded?'<div class="profile-message-detail"><strong>'+escapeProfileMessageHtml(message.category)+':</strong>'+escapeProfileMessageHtml(message.content)+'</div>':'')+
'</article>';
}).join('');
}
function markProfileMessageRead(messageId) {
const messages = loadProfileMessages();
const index = messages.findIndex(function(message) { return message.id === messageId; });
if (index < 0 || messages[index].read) return false;
const nextMessages = messages.slice();
nextMessages[index] = Object.assign({}, messages[index], {read:true});
if (!saveProfileMessages(nextMessages)) {
if (typeof showToast === 'function') showToast('消息状态保存失败,请检查浏览器存储设置');
return false;
}
renderProfileMessages();
return true;
}
function markAllProfileMessagesRead() {
const messages = loadProfileMessages();
if (!countUnreadProfileMessages(messages)) return false;
const nextMessages = messages.map(function(message) { return Object.assign({}, message, {read:true}); });
if (!saveProfileMessages(nextMessages)) {
if (typeof showToast === 'function') showToast('消息状态保存失败,请检查浏览器存储设置');
return false;
}
renderProfileMessages();
if (typeof showToast === 'function') showToast('全部消息已标为已读');
return true;
}
function openProfileMessage(messageId) {
expandedProfileMessageId = expandedProfileMessageId === messageId ? '' : messageId;
if (!markProfileMessageRead(messageId)) renderProfileMessages();
}
function openProfileMessages() {
const profileLink = document.querySelector('.dash-nav-link[onclick*="profile"]');
dashNavigate('profile', profileLink);
const messagesTab = document.querySelector('[data-profile-tab="messages"]');
if (messagesTab) setProfileTab(messagesTab, 'messages');
renderProfileMessages();
}
document.addEventListener('DOMContentLoaded', renderProfileMessages);
// ===== 贴标申请 =====
const LABELING_ORDER_KEY = 'by_labeling_orders_0812';
const LABELING_ORDER_SEED = [
{id:'labeling-seed-1',orderNo:'TB202608120001',inboundNo:'RK20260808018',warehouse:'美西洛杉矶仓',labelRequirement:'换箱唛',skuCount:2,totalBoxes:18,status:'待处理',createdAt:'2026-08-12 09:05',updatedAt:'2026-08-12 09:05',relatedOrderNo:'RMA2026081201',items:[{sku:'SKU-A1001',name:'蓝牙耳机套件',boxes:12,requirement:'换箱唛',fileName:'换标清单_A1001.pdf'},{sku:'SKU-A1002',name:'耳机收纳盒',boxes:6,requirement:'换箱唛',fileName:'换标清单_A1002.pdf'}],fees:{opening:'$12.00',picking:'$8.50',relabel:'$36.00',carton:'-',total:'$56.50'},remark:'请按箱唛文件完成换标。'},
{id:'labeling-seed-2',orderNo:'TB202608110002',inboundNo:'RK20260806023',warehouse:'美东新泽西仓',labelRequirement:'换SKU标',skuCount:3,totalBoxes:24,status:'处理中',createdAt:'2026-08-11 14:20',updatedAt:'2026-08-12 10:10',relatedOrderNo:'RMA2026081102',items:[{sku:'SKU-B2001',name:'便携榨汁杯',boxes:8,requirement:'换SKU标',fileName:'SKU标签_B2001.pdf'},{sku:'SKU-B2002',name:'保温杯',boxes:8,requirement:'换SKU标',fileName:'SKU标签_B2002.pdf'},{sku:'SKU-B2003',name:'运动水壶',boxes:8,requirement:'换SKU标',fileName:'SKU标签_B2003.pdf'}],fees:{opening:'$24.00',picking:'$17.00',relabel:'$48.00',carton:'-',total:'$89.00'},remark:'- '},
{id:'labeling-seed-3',orderNo:'TB202608100003',inboundNo:'RK20260808018',warehouse:'美西洛杉矶仓',labelRequirement:'既换箱唛,也换SKU标',skuCount:1,totalBoxes:10,status:'已完成',createdAt:'2026-08-10 16:45',updatedAt:'2026-08-11 16:30',relatedOrderNo:'RMA2026081003',items:[{sku:'SKU-C3001',name:'无线键盘',boxes:10,requirement:'既换箱唛,也换SKU标',fileName:'换标资料_C3001.pdf'}],fees:{opening:'$10.00',picking:'$7.00',relabel:'$30.00',carton:'$12.00',total:'$59.00'},remark:'已完成。'}
];
let labelingFilteredOrders = null;
function escapeLabelingHtml(value) { return String(value == null ? '' : value).replace(/[&<>"']/g, char => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char])); }
function getLabelingInboundByNo(inboundNo) { return CARGO_DISPOSAL_INBOUND_SEED.find(item => item.inboundNo === String(inboundNo || '').trim()) || null; }
function populateLabelingInboundOptions() { const select=document.querySelector('#labeling-form [name="inboundNo"]'); if(!select)return; const current=select.value; select.innerHTML='<option value="">请选择入仓码</option>'+CARGO_DISPOSAL_INBOUND_SEED.map(item=>'<option value="'+escapeLabelingHtml(item.inboundNo)+'">'+escapeLabelingHtml(item.inboundNo)+'</option>').join(''); if(current&&getLabelingInboundByNo(current))select.value=current; }
function syncLabelingWarehouse() { const form=document.getElementById('labeling-form'); if(!form)return; const inbound=getLabelingInboundByNo(form.elements.inboundNo.value); form.elements.warehouse.value=inbound?inbound.warehouse:''; }
function loadLabelingOrders() { try { const raw=localStorage.getItem(LABELING_ORDER_KEY); if(!raw)return JSON.parse(JSON.stringify(LABELING_ORDER_SEED)); const parsed=JSON.parse(raw); if(!Array.isArray(parsed))throw new Error('贴标申请数据格式无效'); const storedById=new Map(parsed.filter(item=>item&&typeof item==='object'&&item.id).map(item=>[String(item.id),item])); const seeds=LABELING_ORDER_SEED.map(seed=>Object.assign({},JSON.parse(JSON.stringify(seed)),storedById.get(String(seed.id))||{})); const seedIds=new Set(LABELING_ORDER_SEED.map(item=>item.id)); const localOrders=parsed.filter(item=>item&&typeof item==='object'&&!seedIds.has(item.id)); return localOrders.concat(seeds); } catch(error) { return JSON.parse(JSON.stringify(LABELING_ORDER_SEED)); } }
function saveLabelingOrders(orders) { try { const validOrders=(Array.isArray(orders)?orders:[]).filter(item=>item&&typeof item==='object'&&item.id); localStorage.setItem(LABELING_ORDER_KEY,JSON.stringify(validOrders)); return true; } catch(error) { return false; } }
function labelingNav() { return document.querySelector('.dash-nav-link[onclick*="labeling-list"]'); }
function openLabelingList() { labelingFilteredOrders=null; renderLabelingList(); dashNavigate('labeling-list',labelingNav()); }
function openLabelingCreate() { resetLabelingForm(); dashNavigate('labeling',labelingNav()); }
function labelingDetailField(label, value, emphasis, file) { return '<div class="labeling-detail-field"><dt>'+escapeLabelingHtml(label)+'</dt><dd class="'+(emphasis?'is-emphasis ':'')+(file?'labeling-detail-file':'')+'">'+escapeLabelingHtml(value == null || value === '' ? '-' : value)+'</dd></div>'; }
function labelingStatusClass(status) { return status==='已完成' ? 'status-done' : status==='处理中' ? 'status-processing' : 'status-pending'; }
function labelingDetailRows(target, rows) { const element=document.getElementById(target); if(element)element.innerHTML=rows.map(field=>labelingDetailField(field[0],field[1],field[2],field[3])).join(''); }
function renderLabelingList() { const body=document.getElementById('labeling-list-body'); if(!body)return; const orders=labelingFilteredOrders===null?loadLabelingOrders():labelingFilteredOrders; body.innerHTML=orders.length?orders.map((order,index)=>'<tr><td>'+(index+1)+'</td><td>'+escapeLabelingHtml(order.orderNo)+'</td><td>'+escapeLabelingHtml(order.inboundNo)+'</td><td>'+escapeLabelingHtml(order.warehouse)+'</td><td>'+escapeLabelingHtml(order.labelRequirement)+'</td><td>'+Number(order.skuCount||0)+'</td><td>'+Number(order.totalBoxes||0)+'</td><td><span class="status-badge '+labelingStatusClass(order.status)+'">'+escapeLabelingHtml(order.status)+'</span></td><td>'+escapeLabelingHtml(order.createdAt)+'</td><td><span class="storage-transfer-actions"><button type="button" class="btn-text labeling-detail-list-action" data-labeling-id="'+escapeLabelingHtml(order.id)+'" data-labeling-action="detail" onclick="openLabelingDetail(this.dataset.labelingId)">查看</button><button type="button" class="btn-text labeling-detail-list-action" data-labeling-id="'+escapeLabelingHtml(order.id)+'" data-labeling-action="edit" onclick="openLabelingEdit(this.dataset.labelingId)">编辑</button></span></td></tr>').join(''):'<tr class="empty-state"><td colspan="10"><div class="empty-text">暂无贴标申请</div></td></tr>'; const total=document.getElementById('labeling-total'); if(total)total.textContent=orders.length; }
function openLabelingDetail(orderId) { const order=loadLabelingOrders().find(item=>String(item.id)===String(orderId)); if(!order){showToast('未找到贴标申请');return;} const firstItem=(order.items||[])[0]||{}; const totalBoxes=Number(order.totalBoxes||0); const fees=order.fees||{}; document.getElementById('labeling-detail-order-no').textContent=order.orderNo||'-'; const status=document.getElementById('labeling-detail-status'); if(status){status.textContent=order.status||'待处理';status.classList.remove('is-processing','is-done');if(order.status==='处理中')status.classList.add('is-processing');if(order.status==='已完成')status.classList.add('is-done');} labelingDetailRows('labeling-detail-document',[['换标单号',order.orderNo,true],['关联订单',order.relatedOrderNo||order.inboundNo],['状态',order.status,true],['创建时间',order.createdAt],['更新时间',order.updatedAt||order.createdAt]]); labelingDetailRows('labeling-detail-product',[['商品SKU',firstItem.sku||order.sku||'-',true],['中文品名',firstItem.name||order.productName||'-',true],['箱数',firstItem.boxes||totalBoxes],['预算总价',order.budgetTotal||fees.total||'-',true]]); labelingDetailRows('labeling-detail-work',[['贴标要求',order.labelRequirement,true],['上传文件',firstItem.fileName||order.fileName||'-',true,true],['备注',order.remark||'-']]); labelingDetailRows('labeling-detail-fees',[['开箱费',fees.opening||'-'],['挑货费',fees.picking||'-'],['换标费',fees.relabel||'-'],['换箱唛标费',fees.carton||'-'],['换标费总计',fees.total||'-',true]]); dashNavigate('labeling-detail',labelingNav()); window.scrollTo(0,0); }
function filterLabelingOrders() { const orderNo=(document.getElementById('labeling-filter-order-no')?.value||'').trim().toLowerCase(),inboundNo=(document.getElementById('labeling-filter-inbound-no')?.value||'').trim().toLowerCase(),status=document.getElementById('labeling-filter-status')?.value||''; labelingFilteredOrders=loadLabelingOrders().filter(order=>(!orderNo||String(order.orderNo).toLowerCase().includes(orderNo))&&(!inboundNo||String(order.inboundNo).toLowerCase().includes(inboundNo))&&(!status||order.status===status)); renderLabelingList(); }
function resetLabelingFilters() { ['labeling-filter-order-no','labeling-filter-inbound-no','labeling-filter-status'].forEach(id=>{const el=document.getElementById(id);if(el)el.value='';}); labelingFilteredOrders=null; renderLabelingList(); }
function resetLabelingForm() { const form=document.getElementById('labeling-form'); if(form){form.reset();populateLabelingInboundOptions();syncLabelingWarehouse();} const tbody=document.getElementById('labeling-tbody'); if(tbody)tbody.innerHTML='<tr class="empty-state"><td colspan="14"><div class="empty-text">暂无货物明细,请点击「添加行」</div></td></tr>'; }
function getLabelingRelationTemplateRows() {
return [['箱唛', '需贴箱唛', '需贴SKU标', '备注']];
}
function downloadLabelingRelationTemplate() {
if (typeof XLSX === 'undefined') {
showToast('模板组件加载失败,请刷新页面后重试');
return;
}
try {
var worksheet = XLSX.utils.aoa_to_sheet(getLabelingRelationTemplateRows());
worksheet['!cols'] = [{ wch: 24 }, { wch: 14 }, { wch: 14 }, { wch: 32 }];
var workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '关联明细');
XLSX.writeFile(workbook, '贴标关联模板.xlsx');
showToast('关联模板已下载');
} catch (error) {
showToast('模板下载失败,请稍后重试');
}
}
function createLabelingRow(item, options) {
var row = item || {}, editMode = options && options.editMode;
var tr = document.createElement('tr');
var fileName = String(row.fileName || '');
var relationFileName = String(row.relationFileName || '');
tr.innerHTML = (editMode ? '' : '<td><input type="checkbox"></td>') +
'<td><input type="text" class="form-input labeling-sku" style="width:140px;" value="'+escapeLabelingHtml(row.sku || '')+'" required></td>' +
'<td><label class="row-img-cell" onclick="this.querySelector(\'input\').click()">+<input type="file" accept="image/*" onchange="previewRowImage(this)"></label></td>' +
'<td><input type="text" class="form-input labeling-name" style="width:140px;" value="'+escapeLabelingHtml(row.name || '')+'" required></td>' +
'<td><input type="number" class="form-input labeling-boxes" min="1" style="width:80px;" value="'+(Number(row.boxes || 0) || '')+'" required></td>' +
'<td><input type="number" class="form-input labeling-transfer-boxes" min="1" style="width:90px;" value="'+(Number(row.transferBoxes || row.boxes || 0) || '')+'" required></td>' +
'<td><input type="text" class="form-input labeling-shipment-no" style="width:130px;" value="'+escapeLabelingHtml(row.shipmentNo || '')+'"></td>' +
'<td><input type="text" class="form-input labeling-tracking-no" style="width:140px;" value="'+escapeLabelingHtml(row.trackingNo || '')+'"></td>' +
'<td><input type="text" class="form-input labeling-new-sku" style="width:130px;" value="'+escapeLabelingHtml(row.newSku || '')+'"></td>' +
'<td><select class="form-input labeling-requirement" name="labelRequirement" style="width:180px;"><option>换箱唛</option><option>换SKU标</option><option>既换箱唛,也换SKU标</option></select></td>' +
'<td><label class="row-upload-cell labeling-file-upload labeling-label-file" title="上传贴标文件"><span class="row-upload-text">'+(fileName?'✓ '+escapeLabelingHtml(fileName):'📎 上传')+'</span><input type="file" onchange="labelingFileName(this)" data-existing-file="'+escapeLabelingHtml(fileName)+'"></label></td>' +
'<td><label class="row-upload-cell labeling-file-upload labeling-relation-file" title="上传关联文件"><span class="row-upload-text">'+(relationFileName?'✓ '+escapeLabelingHtml(relationFileName):'📎 上传')+'</span><input type="file" accept=".xlsx,.xls" onchange="labelingFileName(this)" data-existing-file="'+escapeLabelingHtml(relationFileName)+'"></label></td>' +
'<td><input type="text" class="form-input labeling-item-remark" maxlength="200" placeholder="请输入备注" style="width:160px;" value="'+escapeLabelingHtml(row.itemRemark || '')+'"></td>' +
'<td><button type="button" class="btn btn-default" style="font-size:12px;padding:4px 10px;color:#ff4d4f;" onclick="this.closest(\'tr\').remove()">删除</button></td>';
var requirement = tr.querySelector('.labeling-requirement');
if (requirement) requirement.value = row.requirement || '换箱唛';
return tr;
}
function addLabelingRow() {
var tbody = document.getElementById('labeling-tbody');
if (!tbody) return;
var empty = tbody.querySelector('.empty-state');
if (empty) tbody.innerHTML = '';
tbody.appendChild(createLabelingRow());
}
function addLabelingEditRow(item) {
var tbody = document.getElementById('labeling-edit-tbody');
if (!tbody) return;
tbody.appendChild(createLabelingRow(item || {}, { editMode: true }));
}
function collectLabelingRows(tbody) {
return Array.from((tbody || {}).querySelectorAll ? tbody.querySelectorAll('tr') : []).map(function(row) {
var file = row.querySelector('.labeling-label-file input[type="file"]');
var relationFile = row.querySelector('.labeling-relation-file input[type="file"]');
return {
sku: row.querySelector('.labeling-sku')?.value.trim() || '',
name: row.querySelector('.labeling-name')?.value.trim() || '',
boxes: Number(row.querySelector('.labeling-boxes')?.value || 0),
transferBoxes: Number(row.querySelector('.labeling-transfer-boxes')?.value || 0),
shipmentNo: row.querySelector('.labeling-shipment-no')?.value.trim() || '',
trackingNo: row.querySelector('.labeling-tracking-no')?.value.trim() || '',
newSku: row.querySelector('.labeling-new-sku')?.value.trim() || '',
requirement: row.querySelector('.labeling-requirement')?.value || '换箱唛',
fileName: file?.files?.[0]?.name || file?.dataset?.existingFile || '',
relationFileName: relationFile?.files?.[0]?.name || relationFile?.dataset?.existingFile || '',
itemRemark: row.querySelector('.labeling-item-remark')?.value.trim() || ''
};
});
}
function mergeLabelingOrder(original, changes) {
var now = new Date();
var updatedAt = now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-'+String(now.getDate()).padStart(2,'0')+' '+String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
var items = (changes.items || []).map(function(item, index) {
var previous = (original.items || [])[index] || {};
return Object.assign({}, previous, item, {
fileName: item.fileName || previous.fileName || '',
relationFileName: item.relationFileName || previous.relationFileName || ''
});
});
var requirements = [...new Set(items.map(function(item) { return item.requirement; }).filter(Boolean))];
return Object.assign({}, original, changes, { updatedAt: updatedAt, items: items, labelRequirement: requirements.join('、'), skuCount: items.length, totalBoxes: items.reduce(function(sum, item) { return sum + Number(item.boxes || 0); }, 0) });
}
function syncLabelingEditWarehouse() {
var form = document.getElementById('labeling-edit-form');
if (!form) return;
var inbound = getLabelingInboundByNo(form.elements.inboundNo.value);
form.elements.warehouse.value = inbound ? inbound.warehouse : '';
}
function openLabelingEdit(orderId) {
var order = loadLabelingOrders().find(function(item) { return String(item.id) === String(orderId); });
var modal = document.getElementById('labeling-edit-modal');
var form = document.getElementById('labeling-edit-form');
var select = form?.elements?.inboundNo;
if (!order || !modal || !form || !select) { showToast('未找到贴标申请'); return; }
select.innerHTML = '<option value="">请选择入仓码</option>' + CARGO_DISPOSAL_INBOUND_SEED.map(function(item) { return '<option value="'+escapeLabelingHtml(item.inboundNo)+'">'+escapeLabelingHtml(item.inboundNo)+'</option>'; }).join('');
select.value = order.inboundNo || '';
form.elements.warehouse.value = order.warehouse || '';
form.elements.deadline.value = order.deadline || '';
form.elements.contact.value = order.contact || '';
form.elements.phone.value = order.phone || '';
form.elements.remark.value = order.remark || '';
var tbody = document.getElementById('labeling-edit-tbody');
tbody.innerHTML = '';
(order.items || []).forEach(function(item) { addLabelingEditRow(item); });
if (!(order.items || []).length) addLabelingEditRow();
modal.dataset.orderId = order.id;
modal.classList.add('show');
document.body.style.overflow = 'hidden';
}
function closeLabelingEdit() {
var modal = document.getElementById('labeling-edit-modal');
if (modal) { modal.classList.remove('show'); delete modal.dataset.orderId; }
document.body.style.overflow = '';
}
function submitLabelingEdit(event) {
if (event) event.preventDefault();
var form = document.getElementById('labeling-edit-form');
var modal = document.getElementById('labeling-edit-modal');
var tbody = document.getElementById('labeling-edit-tbody');
if (!form || !modal || !form.checkValidity()) { if (form) form.reportValidity(); return false; }
var rows = collectLabelingRows(tbody);
if (!rows.length) { showToast('请至少添加一行货物明细'); return false; }
if (rows.some(function(row) { return !row.sku || !row.name || row.boxes < 1 || row.transferBoxes < 1; })) { showToast('请完整填写货物明细'); return false; }
var orders = loadLabelingOrders();
var index = orders.findIndex(function(item) { return String(item.id) === String(modal.dataset.orderId); });
if (index < 0) { showToast('未找到贴标申请'); return false; }
var inbound = getLabelingInboundByNo(form.elements.inboundNo.value);
if (!inbound) { showToast('请选择有效的入仓码'); return false; }
var original = orders[index];
var updated = mergeLabelingOrder(original, { inboundNo: form.elements.inboundNo.value.trim(), warehouse: inbound.warehouse, deadline: form.elements.deadline.value, contact: form.elements.contact.value.trim(), phone: form.elements.phone.value.trim(), remark: form.elements.remark.value.trim(), items: rows });
orders[index] = updated;
if (!saveLabelingOrders(orders)) { showToast('保存失败,请检查浏览器存储设置'); return false; }
closeLabelingEdit();
labelingFilteredOrders = null;
renderLabelingList();
showToast('贴标申请修改已保存');
return false;
}
function clearLabelingRows() {
if (!confirm('确认清空所有货物明细?')) return;
var tbody = document.getElementById('labeling-tbody');
tbody.innerHTML = '<tr class="empty-state"><td colspan="14"><div class="empty-icon">📦</div><div class="empty-text">暂无货物明细,请点击「添加行」</div></td></tr>';
}
function previewRowImage(input) {
if (!input.files || !input.files[0]) return;
var reader = new FileReader();
reader.onload = function(e) {
var label = input.parentElement;
label.innerHTML = '<img src="'+e.target.result+'"><input type="file" accept="image/*" onchange="previewRowImage(this)">';
};
reader.readAsDataURL(input.files[0]);
}
function labelingFileName(input) {
if (!input.files || !input.files[0]) return;
var label = input.parentElement;
var text = label.querySelector('.row-upload-text');
if (text) text.textContent = '✓ '+input.files[0].name;
label.title = input.files[0].name;
}
function submitLabeling(event) {
if(event)event.preventDefault();
var form = document.getElementById('labeling-form');
var tbody = document.getElementById('labeling-tbody');
if(!form||!form.checkValidity()){if(form)form.reportValidity();return false;}
if (tbody.querySelector('.empty-state')) { showToast('请至少添加一行货物明细'); return false; }
var rows=Array.from(tbody.querySelectorAll('tr'));
if(!rows.length){showToast('请至少添加一行货物明细');return false;}
for(var row of rows){if(!row.querySelector('.labeling-sku').value.trim()||!row.querySelector('.labeling-name').value.trim()||Number(row.querySelector('.labeling-boxes').value)<1||Number(row.querySelector('.labeling-transfer-boxes').value)<1){showToast('请完整填写货物明细');return false;}}
var inbound=getLabelingInboundByNo(form.elements.inboundNo.value);
if(!inbound){showToast('请选择有效的入仓码');return false;}
var orders=loadLabelingOrders(),now=new Date(),date=now.getFullYear()+String(now.getMonth()+1).padStart(2,'0')+String(now.getDate()).padStart(2,'0'),sequence=String(orders.length+1).padStart(4,'0'),requirements=[...new Set(rows.map(row=>row.querySelector('.labeling-requirement').value))];
var createdAt=now.getFullYear()+'-'+String(now.getMonth()+1).padStart(2,'0')+'-'+String(now.getDate()).padStart(2,'0')+' '+String(now.getHours()).padStart(2,'0')+':'+String(now.getMinutes()).padStart(2,'0');
var items=collectLabelingRows(tbody);
var totalBoxes=items.reduce(function(sum,item){return sum+item.boxes;},0);
var order={id:'labeling-'+now.getTime(),orderNo:'TB'+date+sequence,inboundNo:form.elements.inboundNo.value.trim(),warehouse:inbound.warehouse,deadline:form.elements.deadline.value,contact:form.elements.contact.value.trim(),phone:form.elements.phone.value.trim(),remark:form.elements.remark.value.trim(),labelRequirement:requirements.join('、'),skuCount:rows.length,totalBoxes:totalBoxes,status:'待处理',createdAt:createdAt,updatedAt:createdAt,relatedOrderNo:form.elements.inboundNo.value.trim(),items:items,fees:{opening:'-',picking:'-',relabel:'-',carton:'-',total:'-'}};
if(!saveLabelingOrders([order].concat(orders))){showToast('保存失败,请检查浏览器存储设置');return false;}
labelingFilteredOrders=null;renderLabelingList();showToast('贴标申请提交成功');openLabelingList();return false;
}
document.addEventListener('DOMContentLoaded',function(){renderLabelingList();const body=document.getElementById('labeling-list-body');if(body&&!body.dataset.labelingBound){body.dataset.labelingBound='1';body.addEventListener('click',function(event){const button=event.target.closest('[data-labeling-id]');if(button&&button.dataset.labelingAction!=='edit')openLabelingDetail(button.getAttribute('data-labeling-id'));});}});
// ===== Dest tab toggle =====
function setDestTab(element, type) {
const module = document.getElementById('first-leg-destination');
if (!module) return;
module.querySelectorAll('#dest-tabs .dest-tab').forEach(tab => {
tab.classList.remove('active');
tab.setAttribute('aria-pressed', 'false');
});
module.querySelectorAll('.dest-content').forEach(content => content.classList.remove('active'));
element.classList.add('active');
element.setAttribute('aria-pressed', 'true');
document.getElementById(`dest-${type}`)?.classList.add('active');
}
// ===== Pickup tab toggle =====
function setPickupTab(element, type) {
const module = document.getElementById('first-leg-origin');
if (!module) return;
module.querySelectorAll('#pickup-tabs .dest-tab').forEach(tab => {
tab.classList.remove('active');
tab.setAttribute('aria-pressed', 'false');
});
module.querySelectorAll('.dest-content').forEach(content => content.classList.remove('active'));
element.classList.add('active');
element.setAttribute('aria-pressed', 'true');
document.getElementById(`pickup-${type}`)?.classList.add('active');
}
// ===== Bulk Pickup tab toggle =====
function setPickupTabBulk(el, type) {
document.querySelectorAll('#pickup-tabs-bulk .dest-tab').forEach(tab => tab.classList.remove('active'));
el.classList.add('active');
document.querySelectorAll('.dest-content-bulk').forEach(c => c.classList.remove('active'));
const target = document.getElementById('pickup-bulk-' + type);
if (target) target.classList.add('active');
}
// ===== Bulk order table =====
let bulkRowCount = 0;
function addBulkRow() {
bulkRowCount++;
const emptyRow = document.getElementById('bulk-empty-row');
if (emptyRow) emptyRow.remove();
const tbody = document.getElementById('bulk-tbody');
const cols = ['FBA仓','','','','','','','','','','','1','1','1','','','','是','否','否','',''];
const inputs = cols.map((v, i) => {
if (i === 0) return `<select style="width:80px;height:28px;border:1px solid transparent;border-radius:4px;font-size:12px;background:transparent;padding:0 4px;" onmouseover="this.style.borderColor='#d9d9d9'" onmouseout="this.style.borderColor='transparent'"><option>FBA仓</option><option>海外仓</option><option>私人仓</option></select>`;
if ([17,18,19].includes(i)) return `<select style="width:60px;height:28px;border:1px solid transparent;border-radius:4px;font-size:12px;background:transparent;padding:0 4px;" onmouseover="this.style.borderColor='#d9d9d9'" onmouseout="this.style.borderColor='transparent'"><option>否</option><option>是</option></select>`;
return `<input type="text" value="${v}" style="width:100%;min-width:80px;height:28px;padding:0 6px;border:1px solid transparent;border-radius:4px;font-size:12px;background:transparent;outline:none;" onmouseover="this.style.borderColor='#d9d9d9'" onmouseout="if(document.activeElement!==this)this.style.borderColor='transparent'" onfocus="this.style.borderColor='#1677ff'" onblur="this.style.borderColor='transparent'" placeholder="">`;
});
const tr = document.createElement('tr');
tr.style.borderBottom = '1px solid #f5f5f5';
tr.innerHTML = `<td style="padding:6px 8px;text-align:center;"><input type="checkbox" class="bulk-row-check"></td>
<td style="padding:6px 8px;text-align:center;color:#bfbfbf;font-size:12px;">${bulkRowCount}</td>
${inputs.map(inp => `<td style="padding:4px 6px;">${inp}</td>`).join('')}`;
tbody.appendChild(tr);
}
function deleteBulkRows() {
document.querySelectorAll('.bulk-row-check:checked').forEach(cb => cb.closest('tr').remove());
if (!document.getElementById('bulk-tbody').querySelector('tr:not(#bulk-empty-row)')) {
bulkRowCount = 0;
document.getElementById('bulk-tbody').innerHTML = '<tr id="bulk-empty-row"><td colspan="24" style="padding:40px 0;text-align:center;color:#bfbfbf;"><div style="font-size:28px;margin-bottom:8px;">📦</div><div style="font-size:13px;">暂无数据,请点击「添加行」或导入 Excel</div></td></tr>';
}
}
function toggleBulkAll(el) {
document.querySelectorAll('.bulk-row-check').forEach(cb => cb.checked = el.checked);
}
// ===== First-leg product and box row management =====
let productRowCount = 0;
let boxRowCount = 0;
function firstLegEmptyRow(tbodyId, emptyId, colspan, icon, text) {
document.getElementById(tbodyId).innerHTML = `<tr class="empty-row" id="${emptyId}"><td colspan="${colspan}"><div class="empty-icon"><i class="fas ${icon}"></i></div><div>${text}</div></td></tr>`;
}
function renumberFirstLegRows(tbodyId) {
document.querySelectorAll(`#${tbodyId} tr:not(.empty-row)`).forEach((row, index) => {
if (row.cells[1]) row.cells[1].textContent = index + 1;
});
}
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, character => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[character]));
}
function getFirstLegProductSkus() {
return [...document.querySelectorAll('#product-tbody .product-sku')]
.map(input => input.value.trim())
.filter(Boolean);
}
function syncBoxSkuOptions() {
const skus = getFirstLegProductSkus();
document.querySelectorAll('#box-tbody .box-sku').forEach(select => {
const current = select.value;
select.innerHTML = '<option value="">请选择 SKU</option>' + skus.map(sku => `<option value="${escapeHtml(sku)}">${escapeHtml(sku)}</option>`).join('');
select.value = skus.includes(current) ? current : '';
select.classList.toggle('is-invalid', Boolean(current) && !skus.includes(current));
});
}
function addProductRow() {
productRowCount += 1;
document.getElementById('product-empty-row')?.remove();
const productDisplayIndex = document.querySelectorAll('#product-tbody tr:not(.empty-row)').length + 1;
const row = document.createElement('tr');
row.dataset.productRowId = productRowCount;
row.innerHTML = `
<td><input type="checkbox" class="product-row-check"></td><td>${productDisplayIndex}</td>
<td><input class="td-input product-sku" placeholder="SKU编号" required aria-required="true" aria-label="SKU NO." oninput="syncBoxSkuOptions();updateFirstLegSummary()"></td>
<td><input class="td-input" placeholder="中文品名"></td>
<td><label class="row-img-cell" title="上传图片"><input type="file" accept="image/*"><i class="fas fa-plus"></i></label></td>
<td><input class="td-input" placeholder="English Name"></td><td><input class="td-input" placeholder="SHIPMENT ID"></td><td><input class="td-input" placeholder="REFERENCE ID"></td><td><input class="td-input" placeholder="UPC编号"></td><td><input class="td-input" placeholder="HS Code"></td><td><input class="td-input" placeholder="HS Code"></td>
<td><input class="td-input" type="number" min="0" step="0.01" placeholder="0.00"></td><td><input class="td-input" type="number" min="0" step="0.01" placeholder="0.00"></td>
<td><input class="td-input" placeholder="材质"></td><td><input class="td-input" placeholder="用途"></td><td><input class="td-input" placeholder="型号"></td><td><input class="td-input" placeholder="品牌"></td>
<td><input class="td-input" placeholder="L x W x H cm"></td><td><input class="td-input" placeholder="备注"></td>`;
document.getElementById('product-tbody').appendChild(row);
syncBoxSkuOptions();
updateFirstLegSummary();
}
function deleteSelectedProductRows() {
const rows = [...document.querySelectorAll('#product-tbody .product-row-check:checked')];
if (!rows.length) return alert('请先勾选要删除的商品');
rows.forEach(input => input.closest('tr').remove());
if (!document.querySelector('#product-tbody tr:not(.empty-row)')) {
productRowCount = 0;
firstLegEmptyRow('product-tbody', 'product-empty-row', 19, 'fa-box-open', '暂无商品信息,请添加商品或上传 Excel');
}
renumberFirstLegRows('product-tbody');
syncBoxSkuOptions();
updateFirstLegSummary();
}
function toggleAllProductRows(input) {
document.querySelectorAll('#product-tbody .product-row-check').forEach(item => { item.checked = input.checked; });
}
function addBoxRow() {
boxRowCount += 1;
document.getElementById('box-empty-row')?.remove();
const boxDisplayIndex = document.querySelectorAll('#box-tbody tr:not(.empty-row)').length + 1;
const row = document.createElement('tr');
row.dataset.boxRowId = boxRowCount;
row.innerHTML = `
<td><input type="checkbox" class="box-row-check"></td><td>${boxDisplayIndex}</td>
<td><select class="td-input box-sku" aria-label="关联 SKU"><option value="">请选择 SKU</option></select></td>
<td><input class="td-input" data-box-field="length" type="number" min="0" step="0.01" placeholder="0"></td>
<td><input class="td-input" data-box-field="width" type="number" min="0" step="0.01" placeholder="0"></td>
<td><input class="td-input" data-box-field="height" type="number" min="0" step="0.01" placeholder="0"></td>
<td><input class="td-input" data-box-field="quantityPerBox" type="number" min="0" step="1" placeholder="0"></td>
<td><input class="td-input" data-box-field="boxes" type="number" min="1" step="1" placeholder="0" required aria-required="true" aria-label="箱数" oninput="updateFirstLegSummary()"></td>
<td><input class="td-input" data-box-field="totalQuantity" type="number" min="0" step="1" placeholder="0"></td>
<td><input class="td-input" data-box-field="grossWeight" type="number" min="0" step="0.01" placeholder="0.00" oninput="updateFirstLegSummary()"></td>
<td><input class="td-input" data-box-field="netWeight" type="number" min="0" step="0.01" placeholder="0.00"></td>
<td><input class="td-input" data-box-field="volume" type="number" min="0" step="0.001" placeholder="0.000" oninput="updateFirstLegSummary()"></td>`;
document.getElementById('box-tbody').appendChild(row);
syncBoxSkuOptions();
updateFirstLegSummary();
}
function deleteSelectedBoxRows() {
const rows = [...document.querySelectorAll('#box-tbody .box-row-check:checked')];
if (!rows.length) return alert('请先勾选要删除的箱子');
rows.forEach(input => input.closest('tr').remove());
if (!document.querySelector('#box-tbody tr:not(.empty-row)')) {
boxRowCount = 0;
firstLegEmptyRow('box-tbody', 'box-empty-row', 12, 'fa-boxes-stacked', '暂无箱子信息,请先添加商品后再添加箱子');
}
renumberFirstLegRows('box-tbody');
updateFirstLegSummary();
}
function toggleAllBoxRows(input) {
document.querySelectorAll('#box-tbody .box-row-check').forEach(item => { item.checked = input.checked; });
}
function toggleAllTable(tableId, el) {
document.querySelectorAll('#' + tableId + ' tbody input[type=checkbox]').forEach(cb => cb.checked = el.checked);
}
function firstLegNumber(selector) {
return [...document.querySelectorAll(selector)].reduce((total, input) => total + (Number(input.value) || 0), 0);
}
function updateFirstLegSummary() {
const products = document.querySelectorAll('#product-tbody tr:not(.empty-row)').length;
const boxes = firstLegNumber('#box-tbody [data-box-field="boxes"]');
const weight = firstLegNumber('#box-tbody [data-box-field="grossWeight"]');
const volume = firstLegNumber('#box-tbody [data-box-field="volume"]');
const productCount = document.getElementById('first-leg-product-count');
const boxCount = document.getElementById('first-leg-box-count');
const totalWeight = document.getElementById('first-leg-total-weight');
const totalVolume = document.getElementById('first-leg-total-volume');
if (productCount) productCount.textContent = products;
if (boxCount) boxCount.textContent = boxes;
if (totalWeight) totalWeight.textContent = weight.toFixed(2);
if (totalVolume) totalVolume.textContent = volume.toFixed(3);
}
function focusFirstLegModule(id) {
const module = document.getElementById(id);
module?.scrollIntoView({behavior: 'smooth', block: 'start'});
module?.querySelector('input, select, textarea, button')?.focus({preventScroll: true});
}
function firstLegRequiredError(moduleId) {
const module = document.getElementById(moduleId);
if (!module) return null;
module.querySelectorAll('.is-invalid').forEach(control => control.classList.remove('is-invalid'));
const groups = [...module.querySelectorAll('.form-group')];
for (const group of groups) {
if (!group.querySelector('.form-label .required')) continue;
const conditionalPanel = group.closest('.dest-content');
if (conditionalPanel && !conditionalPanel.classList.contains('active')) continue;
if (group.querySelector('.dest-type-tabs .dest-tab.active')) continue;
const controls = [...group.querySelectorAll('input, select, textarea')].filter(control => control.type !== 'hidden');
const choices = controls.filter(control => control.type === 'radio' || control.type === 'checkbox');
const valid = choices.length
? choices.some(control => control.checked)
: controls.length > 0 && controls.every(control => String(control.value || '').trim());
if (valid) continue;
controls.forEach(control => control.classList.add('is-invalid'));
const label = group.querySelector('.form-label').textContent.replace('*', '').trim();
return {module: moduleId, message: `请填写${label}`};
}
return null;
}
function validateFirstLegOrder() {
let requiredError = firstLegRequiredError('first-leg-origin');
if (requiredError) return requiredError;
requiredError = firstLegRequiredError('first-leg-destination');
if (requiredError) return requiredError;
const productRows = [...document.querySelectorAll('#product-tbody tr:not(.empty-row)')];
if (!productRows.length) return {module: 'first-leg-products', message: '请至少添加一条商品信息'};
const skus = getFirstLegProductSkus();
if (skus.length !== productRows.length) return {module: 'first-leg-products', message: '请完整填写每条商品的 SKU'};
if (new Set(skus).size !== skus.length) return {module: 'first-leg-products', message: '商品 SKU 不能重复'};
const boxRows = [...document.querySelectorAll('#box-tbody tr:not(.empty-row)')];
if (!boxRows.length) return {module: 'first-leg-boxes', message: '请至少添加一条箱子信息'};
if (boxRows.some(row => !skus.includes(row.querySelector('.box-sku').value))) return {module: 'first-leg-boxes', message: '请为每条箱子信息关联有效 SKU'};
const invalidBoxCountRow = boxRows.find(row => {
const boxCountInput = row.querySelector('[data-box-field="boxes"]');
return !boxCountInput || !String(boxCountInput.value || '').trim() || Number(boxCountInput.value) <= 0;
});
if (invalidBoxCountRow) {
invalidBoxCountRow.querySelector('[data-box-field="boxes"]')?.classList.add('is-invalid');
return {module: 'first-leg-boxes', message: '请完整填写每条箱子信息的箱数'};
}
requiredError = firstLegRequiredError('first-leg-requirements');
if (requiredError) return requiredError;
return null;
}
function resetFirstLegOrder() {
const page = document.getElementById('page-order');
page.querySelectorAll('input').forEach(input => {
if (input.type === 'radio' || input.type === 'checkbox') input.checked = false;
else input.value = '';
});
page.querySelectorAll('select').forEach(select => { select.selectedIndex = 0; });
page.querySelectorAll('textarea').forEach(textarea => { textarea.value = ''; });
page.querySelectorAll('.is-invalid').forEach(control => control.classList.remove('is-invalid'));
pickupAutoFilledValues = null;
productRowCount = 0;
boxRowCount = 0;
firstLegEmptyRow('product-tbody', 'product-empty-row', 19, 'fa-box-open', '暂无商品信息,请添加商品或上传 Excel');
firstLegEmptyRow('box-tbody', 'box-empty-row', 12, 'fa-boxes-stacked', '暂无箱子信息,请先添加商品后再添加箱子');
const pickupDefault = document.querySelectorAll('#pickup-tabs .dest-tab')[0];
const destinationDefault = document.querySelector('#dest-tabs .dest-tab');
if (pickupDefault) setPickupTab(pickupDefault, 'pickup');
if (destinationDefault) setDestTab(destinationDefault, 'fba');
updateFirstLegSummary();
focusFirstLegModule('first-leg-origin');
alert('已重置表单');
}
function submitOrder() {
const error = validateFirstLegOrder();
if (error) {
alert(error.message);
focusFirstLegModule(error.module);
return;
}
alert('订单提交成功!系统将发送确认通知至您的联系邮箱。');
}
document.addEventListener('DOMContentLoaded', () => {
updateFirstLegSummary();
});
// ===== Quick Order functions =====
let quickRowIndex = 2;
function addQuickRow() {
const tbody = document.getElementById('quick-rows');
const tr = document.createElement('tr');
tr.className = 'quick-row';
tr.innerHTML = `
<td>
<div class="select-wrap">
<select class="form-select">
<option value="">请选择</option>
<option>美线普货专线</option>
<option>美线敏感货专线</option>
<option>美线快线</option>
<option>美线FBA直送</option>
</select>
</div>
</td>
<td>
<label class="radio-label" style="display:block;margin-bottom:4px;"><input type="radio" name="dest${quickRowIndex}"> FBA</label>
<label class="radio-label" style="display:block;margin-bottom:4px;"><input type="radio" name="dest${quickRowIndex}"> 海外仓</label>
<label class="radio-label" style="display:block;"><input type="radio" name="dest${quickRowIndex}"> 私人仓</label>
</td>
<td>
<div class="quick-row-actions">
<a href="javascript:void(0)" onclick="addProduct(this)" style="color:#1677ff;font-size:13px;">+ 添加产品</a>
<a href="javascript:void(0)" onclick="delQuickRow(this)" style="color:#ff4d4f;font-size:13px;">- 删除本行</a>
</div>
</td>
<td>
<div class="product-line">
<input type="text" class="form-input" placeholder="产品SKU" style="width:140px;">
<input type="number" class="form-input" value="0" style="width:80px;text-align:center;">
<a href="javascript:void(0)" onclick="delProduct(this)" style="color:#ff4d4f;font-size:13px;">- 删除此产品</a>
</div>
</td>
`;
tbody.appendChild(tr);
quickRowIndex++;
}
function delQuickRow(el) {
const rows = document.querySelectorAll('#quick-rows .quick-row');
if (rows.length <= 1) { alert('至少需要保留一行'); return; }
el.closest('tr').remove();
}
function addProduct(el) {
const td = el.closest('tr').querySelector('td:last-child');
const div = document.createElement('div');
div.className = 'product-line';
div.innerHTML = `
<input type="text" class="form-input" placeholder="产品SKU" style="width:140px;">
<input type="number" class="form-input" value="0" style="width:80px;text-align:center;">
<a href="javascript:void(0)" onclick="delProduct(this)" style="color:#ff4d4f;font-size:13px;">- 删除此产品</a>
`;
td.appendChild(div);
}
function delProduct(el) {
const td = el.closest('td');
const lines = td.querySelectorAll('.product-line');
if (lines.length <= 1) { alert('每行至少需要保留一个产品'); return; }
el.closest('.product-line').remove();
}
function submitQuickOrder() {
alert('快速订单提交成功!');
}
</script>
</body>
</html>