9857 lines
701 KiB
HTML
9857 lines
701 KiB
HTML
<!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">></span><button type="button" onclick="showSeaBusinessPage('Pallet')">海外业务</button><span aria-hidden="true">></span><span>美本土散板</span><span aria-hidden="true">></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="关闭">×</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="关闭">×</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="关闭">×</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="关闭">×</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="关闭">×</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="关闭">×</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="关闭">×</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()">×</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()">×</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="关闭">×</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="关闭">×</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="关闭">×</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="关闭">×</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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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, '&').replace(/</g, '<').replace(/>/g, '>')
|
||
.replace(/"/g, '"').replace(/'/g, ''');
|
||
}
|
||
|
||
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');}
|
||
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'''); }
|
||
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 {'&':'&','<':'<','>':'>','"':'"',"'":'''}[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 {'&':'&','<':'<','>':'>','"':'"',"'":'''}[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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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>
|
||
|
||
|
||
|
||
|