docs(prototype): add SYB inner-code workflow for #118
This commit is contained in:
@@ -0,0 +1,482 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>GoAuto #118 · 档口入库码总原型 v1</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1e40af;
|
||||
--primary-hover: #1d4ed8;
|
||||
--primary-soft: #eff6ff;
|
||||
--accent: #b45309;
|
||||
--accent-soft: #fffbeb;
|
||||
--danger: #b91c1c;
|
||||
--danger-soft: #fef2f2;
|
||||
--success: #047857;
|
||||
--success-soft: #ecfdf5;
|
||||
--info: #0369a1;
|
||||
--info-soft: #f0f9ff;
|
||||
--text: #172033;
|
||||
--text-secondary: #4b5563;
|
||||
--text-muted: #6b7280;
|
||||
--border: #dbe3ef;
|
||||
--border-strong: #c7d2e3;
|
||||
--surface: #ffffff;
|
||||
--canvas: #f5f7fb;
|
||||
--sidebar: #17213a;
|
||||
--sidebar-hover: #22304f;
|
||||
--shadow: 0 12px 36px rgba(30, 64, 175, .10);
|
||||
--radius: 8px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html { color-scheme: light; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--canvas);
|
||||
color: var(--text);
|
||||
font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
button, input, select { font: inherit; }
|
||||
button, select, input[type="checkbox"] { cursor: pointer; }
|
||||
button:focus-visible, input:focus-visible, select:focus-visible, [tabindex]:focus-visible {
|
||||
outline: 3px solid rgba(30, 64, 175, .28);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
button:disabled { cursor: not-allowed; opacity: .48; }
|
||||
.skip-link { position: fixed; left: 16px; top: -60px; z-index: 1000; padding: 10px 14px; background: #fff; color: var(--primary); border-radius: 6px; }
|
||||
.skip-link:focus { top: 12px; }
|
||||
.app { min-height: 100vh; display: grid; grid-template-columns: 216px minmax(0, 1fr); }
|
||||
.sidebar { background: var(--sidebar); color: #d7deeb; min-height: 100vh; position: fixed; inset: 0 auto 0 0; width: 216px; z-index: 20; }
|
||||
.brand { height: 58px; display: flex; align-items: center; gap: 10px; padding: 0 20px; color: #fff; font-size: 18px; font-weight: 700; border-bottom: 1px solid rgba(255,255,255,.08); }
|
||||
.brand-mark { width: 28px; height: 28px; border-radius: 7px; display: grid; place-items: center; background: #2563eb; font-size: 12px; letter-spacing: -.4px; }
|
||||
.nav { padding: 12px 10px; }
|
||||
.nav-label { padding: 8px 12px 6px; font-size: 11px; color: #8fa0bd; letter-spacing: .08em; }
|
||||
.nav-item { min-height: 44px; padding: 0 12px; border-radius: 6px; display: flex; align-items: center; gap: 10px; color: #cad4e5; text-decoration: none; margin: 2px 0; }
|
||||
.nav-item:hover { background: var(--sidebar-hover); }
|
||||
.nav-item.active { background: #2563eb; color: #fff; font-weight: 600; }
|
||||
.nav-icon { width: 18px; height: 18px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.8; }
|
||||
.shell { grid-column: 2; min-width: 0; }
|
||||
.topbar { height: 58px; background: var(--surface); border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; padding: 0 22px; position: sticky; top: 0; z-index: 15; }
|
||||
.breadcrumbs { color: var(--text-muted); }
|
||||
.breadcrumbs strong { color: var(--text); font-weight: 600; }
|
||||
.top-actions { display: flex; align-items: center; gap: 12px; }
|
||||
.prototype-badge { color: #6d28d9; background: #f5f3ff; border: 1px solid #ddd6fe; padding: 4px 9px; border-radius: 999px; font-size: 12px; }
|
||||
.user-chip { display: flex; align-items: center; gap: 8px; color: var(--text-secondary); }
|
||||
.avatar { width: 30px; height: 30px; border-radius: 50%; background: #dbeafe; color: var(--primary); display: grid; place-items: center; font-weight: 700; }
|
||||
main { padding: 20px 22px 36px; max-width: 1680px; margin: 0 auto; }
|
||||
.page-head { display: flex; gap: 16px; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; }
|
||||
h1 { font-size: 22px; line-height: 1.3; margin: 0 0 5px; }
|
||||
.page-subtitle { margin: 0; color: var(--text-secondary); }
|
||||
.demo-box { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: #eef2ff; border: 1px solid #c7d2fe; border-radius: 8px; }
|
||||
.demo-box label { font-size: 12px; color: #3730a3; font-weight: 600; white-space: nowrap; }
|
||||
select, .input { height: 36px; border: 1px solid var(--border-strong); border-radius: 6px; background: #fff; color: var(--text); padding: 0 10px; }
|
||||
.demo-box select { min-width: 180px; border-color: #a5b4fc; }
|
||||
.stats { display: grid; grid-template-columns: repeat(5, minmax(130px, 1fr)); gap: 10px; margin-bottom: 12px; }
|
||||
.stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 13px 15px; min-width: 0; }
|
||||
.stat-label { color: var(--text-muted); font-size: 12px; margin-bottom: 3px; }
|
||||
.stat-value { font-size: 22px; line-height: 1.25; font-weight: 700; font-variant-numeric: tabular-nums; }
|
||||
.stat-note { color: var(--text-muted); font-size: 12px; margin-top: 2px; }
|
||||
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 2px 8px rgba(28,39,60,.03); }
|
||||
.filters { padding: 14px; display: grid; grid-template-columns: 150px 150px minmax(230px, 1fr) auto; gap: 10px; align-items: end; margin-bottom: 12px; }
|
||||
.field label { display: block; color: var(--text-secondary); font-size: 12px; font-weight: 600; margin-bottom: 5px; }
|
||||
.field input, .field select { width: 100%; }
|
||||
.filter-actions { display: flex; gap: 8px; }
|
||||
.btn { min-height: 36px; padding: 7px 13px; border-radius: 6px; border: 1px solid var(--border-strong); background: #fff; color: var(--text); font-weight: 600; display: inline-flex; align-items: center; justify-content: center; gap: 7px; transition: background .16s, border-color .16s, color .16s; }
|
||||
.btn:hover:not(:disabled) { background: #f8fafc; border-color: #94a3b8; }
|
||||
.btn-primary { background: var(--primary); color: #fff; border-color: var(--primary); }
|
||||
.btn-primary:hover:not(:disabled) { background: var(--primary-hover); border-color: var(--primary-hover); }
|
||||
.btn-warning { background: #b45309; color: #fff; border-color: #b45309; }
|
||||
.btn-danger { color: var(--danger); border-color: #fecaca; background: #fff; }
|
||||
.btn-link { border: 0; background: transparent; color: var(--primary); padding: 3px 5px; min-height: 30px; }
|
||||
.btn-link:hover:not(:disabled) { background: var(--primary-soft); border-color: transparent; }
|
||||
.toolbar { min-height: 54px; padding: 9px 12px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--border); }
|
||||
.toolbar-left, .toolbar-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.selection-note { color: var(--text-secondary); padding-right: 4px; }
|
||||
.selection-note strong { color: var(--primary); }
|
||||
.safety-note { font-size: 12px; color: var(--accent); background: var(--accent-soft); border: 1px solid #fde68a; padding: 6px 9px; border-radius: 6px; }
|
||||
.table-wrap { overflow-x: auto; }
|
||||
table { width: 100%; min-width: 1240px; border-collapse: collapse; }
|
||||
th { height: 42px; padding: 0 10px; text-align: left; background: #f8fafc; color: #475569; border-bottom: 1px solid var(--border); font-size: 12px; font-weight: 700; white-space: nowrap; }
|
||||
td { padding: 10px; border-bottom: 1px solid #edf1f6; vertical-align: top; }
|
||||
tbody tr:hover { background: #fbfdff; }
|
||||
tbody tr.selected { background: #f5f9ff; }
|
||||
.check { width: 17px; height: 17px; accent-color: var(--primary); }
|
||||
.mono { font-family: Consolas, "SFMono-Regular", monospace; font-variant-numeric: tabular-nums; }
|
||||
.muted { color: var(--text-muted); }
|
||||
.small { font-size: 12px; }
|
||||
.cell-main { color: var(--text); font-weight: 600; }
|
||||
.cell-sub { color: var(--text-muted); font-size: 12px; margin-top: 2px; }
|
||||
.codes { display: flex; gap: 4px; flex-wrap: wrap; max-width: 200px; }
|
||||
.code-pill { font-family: Consolas, monospace; background: #f1f5f9; border: 1px solid #e2e8f0; color: #334155; border-radius: 4px; padding: 1px 5px; font-size: 12px; }
|
||||
.tag { display: inline-flex; min-height: 24px; align-items: center; border-radius: 999px; padding: 2px 8px; border: 1px solid; font-size: 12px; font-weight: 600; white-space: nowrap; }
|
||||
.tag::before { content: ""; width: 6px; height: 6px; border-radius: 50%; margin-right: 5px; background: currentColor; }
|
||||
.pending { color: #475569; background: #f8fafc; border-color: #cbd5e1; }
|
||||
.ready { color: #1d4ed8; background: #eff6ff; border-color: #bfdbfe; }
|
||||
.applying { color: #b45309; background: #fffbeb; border-color: #fde68a; }
|
||||
.success { color: var(--success); background: var(--success-soft); border-color: #a7f3d0; }
|
||||
.failed { color: var(--danger); background: var(--danger-soft); border-color: #fecaca; }
|
||||
.needs { color: #7c3aed; background: #f5f3ff; border-color: #ddd6fe; }
|
||||
.pagination { min-height: 54px; padding: 10px 14px; display: flex; align-items: center; justify-content: flex-end; gap: 8px; color: var(--text-secondary); }
|
||||
.page-btn { min-width: 34px; height: 34px; border: 1px solid var(--border); border-radius: 5px; background: #fff; }
|
||||
.page-btn.active { color: #fff; background: var(--primary); border-color: var(--primary); }
|
||||
.state-panel { display: none; min-height: 420px; padding: 74px 24px; text-align: center; }
|
||||
.state-panel.visible { display: block; }
|
||||
.state-icon { width: 56px; height: 56px; border-radius: 50%; display: grid; place-items: center; margin: 0 auto 15px; background: #eef2ff; color: var(--primary); }
|
||||
.state-icon svg { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 1.8; }
|
||||
.state-panel h2 { font-size: 18px; margin: 0 0 6px; }
|
||||
.state-panel p { max-width: 520px; color: var(--text-secondary); margin: 0 auto 18px; }
|
||||
.skeleton { max-width: 820px; margin: 20px auto; display: grid; gap: 12px; }
|
||||
.sk { height: 18px; border-radius: 4px; background: linear-gradient(90deg,#eef2f7 25%,#f8fafc 42%,#eef2f7 63%); background-size: 300% 100%; animation: shimmer 1.4s infinite; }
|
||||
@keyframes shimmer { to { background-position: -100% 0; } }
|
||||
.modal-layer { display: none; position: fixed; inset: 0; z-index: 100; background: rgba(15, 23, 42, .52); align-items: center; justify-content: center; padding: 22px; }
|
||||
.modal-layer.open { display: flex; }
|
||||
.modal { width: min(760px, 100%); max-height: calc(100vh - 44px); overflow: auto; background: #fff; border-radius: 10px; box-shadow: 0 24px 70px rgba(15,23,42,.28); }
|
||||
.modal.sm { width: min(570px, 100%); }
|
||||
.modal-head { min-height: 58px; padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
.modal-head h2 { font-size: 18px; margin: 0; }
|
||||
.icon-btn { width: 36px; height: 36px; border: 0; border-radius: 6px; background: transparent; font-size: 22px; color: var(--text-muted); }
|
||||
.icon-btn:hover { background: #f1f5f9; }
|
||||
.modal-body { padding: 18px; }
|
||||
.modal-foot { padding: 12px 18px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; }
|
||||
.dropzone { border: 1.5px dashed #93a4bd; border-radius: 8px; min-height: 150px; display: grid; place-items: center; text-align: center; padding: 24px; background: #f8fafc; }
|
||||
.dropzone strong { display: block; font-size: 15px; margin-bottom: 4px; }
|
||||
.dropzone p { color: var(--text-muted); margin: 0 0 12px; }
|
||||
.file-line { display: none; margin-top: 12px; border: 1px solid var(--border); background: #fff; border-radius: 7px; padding: 10px 12px; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
.file-line.visible { display: flex; }
|
||||
.alert { border: 1px solid; border-radius: 7px; padding: 10px 12px; display: flex; gap: 9px; align-items: flex-start; margin-top: 12px; }
|
||||
.alert-info { background: var(--info-soft); border-color: #bae6fd; color: #075985; }
|
||||
.alert-warning { background: var(--accent-soft); border-color: #fde68a; color: #92400e; }
|
||||
.alert-danger { background: var(--danger-soft); border-color: #fecaca; color: #991b1b; }
|
||||
.alert-success { background: var(--success-soft); border-color: #a7f3d0; color: #065f46; }
|
||||
.summary-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin: 13px 0; }
|
||||
.summary-box { border: 1px solid var(--border); border-radius: 7px; padding: 11px; background: #fafcff; }
|
||||
.summary-box .num { font-size: 21px; font-weight: 750; font-variant-numeric: tabular-nums; }
|
||||
.summary-box .label { color: var(--text-muted); font-size: 12px; }
|
||||
.error-list { margin: 12px 0 0; padding: 0; list-style: none; border: 1px solid #fecaca; border-radius: 7px; overflow: hidden; }
|
||||
.error-list li { padding: 8px 10px; border-bottom: 1px solid #fee2e2; color: #991b1b; }
|
||||
.error-list li:last-child { border-bottom: 0; }
|
||||
.metric-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin: 14px 0; }
|
||||
.metric { border: 1px solid var(--border); border-radius: 7px; padding: 12px; }
|
||||
.metric strong { display: block; font-size: 24px; line-height: 1.2; font-variant-numeric: tabular-nums; }
|
||||
.metric span { color: var(--text-muted); font-size: 12px; }
|
||||
.confirm-check { display: flex; align-items: flex-start; gap: 9px; padding: 11px; border: 1px solid #fed7aa; background: #fff7ed; border-radius: 7px; color: #7c2d12; }
|
||||
.confirm-check input { width: 18px; height: 18px; accent-color: #b45309; flex: 0 0 auto; margin-top: 1px; }
|
||||
.progress-wrap { margin: 14px 0; }
|
||||
.progress-head { display: flex; justify-content: space-between; margin-bottom: 6px; }
|
||||
.progress-track { height: 10px; border-radius: 999px; overflow: hidden; background: #e8edf5; }
|
||||
.progress-fill { height: 100%; width: 64%; background: var(--primary); border-radius: inherit; transition: width .25s ease; }
|
||||
.timeline { list-style: none; padding: 0; margin: 14px 0 0; }
|
||||
.timeline li { position: relative; padding: 0 0 15px 25px; color: var(--text-secondary); }
|
||||
.timeline li::before { content: ""; position: absolute; left: 4px; top: 5px; width: 9px; height: 9px; border-radius: 50%; background: #94a3b8; }
|
||||
.timeline li::after { content: ""; position: absolute; left: 8px; top: 16px; bottom: 2px; width: 1px; background: #cbd5e1; }
|
||||
.timeline li:last-child::after { display: none; }
|
||||
.timeline li.done::before { background: var(--success); }
|
||||
.timeline li.active::before { background: var(--primary); box-shadow: 0 0 0 4px #dbeafe; }
|
||||
.timeline strong { color: var(--text); }
|
||||
.evidence { border: 1px solid var(--border); border-radius: 7px; overflow: hidden; margin-top: 12px; }
|
||||
.evidence-row { display: grid; grid-template-columns: 145px 1fr; gap: 10px; padding: 9px 11px; border-bottom: 1px solid #edf1f6; }
|
||||
.evidence-row:last-child { border-bottom: 0; }
|
||||
.evidence-row dt { color: var(--text-muted); }
|
||||
.evidence-row dd { margin: 0; color: var(--text); }
|
||||
.toast { display: none; position: fixed; z-index: 180; right: 22px; top: 76px; max-width: 390px; background: #fff; color: var(--text); border: 1px solid var(--border); border-left: 4px solid var(--success); border-radius: 7px; box-shadow: var(--shadow); padding: 11px 14px; }
|
||||
.toast.show { display: block; }
|
||||
.toast.error { border-left-color: var(--danger); }
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
.app { grid-template-columns: 72px minmax(0, 1fr); }
|
||||
.sidebar { width: 72px; }
|
||||
.brand { padding: 0; justify-content: center; }
|
||||
.brand span:last-child, .nav-label, .nav-item span { display: none; }
|
||||
.nav-item { justify-content: center; padding: 0; }
|
||||
.shell { grid-column: 2; }
|
||||
.stats { grid-template-columns: repeat(3, 1fr); }
|
||||
.filters { grid-template-columns: 1fr 1fr; }
|
||||
.filter-actions { align-self: end; }
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.app { display: block; }
|
||||
.sidebar { display: none; }
|
||||
.shell { min-width: 0; }
|
||||
.topbar { padding: 0 14px; }
|
||||
.prototype-badge, .user-chip span { display: none; }
|
||||
main { padding: 14px; }
|
||||
.page-head { display: block; }
|
||||
.demo-box { margin-top: 12px; }
|
||||
.demo-box select { width: 100%; min-width: 0; }
|
||||
.stats { grid-template-columns: 1fr 1fr; }
|
||||
.filters { grid-template-columns: 1fr; }
|
||||
.toolbar { align-items: flex-start; }
|
||||
.toolbar-right { display: none; }
|
||||
.summary-grid, .metric-grid { grid-template-columns: 1fr 1fr; }
|
||||
.modal-layer { padding: 8px; }
|
||||
.evidence-row { grid-template-columns: 1fr; gap: 2px; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">跳到主要内容</a>
|
||||
<div class="app">
|
||||
<aside class="sidebar" aria-label="主导航">
|
||||
<div class="brand"><span class="brand-mark">GA</span><span>GoAuto</span></div>
|
||||
<nav class="nav">
|
||||
<div class="nav-label">业务管理</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h10"/></svg><span>采购任务</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" viewBox="0 0 24 24"><path d="M4 5h16v14H4zM8 9h8M8 13h5"/></svg><span>SYB 商品</span></a>
|
||||
<a class="nav-item active" href="#main" aria-current="page"><svg class="nav-icon" viewBox="0 0 24 24"><path d="M7 3v18M17 3v18M3 7h18M3 17h18M10 10l4 4M14 10l-4 4"/></svg><span>档口入库码</span></a>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" viewBox="0 0 24 24"><path d="M4 4h16v16H4zM8 8h8v8H8z"/></svg><span>SYB 同步记录</span></a>
|
||||
<div class="nav-label">系统管理</div>
|
||||
<a class="nav-item" href="#"><svg class="nav-icon" viewBox="0 0 24 24"><circle cx="12" cy="8" r="3"/><path d="M5 21c0-4 3-7 7-7s7 3 7 7"/></svg><span>用户与权限</span></a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div class="shell">
|
||||
<header class="topbar">
|
||||
<div class="breadcrumbs">首页 / 顺运宝 / <strong>档口入库码</strong></div>
|
||||
<div class="top-actions">
|
||||
<span class="prototype-badge">#118 · v1 审核原型</span>
|
||||
<div class="user-chip"><span class="avatar">采</span><span>采购员</span></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="main" tabindex="-1">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>档口入库码</h1>
|
||||
<p class="page-subtitle">导入标签码,精确匹配顺运宝明细,确认后逐件写入;结果不明确时只读复核。</p>
|
||||
</div>
|
||||
<div class="demo-box">
|
||||
<label for="demoState">演示状态</label>
|
||||
<select id="demoState" aria-label="切换原型演示状态">
|
||||
<option value="normal">正常列表</option>
|
||||
<option value="import">Excel 导入</option>
|
||||
<option value="import-error">导入部分失败</option>
|
||||
<option value="matching">匹配结果</option>
|
||||
<option value="confirm">写入确认</option>
|
||||
<option value="progress">执行进度</option>
|
||||
<option value="needs-check">结果不明确</option>
|
||||
<option value="empty">空状态</option>
|
||||
<option value="loading">加载中</option>
|
||||
<option value="failed">加载失败</option>
|
||||
<option value="readonly">只读权限</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="stats" aria-label="状态汇总">
|
||||
<article class="stat"><div class="stat-label">今日导入记录</div><div class="stat-value">126</div><div class="stat-note">单件码 138 个</div></article>
|
||||
<article class="stat"><div class="stat-label">待匹配</div><div class="stat-value">18</div><div class="stat-note">需读取 SYB</div></article>
|
||||
<article class="stat"><div class="stat-label">可写入</div><div class="stat-value">31</div><div class="stat-note">计划仍在有效期</div></article>
|
||||
<article class="stat"><div class="stat-label">执行中</div><div class="stat-value">1</div><div class="stat-note">批次 IC-0827-04</div></article>
|
||||
<article class="stat"><div class="stat-label">需人工复核</div><div class="stat-value" style="color:#7c3aed">2</div><div class="stat-note">禁止直接重试</div></article>
|
||||
</section>
|
||||
|
||||
<section id="workArea">
|
||||
<form class="card filters" onsubmit="return false" aria-label="筛选条件">
|
||||
<div class="field"><label for="date">营业日期</label><input class="input" id="date" type="date" value="2026-08-27"></div>
|
||||
<div class="field"><label for="status">状态</label><select id="status"><option>全部状态</option><option>待匹配</option><option>可写入</option><option>写入中</option><option>需复核</option></select></div>
|
||||
<div class="field"><label for="keyword">关键词</label><input class="input" id="keyword" value="" placeholder="订单号 / 店铺 / 档口 / SKU / 规格 / 入库码"></div>
|
||||
<div class="filter-actions"><button class="btn btn-primary" type="submit">查询</button><button class="btn" type="button">重置</button></div>
|
||||
</form>
|
||||
|
||||
<section class="card" aria-label="档口入库码列表">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<button class="btn btn-primary write-action" id="openImport" type="button">导入 Excel</button>
|
||||
<span class="selection-note">已选择 <strong id="selectedCount">2</strong> 条</span>
|
||||
<button class="btn write-action" id="openMatch" type="button">批量匹配</button>
|
||||
<button class="btn btn-warning write-action" id="openConfirm" type="button">开始写入</button>
|
||||
<button class="btn btn-danger write-action" id="softDelete" type="button">软删除</button>
|
||||
</div>
|
||||
<div class="toolbar-right"><span class="safety-note">远端写入串行执行 · 不确定结果不会自动重试</span></div>
|
||||
</div>
|
||||
<div id="tableRegion" class="table-wrap">
|
||||
<table>
|
||||
<thead><tr><th><input class="check" type="checkbox" checked aria-label="选择本页全部记录"></th><th>来源 / 日期</th><th>订单 / 店铺</th><th>档口 / 原始 SKU</th><th>规格</th><th>单件入库码</th><th>SYB 匹配</th><th>状态</th><th>结果</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="selected"><td><input class="check row-check" type="checkbox" checked aria-label="选择记录 1821"></td><td><div class="cell-main">第 18 行</div><div class="cell-sub">2026-08-27 · 序号 017</div></td><td><div class="cell-main mono">260827TB95MJTQ</div><div class="cell-sub">Shopee 泰国女装店</div></td><td><div class="cell-main">A23-07</div><div class="cell-sub mono">SKU-RED-XL</div></td><td><div class="cell-main">红色 / XL</div><div class="cell-sub">规范键:红色|XL</div></td><td><div class="codes"><span class="code-pill">IC82700171</span><span class="code-pill">IC82700172</span></div><div class="cell-sub">2 件</div></td><td><div class="cell-main">明细 #145306175</div><div class="cell-sub">需新建 1 个占位明细</div></td><td><span class="tag ready">可写入</span></td><td><div class="cell-sub">计划于 23:18 生成</div></td><td><button class="btn btn-link" type="button" data-action="evidence">查看证据</button></td></tr>
|
||||
<tr class="selected"><td><input class="check row-check" type="checkbox" checked aria-label="选择记录 1822"></td><td><div class="cell-main">第 19 行</div><div class="cell-sub">2026-08-27 · 序号 018</div></td><td><div class="cell-main mono">260827TB95MK2</div><div class="cell-sub">Shopee 泰国女装店</div></td><td><div class="cell-main">B10</div><div class="cell-sub mono">SKU-BLK-M</div></td><td><div class="cell-main">黑色 / M</div><div class="cell-sub">规范键:黑色|M</div></td><td><div class="codes"><span class="code-pill">IC82700180</span></div><div class="cell-sub">1 件</div></td><td><div class="cell-main">明细 #145306244</div><div class="cell-sub">将替换旧码 OLD-180</div></td><td><span class="tag ready">可写入</span></td><td><div class="cell-sub">计划于 23:19 生成</div></td><td><button class="btn btn-link" type="button" data-action="evidence">查看证据</button></td></tr>
|
||||
<tr><td><input class="check row-check" type="checkbox" aria-label="选择记录 1823"></td><td><div class="cell-main">第 20 行</div><div class="cell-sub">2026-08-27 · 序号 019</div></td><td><div class="cell-main mono">260827TB95MKG</div><div class="cell-sub">Shopee 泰国女装店</div></td><td><div class="cell-main">0082</div><div class="cell-sub mono">SKU-WHT-L</div></td><td><div class="cell-main">白色 / L</div><div class="cell-sub">规范键:白色|L</div></td><td><div class="codes"><span class="code-pill">IC82700190</span></div></td><td><div class="cell-main">—</div><div class="cell-sub">尚未读取 SYB</div></td><td><span class="tag pending">待匹配</span></td><td><div class="cell-sub">等待批量匹配</div></td><td><button class="btn btn-link" type="button">详情</button></td></tr>
|
||||
<tr><td><input class="check row-check" type="checkbox" aria-label="选择记录 1819"></td><td><div class="cell-main">第 16 行</div><div class="cell-sub">2026-08-27 · 序号 015</div></td><td><div class="cell-main mono">260827TB95MJ81</div><div class="cell-sub">Shopee 泰国鞋包店</div></td><td><div class="cell-main">C-12</div><div class="cell-sub mono">SKU-39-BE</div></td><td><div class="cell-main">米白 / 39</div><div class="cell-sub">规范键:米白|39</div></td><td><div class="codes"><span class="code-pill">IC82700150</span></div></td><td><div class="cell-main">明细 #145305980</div><div class="cell-sub">网络中断后结果未知</div></td><td><span class="tag needs">需复核</span></td><td><div class="cell-sub">禁止重新写入</div></td><td><button class="btn btn-link" type="button" data-action="recheck">只读复核</button></td></tr>
|
||||
<tr><td><input class="check row-check" type="checkbox" aria-label="选择记录 1818"></td><td><div class="cell-main">第 15 行</div><div class="cell-sub">2026-08-27 · 序号 014</div></td><td><div class="cell-main mono">260827TB95MJ70</div><div class="cell-sub">Shopee 泰国鞋包店</div></td><td><div class="cell-main">D5</div><div class="cell-sub mono">SKU-40-BK</div></td><td><div class="cell-main">黑色 / 40</div><div class="cell-sub">规范键:黑色|40</div></td><td><div class="codes"><span class="code-pill">IC82700140</span></div></td><td><div class="cell-main">明细 #145305911</div><div class="cell-sub">唯一目标码已核验</div></td><td><span class="tag success">已写入</span></td><td><div class="cell-sub">23:06 完成</div></td><td><button class="btn btn-link" type="button" data-action="evidence">详情</button></td></tr>
|
||||
<tr><td><input class="check row-check" type="checkbox" aria-label="选择记录 1817"></td><td><div class="cell-main">第 14 行</div><div class="cell-sub">2026-08-27 · 序号 013</div></td><td><div class="cell-main mono">260827TB95MJ55</div><div class="cell-sub">Shopee 泰国女装店</div></td><td><div class="cell-main">A23-08</div><div class="cell-sub mono">SKU-PNK-S</div></td><td><div class="cell-main">粉色 / S</div><div class="cell-sub">规范键:粉色|S</div></td><td><div class="codes"><span class="code-pill">IC82700130</span></div></td><td><div class="cell-main">候选 2 条</div><div class="cell-sub">原始 SKU 与档口证据冲突</div></td><td><span class="tag failed">匹配失败</span></td><td><div class="cell-sub">请检查 Excel 源数据</div></td><td><button class="btn btn-link" type="button" data-action="evidence">查看原因</button></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="specialState"></div>
|
||||
<div class="pagination" id="pagination"><span>共 126 条</span><select aria-label="每页条数"><option>20 条/页</option><option>50 条/页</option><option selected>100 条/页</option><option>200 条/页</option></select><button class="page-btn" disabled aria-label="上一页">‹</button><button class="page-btn active">1</button><button class="page-btn">2</button><button class="page-btn" aria-label="下一页">›</button></div>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-layer" id="importModal" role="dialog" aria-modal="true" aria-labelledby="importTitle">
|
||||
<section class="modal">
|
||||
<header class="modal-head"><h2 id="importTitle">导入档口入库码 Excel</h2><button class="icon-btn close-modal" aria-label="关闭导入对话框">×</button></header>
|
||||
<div class="modal-body">
|
||||
<div id="importPick">
|
||||
<div class="dropzone"><div><strong>选择或拖入 Excel 文件</strong><p>仅 .xlsx;最大 10 MB、5000 行;工作表“标签入库码映射”</p><button class="btn btn-primary" id="pickFile" type="button">选择演示文件</button></div></div>
|
||||
<div class="file-line" id="fileLine"><div><strong>标签入库码映射_20260827.xlsx</strong><div class="small muted">286 KB · 本原型不会读取或上传文件</div></div><span class="tag ready">待校验</span></div>
|
||||
<div class="alert alert-info"><strong>说明</strong><span>Excel 原文件不会保存。系统只保存通过校验的结构化记录和操作者审计。</span></div>
|
||||
</div>
|
||||
<div id="importResult" hidden>
|
||||
<div class="alert alert-success"><strong>校验完成</strong><span>共读取 132 行,126 条业务记录可导入。</span></div>
|
||||
<div class="summary-grid"><div class="summary-box"><div class="num">126</div><div class="label">可导入记录</div></div><div class="summary-box"><div class="num">138</div><div class="label">单件入库码</div></div><div class="summary-box"><div class="num">4</div><div class="label">合并多件记录</div></div><div class="summary-box"><div class="num" style="color:#b91c1c">6</div><div class="label">未通过行</div></div></div>
|
||||
<div class="alert alert-warning"><strong>同日快照</strong><span>2026-08-27 已有 12 条全部处于“待匹配”,可以在事务中整体替换;如存在匹配或写入证据,系统会拒绝替换。</span></div>
|
||||
<ul class="error-list" aria-label="导入错误"><li><strong>第 37 行:</strong>入库码包含英文逗号,请拆成独立行</li><li><strong>第 58 行:</strong>订单号为空</li><li><strong>第 91、92 行:</strong>日期内入库码 IC82700901 重复</li></ul>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="modal-foot"><button class="btn close-modal" type="button">取消</button><button class="btn btn-primary" id="validateImport" type="button" disabled>校验文件</button><button class="btn btn-primary" id="commitImport" type="button" hidden>导入 126 条有效记录</button></footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="modal-layer" id="matchModal" role="dialog" aria-modal="true" aria-labelledby="matchTitle">
|
||||
<section class="modal">
|
||||
<header class="modal-head"><h2 id="matchTitle">只读匹配结果</h2><button class="icon-btn close-modal" aria-label="关闭匹配结果">×</button></header>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info"><strong>只读操作</strong><span>已实时读取 SYB 订单明细,没有调用删除、创建或写码接口。</span></div>
|
||||
<div class="summary-grid"><div class="summary-box"><div class="num">18</div><div class="label">选择记录</div></div><div class="summary-box"><div class="num" style="color:#047857">14</div><div class="label">可写入</div></div><div class="summary-box"><div class="num" style="color:#b91c1c">3</div><div class="label">匹配失败</div></div><div class="summary-box"><div class="num">1</div><div class="label">已存在目标码</div></div></div>
|
||||
<dl class="evidence"><div class="evidence-row"><dt>订单与明细</dt><dd><span class="mono">260827TB95MJTQ</span> → stock #75104587 / detail #145306175</dd></div><div class="evidence-row"><dt>规格证据</dt><dd>Excel“红色 / XL”与 SYB 规范化规格完全一致</dd></div><div class="evidence-row"><dt>SKU 证据</dt><dd><span class="mono">SKU-RED-XL</span> 与 variationSku 完全一致</dd></div><div class="evidence-row"><dt>多件规划</dt><dd>数量 2;复用原明细 1 条,预计新建占位明细 1 条</dd></div><div class="evidence-row"><dt>执行前提</dt><dd>写入前必须重新读取整个订单;远端漂移时计划自动失效</dd></div></dl>
|
||||
<div class="alert alert-danger"><strong>3 条未匹配</strong><span>1 条原始 SKU 与档口证据冲突,1 条候选重复,1 条订单不存在。系统不会选择相近候选。</span></div>
|
||||
</div>
|
||||
<footer class="modal-foot"><button class="btn close-modal">关闭</button><button class="btn btn-primary" id="selectReady">仅选择 14 条可写入记录</button></footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="modal-layer" id="confirmModal" role="dialog" aria-modal="true" aria-labelledby="confirmTitle">
|
||||
<section class="modal">
|
||||
<header class="modal-head"><h2 id="confirmTitle">确认逐件写入顺运宝</h2><button class="icon-btn close-modal" aria-label="关闭写入确认">×</button></header>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning"><strong>这是外部写入操作</strong><span>系统会修改 SYB 入库码,并可能创建数量 1、价格 0 的占位明细。执行后不能依靠重复提交恢复。</span></div>
|
||||
<div class="metric-grid"><div class="metric"><strong>14</strong><span>业务记录</span></div><div class="metric"><strong>19</strong><span>单件入库码</span></div><div class="metric"><strong>5</strong><span>预计占位明细</span></div><div class="metric"><strong>3</strong><span>将替换旧码</span></div></div>
|
||||
<dl class="evidence"><div class="evidence-row"><dt>执行方式</dt><dd>后台串行执行;同一时刻只有一个活动写入批次</dd></div><div class="evidence-row"><dt>写入前验证</dt><dd>重新读取整个订单并验证 stock/detail、规格、SKU 和旧码</dd></div><div class="evidence-row"><dt>异常处理</dt><dd>超时、断线或响应无法确认时转为“需复核”,禁止自动重试</dd></div><div class="evidence-row"><dt>复核方式</dt><dd>仅重新读取 SYB 当前状态,不调用任何写入接口</dd></div></dl>
|
||||
<label class="confirm-check"><input id="ackRisk" type="checkbox"><span>我已核对以上数量,并了解结果不明确时只能先进行只读复核,不能直接重新写入。</span></label>
|
||||
</div>
|
||||
<footer class="modal-foot"><button class="btn close-modal">返回检查</button><button class="btn btn-warning" id="confirmApply" disabled>确认并开始写入</button></footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="modal-layer" id="progressModal" role="dialog" aria-modal="true" aria-labelledby="progressTitle">
|
||||
<section class="modal">
|
||||
<header class="modal-head"><h2 id="progressTitle">写入批次 IC-0827-04</h2><button class="icon-btn close-modal" aria-label="关闭执行进度">×</button></header>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info"><strong>后台执行中</strong><span>关闭窗口不会中止任务。请勿重复提交;系统会持续保存逐件检查点。</span></div>
|
||||
<div class="progress-wrap"><div class="progress-head"><strong>9 / 14 条已处理</strong><span class="mono">64%</span></div><div class="progress-track" role="progressbar" aria-valuemin="0" aria-valuemax="14" aria-valuenow="9"><div class="progress-fill"></div></div></div>
|
||||
<div class="summary-grid"><div class="summary-box"><div class="num" style="color:#047857">7</div><div class="label">已写入</div></div><div class="summary-box"><div class="num">1</div><div class="label">原码已存在</div></div><div class="summary-box"><div class="num" style="color:#b91c1c">1</div><div class="label">失败</div></div><div class="summary-box"><div class="num">5</div><div class="label">等待处理</div></div></div>
|
||||
<ol class="timeline"><li class="done"><strong>23:31:02 · 批次已排队</strong><br><span>14 条记录通过计划有效性检查</span></li><li class="done"><strong>23:31:08 · 前 8 条已完成</strong><br><span>写后读取确认目标码只出现在预期明细</span></li><li class="active"><strong>23:31:11 · 正在处理 260827TB95MJTQ</strong><br><span>第 2 件占位明细已创建,正在写入 IC82700172</span></li><li><strong>等待处理剩余 5 条</strong></li></ol>
|
||||
</div>
|
||||
<footer class="modal-foot"><button class="btn close-modal">关闭并在后台执行</button><button class="btn btn-primary" id="showNeeds">演示结果不明确</button></footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="modal-layer" id="recheckModal" role="dialog" aria-modal="true" aria-labelledby="recheckTitle">
|
||||
<section class="modal">
|
||||
<header class="modal-head"><h2 id="recheckTitle">结果不明确 · 只读复核</h2><button class="icon-btn close-modal" aria-label="关闭只读复核">×</button></header>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-danger"><strong>不能直接重试</strong><span>写入请求发出后连接中断,无法判断 SYB 是否已处理。重复写入可能覆盖正确数据或创建重复明细。</span></div>
|
||||
<dl class="evidence"><div class="evidence-row"><dt>记录</dt><dd><span class="mono">260827TB95MJ81</span> · 米白 / 39 · IC82700150</dd></div><div class="evidence-row"><dt>最后检查点</dt><dd>写入 detail #145305980 请求已发出;尚未完成写后整单核验</dd></div><div class="evidence-row"><dt>允许的操作</dt><dd>重新读取 SYB 整个订单,比较目标码出现位置和数量</dd></div><div class="evidence-row"><dt>禁止的操作</dt><dd>删除旧码、创建明细、再次写码</dd></div></dl>
|
||||
<div id="recheckResult" hidden class="alert alert-success"><strong>只读复核完成</strong><span>目标码 IC82700150 仅出现在预期 detail #145305980,可确认本条已经写入成功。</span></div>
|
||||
</div>
|
||||
<footer class="modal-foot"><button class="btn close-modal">暂不处理</button><button class="btn btn-primary" id="runRecheck">重新读取 SYB(只读)</button></footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="modal-layer" id="evidenceModal" role="dialog" aria-modal="true" aria-labelledby="evidenceTitle">
|
||||
<section class="modal sm">
|
||||
<header class="modal-head"><h2 id="evidenceTitle">匹配与写入证据</h2><button class="icon-btn close-modal" aria-label="关闭证据详情">×</button></header>
|
||||
<div class="modal-body"><dl class="evidence"><div class="evidence-row"><dt>业务身份</dt><dd>2026-08-27 / 260827TB95MJTQ / A23-07 / 红色|XL</dd></div><div class="evidence-row"><dt>远端身份</dt><dd>stock #75104587 / detail #145306175</dd></div><div class="evidence-row"><dt>匹配理由</dt><dd>规格规范键与原始 SKU 均完全一致,无冲突证据</dd></div><div class="evidence-row"><dt>远端快照</dt><dd>计划时旧码为空;执行前仍会重新读取并比较</dd></div></dl></div>
|
||||
<footer class="modal-foot"><button class="btn btn-primary close-modal">知道了</button></footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||||
|
||||
<script>
|
||||
const qs = (selector, root = document) => root.querySelector(selector);
|
||||
const qsa = (selector, root = document) => [...root.querySelectorAll(selector)];
|
||||
const modals = qsa('.modal-layer');
|
||||
let lastFocus = null;
|
||||
|
||||
function openModal(id) {
|
||||
lastFocus = document.activeElement;
|
||||
const layer = qs('#' + id);
|
||||
layer.classList.add('open');
|
||||
document.body.style.overflow = 'hidden';
|
||||
setTimeout(() => qs('button, input, select', layer)?.focus(), 0);
|
||||
}
|
||||
function closeModal(layer) {
|
||||
layer.classList.remove('open');
|
||||
if (!qsa('.modal-layer.open').length) document.body.style.overflow = '';
|
||||
if (layer.id === 'importModal') resetImport();
|
||||
lastFocus?.focus?.();
|
||||
}
|
||||
function closeAll() { modals.forEach(m => m.classList.remove('open')); document.body.style.overflow = ''; }
|
||||
qsa('.close-modal').forEach(button => button.addEventListener('click', () => closeModal(button.closest('.modal-layer'))));
|
||||
modals.forEach(layer => layer.addEventListener('mousedown', e => { if (e.target === layer) closeModal(layer); }));
|
||||
document.addEventListener('keydown', e => { if (e.key === 'Escape') { const top = qsa('.modal-layer.open').at(-1); if (top) closeModal(top); } });
|
||||
|
||||
const toast = qs('#toast');
|
||||
function notify(message, error = false) {
|
||||
toast.textContent = message;
|
||||
toast.className = 'toast show' + (error ? ' error' : '');
|
||||
clearTimeout(notify.timer);
|
||||
notify.timer = setTimeout(() => toast.className = 'toast', 3600);
|
||||
}
|
||||
|
||||
qs('#openImport').addEventListener('click', () => openModal('importModal'));
|
||||
qs('#openMatch').addEventListener('click', () => openModal('matchModal'));
|
||||
qs('#openConfirm').addEventListener('click', () => openModal('confirmModal'));
|
||||
qs('#pickFile').addEventListener('click', () => { qs('#fileLine').classList.add('visible'); qs('#validateImport').disabled = false; qs('#validateImport').focus(); });
|
||||
qs('#validateImport').addEventListener('click', () => { qs('#importPick').hidden = true; qs('#importResult').hidden = false; qs('#validateImport').hidden = true; qs('#commitImport').hidden = false; });
|
||||
qs('#commitImport').addEventListener('click', () => { closeModal(qs('#importModal')); notify('演示:126 条有效记录已导入,6 条错误未写入'); resetImport(); });
|
||||
function resetImport() { setTimeout(() => { qs('#importPick').hidden = false; qs('#importResult').hidden = true; qs('#validateImport').hidden = false; qs('#validateImport').disabled = true; qs('#commitImport').hidden = true; qs('#fileLine').classList.remove('visible'); }, 200); }
|
||||
qs('#selectReady').addEventListener('click', () => { closeModal(qs('#matchModal')); const count = qs('#selectedCount'); if (count) count.textContent = '14'; notify('已选择 14 条可写入记录,失败记录未被选择'); });
|
||||
qs('#ackRisk').addEventListener('change', e => qs('#confirmApply').disabled = !e.target.checked);
|
||||
qs('#confirmApply').addEventListener('click', () => { closeModal(qs('#confirmModal')); setTimeout(() => openModal('progressModal'), 120); });
|
||||
qs('#showNeeds').addEventListener('click', () => { closeModal(qs('#progressModal')); setTimeout(() => openModal('recheckModal'), 120); });
|
||||
qs('#runRecheck').addEventListener('click', e => { e.currentTarget.disabled = true; e.currentTarget.textContent = '正在读取…'; setTimeout(() => { qs('#recheckResult').hidden = false; e.currentTarget.textContent = '复核完成'; notify('只读复核完成:该记录已确认写入'); }, 700); });
|
||||
qs('#softDelete').addEventListener('click', () => notify('演示:已写入和需复核记录不可删除;只会软删除安全状态记录', true));
|
||||
qsa('[data-action="evidence"]').forEach(b => b.addEventListener('click', () => openModal('evidenceModal')));
|
||||
qsa('[data-action="recheck"]').forEach(b => b.addEventListener('click', () => openModal('recheckModal')));
|
||||
qsa('.row-check').forEach(c => c.addEventListener('change', () => {
|
||||
c.closest('tr').classList.toggle('selected', c.checked);
|
||||
const count = qs('#selectedCount');
|
||||
if (count) count.textContent = qsa('.row-check:checked').length;
|
||||
}));
|
||||
|
||||
const tableRegion = qs('#tableRegion');
|
||||
const special = qs('#specialState');
|
||||
const pagination = qs('#pagination');
|
||||
const stateTemplates = {
|
||||
empty: `<section class="state-panel visible"><div class="state-icon"><svg viewBox="0 0 24 24"><path d="M4 6h16v14H4zM8 10h8M8 14h5"/></svg></div><h2>还没有档口入库码</h2><p>先导入“标签入库码映射”Excel。系统会校验订单、档口、规格和单件码,但不会保存原文件。</p><button class="btn btn-primary" onclick="openModal('importModal')">导入第一个 Excel</button></section>`,
|
||||
loading: `<section class="state-panel visible" aria-busy="true"><div class="state-icon"><svg viewBox="0 0 24 24"><path d="M12 3a9 9 0 1 0 9 9"/></svg></div><h2>正在加载档口入库码…</h2><p>正在读取列表和批次进度,请稍候。</p><div class="skeleton"><div class="sk"></div><div class="sk" style="width:88%"></div><div class="sk" style="width:94%"></div><div class="sk" style="width:72%"></div></div></section>`,
|
||||
failed: `<section class="state-panel visible" role="alert"><div class="state-icon" style="background:#fef2f2;color:#b91c1c"><svg viewBox="0 0 24 24"><path d="M12 9v4M12 17h.01M10 3h4l8 17H2z"/></svg></div><h2>列表加载失败</h2><p>服务端暂时无法响应。筛选条件已保留,可以重试;重试只读取列表,不会重新匹配或写入 SYB。</p><button class="btn btn-primary" onclick="document.querySelector('#demoState').value='normal'; renderState('normal'); notify('演示:列表已重新加载')">重新加载</button></section>`,
|
||||
readonly: `<section class="state-panel visible"><div class="state-icon" style="background:#f8fafc;color:#475569"><svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="10" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg></div><h2>当前账号只有查看权限</h2><p>你可以查看记录、匹配证据和批次结果,但不能导入、匹配、写入、复核或删除。需要操作时请联系管理员调整角色。</p><button class="btn" onclick="document.querySelector('#demoState').value='normal'; renderState('normal')">返回列表演示</button></section>`
|
||||
};
|
||||
function renderState(state) {
|
||||
closeAll();
|
||||
tableRegion.style.display = '';
|
||||
pagination.style.display = '';
|
||||
special.innerHTML = '';
|
||||
qsa('.write-action').forEach(b => b.disabled = false);
|
||||
if (stateTemplates[state]) {
|
||||
tableRegion.style.display = 'none'; pagination.style.display = 'none'; special.innerHTML = stateTemplates[state];
|
||||
if (state === 'readonly') qsa('.write-action').forEach(b => b.disabled = true);
|
||||
return;
|
||||
}
|
||||
if (state === 'import') openModal('importModal');
|
||||
if (state === 'import-error') { openModal('importModal'); qs('#importPick').hidden = true; qs('#importResult').hidden = false; qs('#validateImport').hidden = true; qs('#commitImport').hidden = false; }
|
||||
if (state === 'matching') openModal('matchModal');
|
||||
if (state === 'confirm') openModal('confirmModal');
|
||||
if (state === 'progress') openModal('progressModal');
|
||||
if (state === 'needs-check') openModal('recheckModal');
|
||||
}
|
||||
qs('#demoState').addEventListener('change', e => renderState(e.target.value));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user