Files
shanxia2.0/docs/fresh-mint-green.html
T
2026-10-15 08:50:25 +08:00

3630 lines
133 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>山峡化工 · 配送调度</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--text: #1e293b;
--text-muted: #64748b;
--border: #d9e8e2;
/* 工作区:极淡绿底;卡片/面板仍为白。强调色仍只用于导航选中、主按钮、主状态标签 */
--bg-page: #fefefe;
--surface: #ffffff;
--table-head: #f7faf8;
--row-alt: #f1f5f9;
--row-hover: #fee2e2;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
--radius: 10px;
/* 主内容区:全宽,仅四边留白 */
--content-padding: 24px;
}
* { box-sizing: border-box; }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
body {
margin: 0;
min-height: 100vh;
font-family: Inter, -apple-system, sans-serif;
font-size: 14px;
line-height: 1.5;
color: var(--text);
background: var(--bg-page);
}
.layout { display: flex; min-height: 100vh; }
.sidebar {
width: 220px;
flex-shrink: 0;
background: var(--surface);
border-right: 1px solid var(--border);
padding: 24px 16px;
}
.logo {
font-size: 16px;
font-weight: 700;
color: var(--text);
padding: 0 12px 20px;
margin-bottom: 8px;
border-bottom: 1px solid var(--border);
}
.logo span { color: var(--primary); }
.nav-details {
border: none;
margin: 0 0 4px;
padding: 0;
}
.nav-details > summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
margin: 0;
border-radius: var(--radius);
font-size: 14px;
font-weight: 700;
color: var(--text-muted);
letter-spacing: 0.02em;
user-select: none;
}
.nav-details > summary::-webkit-details-marker { display: none; }
.nav-details > summary::after {
content: "";
width: 6px;
height: 6px;
border-right: 2px solid var(--text-muted);
border-bottom: 2px solid var(--text-muted);
transform: rotate(-45deg);
flex-shrink: 0;
margin-left: 8px;
opacity: 0.7;
transition: transform 0.2s ease;
}
.nav-details[open] > summary::after {
transform: rotate(45deg);
margin-top: -2px;
}
.nav-details > summary:hover {
background: var(--row-alt);
color: var(--text);
}
.nav-sub {
padding: 2px 0 6px 4px;
margin-left: 4px;
border-left: 2px solid var(--border);
}
.nav a {
display: block;
padding: 8px 12px 8px 14px;
margin: 2px 0;
border-radius: var(--radius);
color: var(--text-muted);
text-decoration: none;
font-weight: 500;
font-size: 15px;
}
.nav a:hover { background: var(--row-alt); color: var(--text); }
.nav a.active { background: var(--primary-bg); color: var(--primary-dark); font-weight: 600; }
.nav-foot { margin-top: 24px; padding: 16px 12px 0; font-size: 15px; }
.nav-foot a { color: var(--text-muted); text-decoration: none; }
.nav-foot a:hover { color: var(--primary-dark); }
.main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
width: 100%;
}
.header {
height: 56px;
padding: 0 var(--content-padding);
display: flex;
align-items: center;
justify-content: space-between;
background: var(--surface);
border-bottom: 1px solid var(--border);
}
.crumb { font-size: 13px; color: var(--text-muted); }
.crumb b { color: var(--text); font-weight: 600; }
.content {
width: 100%;
max-width: none;
flex: 1;
padding: var(--content-padding);
box-sizing: border-box;
display: flex;
flex-direction: column;
min-height: 0;
}
.top-progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 3px;
z-index: 9999;
pointer-events: none;
overflow: hidden;
opacity: 0;
transition: opacity 0.15s ease;
}
.top-progress-bar.is-active {
opacity: 1;
}
.top-progress-bar::after {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 40%;
background: linear-gradient(90deg, var(--primary), #34d399);
animation: top-progress-slide 0.85s ease-in-out infinite;
}
@keyframes top-progress-slide {
0% { transform: translateX(-100%); }
100% { transform: translateX(350%); }
}
.tabs-strip {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 4px;
margin: 0 0 16px;
padding: 0 2px 10px;
border-bottom: 1px solid var(--border);
}
.tab-chip {
appearance: none;
-webkit-appearance: none;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text-muted);
font-size: 13px;
font-weight: 600;
font-family: inherit;
padding: 8px 14px;
border-radius: 8px 8px 0 0;
cursor: pointer;
margin-bottom: -11px;
position: relative;
z-index: 1;
max-width: 220px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tab-chip:hover {
color: var(--text);
background: var(--row-alt);
}
.tab-chip.is-active {
color: var(--primary-dark);
background: var(--surface);
border-bottom-color: var(--surface);
box-shadow: 0 -2px 0 var(--primary) inset;
z-index: 2;
}
.tab-panel-mount {
flex: 1;
min-height: 0;
position: relative;
}
.tab-panel {
display: none;
animation: tab-fade-in 0.2s ease;
}
.tab-panel.is-active {
display: block;
}
@keyframes tab-fade-in {
from { opacity: 0.85; }
to { opacity: 1; }
}
.tab-panel.is-refreshing {
position: relative;
pointer-events: none;
min-height: 160px;
}
.tab-panel.is-refreshing::before {
content: "";
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.55);
z-index: 20;
border-radius: var(--radius);
}
.tab-panel.is-refreshing::after {
content: "";
position: absolute;
left: 50%;
top: 48px;
width: 28px;
height: 28px;
margin-left: -14px;
border: 3px solid var(--border);
border-top-color: var(--primary);
border-radius: 50%;
animation: tab-spin 0.7s linear infinite;
z-index: 21;
}
@keyframes tab-spin {
to { transform: rotate(360deg); }
}
.btn.is-loading,
.btn-primary.is-loading,
.btn-op.is-loading,
.pager-btn.is-loading,
.btn-icon.is-loading,
.upload-zone-btn.is-loading,
.btn-block.is-loading {
position: relative;
pointer-events: none;
cursor: not-allowed;
color: #94a3b8 !important;
background: #f1f5f9 !important;
border-color: #cbd5e1 !important;
box-shadow: none !important;
opacity: 1;
padding-left: 30px;
}
.btn-op.is-loading {
padding-left: 24px;
}
.btn-icon.is-loading {
padding-left: 22px;
}
/* 加载时把原有左侧图标隐藏,视觉上替换为 loading 图标 */
.is-loading .ic,
.is-loading .ic-sm {
opacity: 0;
}
.btn.is-loading::before,
.btn-primary.is-loading::before,
.btn-op.is-loading::before,
.pager-btn.is-loading::before,
.btn-icon.is-loading::before,
.upload-zone-btn.is-loading::before,
.btn-block.is-loading::before {
content: "";
position: absolute;
left: 10px;
top: 50%;
width: 14px;
height: 14px;
margin-top: -7px;
border: 2px solid #cbd5e1;
border-top-color: #64748b;
border-radius: 50%;
animation: tab-spin 0.65s linear infinite;
}
.btn-op.is-loading::before,
.btn-icon.is-loading::before {
left: 8px;
width: 12px;
height: 12px;
margin-top: -6px;
}
.page-head {
margin-bottom: 16px;
}
.page-head h1 {
margin: 0 0 4px;
font-size: 20px;
font-weight: 700;
letter-spacing: -0.02em;
}
.desc {
margin: 0;
color: var(--text-muted);
font-size: 13px;
line-height: 1.45;
max-width: 560px;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;
margin-bottom: 16px;
}
.stat {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 12px 14px;
box-shadow: var(--shadow);
display: flex;
flex-direction: column;
gap: 8px;
min-height: 112px;
}
.stat-hd {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 0;
}
.stat-ic {
width: 44px;
height: 44px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: var(--primary-bg);
color: var(--primary-dark);
border: 1px solid rgba(16, 185, 129, 0.12);
}
.stat-ic svg {
width: 24px;
height: 24px;
display: block;
}
.stat-main {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 10px;
min-width: 0;
flex: 1;
}
.stat .k {
font-size: 14px;
color: var(--text);
margin: 0;
line-height: 1.35;
font-weight: 600;
max-width: 52%;
word-break: break-word;
}
.stat-side {
min-width: 0;
text-align: right;
margin-left: auto;
}
.stat .v {
font-size: 22px;
font-weight: 700;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
line-height: 1.1;
}
.stat .hint {
font-size: 11px;
color: var(--text-muted);
margin-top: 4px;
line-height: 1.3;
white-space: nowrap;
}
.stat.warn .stat-ic {
background: #fffbeb;
border-color: rgba(217, 119, 6, 0.2);
color: #d97706;
}
.stat.warn .v,
.stat.warn .hint {
color: #d97706;
}
/* 通用图标尺寸(按钮内、行内操作等) */
.ic {
width: 16px;
height: 16px;
flex-shrink: 0;
display: block;
}
.ic-sm { width: 14px; height: 14px; }
/* 带清除的输入框 */
.input-clear-wrap {
position: relative;
width: 100%;
display: block;
}
.input-clear-wrap .input {
width: 100%;
min-width: 0;
padding-right: 34px;
}
.input-clear {
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
width: 28px;
height: 28px;
padding: 0;
margin: 0;
border: none;
border-radius: 6px;
background: transparent;
color: var(--text-muted);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.input-clear:hover {
background: var(--row-alt);
color: var(--text);
}
.input-clear:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 1px;
}
.input-clear[hidden] {
display: none !important;
}
.panel {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
}
.panel-top {
padding: 10px 16px;
border-bottom: 1px solid var(--border);
background: var(--surface);
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.panel-top h2 {
margin: 0;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.panel-top h2 .panel-ic {
width: 18px;
height: 18px;
color: var(--primary-dark);
flex-shrink: 0;
}
.tools { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
/* 列表上方:多条件检索 */
.filter-block {
padding: 12px 16px 10px;
border-bottom: 1px solid var(--border);
background: var(--surface);
}
.filter-form { margin: 0; }
.filter-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px 14px;
margin-bottom: 10px;
}
@media (max-width: 1100px) {
.filter-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
.filter-grid { grid-template-columns: 1fr; }
}
.filter-field label {
display: block;
font-size: 11px;
font-weight: 500;
color: var(--text-muted);
margin-bottom: 4px;
}
.filter-field .input,
.filter-field .select {
width: 100%;
min-width: 0;
}
.select {
height: 36px;
padding: 0 12px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 14px;
font-family: inherit;
color: var(--text);
background: var(--surface);
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 32px;
}
.select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
}
.filter-extra {
padding-top: 10px;
margin-top: 2px;
border-top: 1px dashed var(--border);
}
.filter-extra[hidden] {
display: none !important;
}
.filter-hint {
margin: 0 0 8px;
font-size: 11px;
color: var(--text-muted);
}
.filter-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px;
border-top: 1px dashed var(--border);
padding-top: 10px;
}
.filter-actions-end {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.btn-link {
border: none;
background: transparent;
color: var(--primary-dark);
font-weight: 600;
padding: 0 8px;
height: 36px;
}
.btn-link:hover {
background: var(--primary-bg);
border-radius: 8px;
}
.input {
height: 36px;
padding: 0 12px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 14px;
font-family: inherit;
color: var(--text);
min-width: 200px;
background: var(--surface);
}
.input:focus {
outline: none;
border-color: var(--primary);
background: var(--surface);
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
}
.btn {
height: 36px;
padding: 0 14px;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
font-family: inherit;
cursor: pointer;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.btn:hover { background: var(--row-alt); }
.btn-primary {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
.btn-primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); }
table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
th, td { padding: 12px 20px; text-align: left; border-bottom: 1px solid var(--border); }
th {
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
background: var(--table-head);
}
tbody tr:nth-child(even) td { background: var(--row-alt); }
tbody tr:hover td { background: var(--row-hover); }
tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:last-child td { border-bottom: 1px solid var(--border); }
/* 列表区:上方工具栏(单行)+ 表格(末列操作 sticky固定在右侧) */
.list-body {
border-top: 1px solid var(--border);
}
.list-toolbar {
padding: 0;
background: var(--table-head);
border-bottom: 1px solid var(--border);
}
.list-toolbar-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
gap: 8px;
padding: 8px 16px;
}
.list-toolbar-selected {
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
white-space: nowrap;
padding: 0 10px 0 0;
margin-right: 4px;
border-right: 1px solid var(--border);
line-height: 36px;
min-height: 36px;
display: inline-flex;
align-items: center;
}
.list-toolbar-selected .sel-num {
color: var(--primary-dark);
font-weight: 700;
margin: 0 2px;
}
.list-pager {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px 12px;
padding: 8px 16px;
background: var(--surface);
border-top: 1px solid var(--border);
}
.pager-meta {
font-size: 13px;
color: var(--text-muted);
}
.pager-controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.pager-size-label {
font-size: 13px;
color: var(--text-muted);
margin: 0;
}
.pager-select {
width: auto;
min-width: 72px;
height: 36px;
}
.pager-size-suffix {
font-size: 13px;
color: var(--text-muted);
margin-right: 4px;
}
.pager-btn:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.pager-nums {
display: inline-flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
}
.pager-num {
min-width: 36px;
padding: 0 10px;
height: 32px;
font-size: 13px;
}
.pager-num.is-current {
background: var(--primary);
border-color: var(--primary);
color: #fff;
cursor: default;
}
.pager-num.is-current:hover {
background: var(--primary-dark);
border-color: var(--primary-dark);
color: #fff;
}
.pager-gap {
padding: 0 4px;
color: var(--text-muted);
font-size: 13px;
user-select: none;
}
.data-table tbody tr.is-hidden {
display: none !important;
}
.btn-block {
width: 100%;
height: auto;
min-height: 34px;
padding: 8px 8px;
font-size: 12px;
line-height: 1.3;
justify-content: center;
}
.table-scroll {
overflow-x: auto;
overflow-y: auto;
max-height: min(520px, calc(100vh - 300px));
-webkit-overflow-scrolling: touch;
}
.data-table { min-width: 1320px; }
/* 图文列:左对齐,文案单行省略;可叠副标题 */
.data-table .td-cell-media {
text-align: left;
max-width: 240px;
vertical-align: middle;
}
.cell-media {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}
.cell-media-img {
width: 40px;
height: 40px;
border-radius: 8px;
object-fit: cover;
flex-shrink: 0;
border: 1px solid var(--border);
background: var(--row-alt);
}
.cell-media-stack {
min-width: 0;
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.cell-media-text {
font-size: 13px;
font-weight: 500;
color: var(--text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cell-media-sub {
font-size: 12px;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 表头居中;表体默认居中,长文本列用 .td-left */
.data-table tbody td {
text-align: center;
vertical-align: middle;
}
.data-table .td-left {
text-align: left;
}
.data-table .col-qty,
.data-table .col-amt {
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* 列表金额列:统一颜色,便于阅读 */
.data-table tbody td.col-amt,
.data-table tfoot .total-row td.col-amt {
color: var(--primary-dark);
font-weight: 600;
}
.view-subtable td.money-val {
font-variant-numeric: tabular-nums;
}
.data-table tfoot .total-row td {
text-align: center;
font-weight: 600;
color: var(--text);
background: var(--primary-bg);
border-top: 2px solid var(--primary);
border-bottom: none;
padding-top: 14px;
padding-bottom: 14px;
}
.data-table tfoot .total-row .td-total-label {
text-align: center;
letter-spacing: 0.12em;
}
.data-table tfoot .total-row .td-check,
.data-table tfoot .total-row .td-op {
background: var(--primary-bg);
}
/* 表头:居中 + 纵向滚动时吸顶 */
.data-table thead th {
text-align: center;
position: sticky;
top: 0;
z-index: 3;
background: var(--table-head);
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
}
.data-table thead th.th-check {
z-index: 6;
box-shadow: 6px 0 10px -6px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(0, 0, 0, 0.06);
}
.data-table thead th.th-op {
z-index: 5;
box-shadow: -6px 0 10px -6px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(0, 0, 0, 0.06);
}
/* 首列多选:固定在左侧,横向滚动时仍可选 */
th.th-check,
td.td-check {
position: sticky;
left: 0;
z-index: 2;
width: 52px;
min-width: 52px;
max-width: 52px;
padding: 12px 10px;
text-align: center;
vertical-align: middle;
box-shadow: 6px 0 10px -6px rgba(0, 0, 0, 0.1);
background: var(--surface);
}
tbody tr:nth-child(even) td.td-check { background: var(--row-alt); }
tbody tr:hover td.td-check { background: var(--row-hover); }
.th-check-inner {
display: flex;
align-items: center;
justify-content: center;
}
.row-check {
appearance: none;
-webkit-appearance: none;
width: 16px;
height: 16px;
margin: 0;
cursor: pointer;
flex-shrink: 0;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--surface);
vertical-align: middle;
position: relative;
box-sizing: border-box;
}
.row-check:hover {
border-color: var(--primary);
}
.row-check:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
.row-check:checked {
background: var(--primary);
border-color: var(--primary);
}
.row-check:checked::after {
content: "";
position: absolute;
left: 5px;
top: 2px;
width: 3px;
height: 6px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
box-sizing: border-box;
}
.row-check:indeterminate {
background: var(--primary);
border-color: var(--primary);
}
.row-check:indeterminate::after {
content: "";
position: absolute;
left: 3px;
top: 6px;
width: 10px;
height: 2px;
background: #fff;
border-radius: 1px;
transform: none;
border: none;
}
th.th-op,
td.td-op {
position: sticky;
right: 0;
left: auto;
z-index: 2;
width: auto;
min-width: 196px;
max-width: 220px;
box-shadow: -6px 0 10px -6px rgba(0, 0, 0, 0.1);
background: var(--surface);
vertical-align: middle;
text-align: center;
}
tbody tr:nth-child(even) td.td-op { background: var(--row-alt); }
tbody tr:hover td.td-op { background: var(--row-hover); }
.cell-ops {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
gap: 6px;
justify-content: center;
}
.btn-op {
height: 28px;
padding: 0 8px;
flex-shrink: 0;
white-space: nowrap;
font-size: 12px;
font-weight: 600;
border-radius: 6px;
border: 1px solid var(--border);
background: var(--surface);
cursor: pointer;
font-family: inherit;
color: var(--text);
display: inline-flex;
align-items: center;
gap: 4px;
}
.btn-op:hover { background: var(--primary-bg); border-color: var(--primary); color: var(--primary-dark); }
.btn-op.primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-op.primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); color: #fff; }
/* 主状态用品牌绿;次要状态用中性灰,避免一屏多种「彩」 */
.tag {
display: inline-block;
padding: 2px 8px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
background: var(--primary-bg);
color: var(--primary-dark);
}
.tag.muted {
background: var(--table-head);
color: var(--text-muted);
font-weight: 500;
}
/* 树形列表:父表与子表列定义完全分离,子表为内嵌独立表格 */
.tree-panel-note {
margin: 0 0 12px;
padding: 10px 14px;
font-size: 13px;
color: var(--text-muted);
background: var(--primary-bg);
border-radius: 8px;
border: 1px solid rgba(16, 185, 129, 0.2);
line-height: 1.5;
}
.tree-parent-grid {
min-width: 920px;
table-layout: fixed;
}
table.data-table.tree-parent-grid {
min-width: 920px;
}
.tree-child-table .th-op,
.tree-child-table td.td-op {
position: static;
left: auto;
right: auto;
box-shadow: none;
min-width: 0;
max-width: none;
}
.tree-parent-grid thead th {
text-align: center;
font-size: 12px;
color: var(--text-muted);
}
.tree-parent-grid thead th.th-tree-parent-left {
text-align: left;
}
.tree-parent-grid tbody tr.tree-row-parent td {
vertical-align: middle;
background: var(--surface);
font-weight: 500;
}
.tree-parent-grid tbody tr.tree-row-parent:hover td {
background: var(--primary-bg);
}
.tree-row-children-wrap td.td-tree-nested {
padding: 0 !important;
border-bottom: 1px solid var(--border);
vertical-align: top;
background: #f8fafc;
}
.tree-nested-inner {
padding: 12px 12px 14px 16px;
border-left: 3px solid var(--primary);
margin: 0;
}
.tree-nested-label {
font-size: 12px;
font-weight: 700;
color: var(--text-muted);
margin: 0 0 8px;
letter-spacing: 0.02em;
}
.tree-child-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
}
.tree-child-table thead th {
padding: 8px 10px;
text-align: center;
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
background: #e2e8f0;
border-bottom: 1px solid var(--border);
}
.tree-child-table thead th.th-child-left {
text-align: left;
}
.tree-child-table tbody td {
padding: 8px 10px;
text-align: center;
border-bottom: 1px solid var(--border);
vertical-align: middle;
background: var(--surface);
}
.tree-child-table tbody tr:last-child td {
border-bottom: none;
}
.tree-child-table tbody tr:hover td {
background: #f1f5f9;
}
.tree-child-table tbody td.td-child-left {
text-align: left;
}
.tree-child-table .col-amt {
font-variant-numeric: tabular-nums;
color: var(--primary-dark);
font-weight: 600;
}
.tree-row-children-wrap.is-hidden {
display: none !important;
}
/* 孙列表:再嵌套一层独立表格,列与父表、子表均不同 */
.tree-nested-deep {
margin-top: 4px;
padding-left: 14px !important;
border-left: 3px solid #f59e0b;
background: linear-gradient(90deg, rgba(254, 243, 199, 0.35) 0%, transparent 12px);
}
.tree-nested-deep .tree-nested-label {
color: #b45309;
}
.td-tree-nested-l2 {
padding: 0 !important;
border-bottom: none !important;
background: #fffbeb !important;
vertical-align: top;
}
.tree-l2-wrap.is-hidden {
display: none !important;
}
.tree-grandchild-table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
background: var(--surface);
border: 1px solid #fcd34d;
border-radius: 8px;
overflow: hidden;
}
.tree-grandchild-table thead th {
padding: 6px 8px;
text-align: center;
font-size: 10px;
font-weight: 600;
color: #92400e;
background: #fef3c7;
border-bottom: 1px solid #fcd34d;
}
.tree-grandchild-table thead th.th-gc-left {
text-align: left;
}
.tree-grandchild-table tbody td {
padding: 6px 8px;
text-align: center;
border-bottom: 1px solid #fef3c7;
vertical-align: middle;
background: #fff;
}
.tree-grandchild-table tbody tr:last-child td {
border-bottom: none;
}
.tree-grandchild-table tbody tr:hover td {
background: #fffbeb;
}
.tree-grandchild-table .td-gc-left {
text-align: left;
}
.tree-grandchild-table .col-amt {
font-variant-numeric: tabular-nums;
color: #b45309;
font-weight: 600;
}
.tree-grandchild-table .th-op,
.tree-grandchild-table td.td-op {
position: static;
box-shadow: none;
min-width: 0;
}
.tree-grandchild-table .td-tree-lead {
min-width: 80px;
width: auto;
padding: 6px 8px !important;
text-align: left !important;
}
.tree-grandchild-table .td-tree-lead .td-tree-lead-inner {
justify-content: flex-start;
}
.tree-expand-nested {
width: 24px;
height: 24px;
}
/* 无下级时占位,与展开按钮同宽,保证勾选对齐 */
.tree-expand-spacer {
display: inline-block;
width: 24px;
height: 24px;
flex-shrink: 0;
}
.tree-child-table .td-tree-lead {
min-width: 108px;
width: auto;
}
.tree-cb-grandchild {
appearance: none;
-webkit-appearance: none;
width: 14px;
height: 14px;
margin: 0;
cursor: pointer;
flex-shrink: 0;
border: 1px dashed #d97706;
border-radius: 3px;
background: #fffbeb;
vertical-align: middle;
position: relative;
box-sizing: border-box;
}
.tree-cb-grandchild:hover {
border-style: solid;
border-color: #f59e0b;
}
.tree-cb-grandchild:checked {
background: #f59e0b;
border-color: #f59e0b;
border-style: solid;
}
.tree-cb-grandchild:checked::after {
content: "";
position: absolute;
left: 3px;
top: 0px;
width: 3px;
height: 6px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.td-tree-lead {
width: 120px;
min-width: 120px;
padding: 10px 12px !important;
text-align: left !important;
}
.td-tree-lead-inner {
display: flex;
align-items: center;
gap: 8px;
}
.tree-expand {
flex-shrink: 0;
width: 28px;
height: 28px;
padding: 0;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--surface);
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
transition: transform 0.2s ease, background 0.15s;
}
.tree-expand:hover {
border-color: var(--primary);
color: var(--primary-dark);
background: var(--primary-bg);
}
.tree-expand[aria-expanded="false"] .tree-expand-ic {
transform: rotate(-90deg);
}
.tree-expand-ic {
display: block;
transition: transform 0.2s ease;
}
/* 父行专用勾选:尺寸与主色描边,与平面列表的 .row-check 区分 */
.tree-cb-parent {
appearance: none;
-webkit-appearance: none;
width: 18px;
height: 18px;
margin: 0;
cursor: pointer;
flex-shrink: 0;
border: 2px solid var(--primary);
border-radius: 5px;
background: var(--surface);
vertical-align: middle;
position: relative;
box-sizing: border-box;
}
.tree-cb-parent:checked {
background: var(--primary);
border-color: var(--primary);
}
.tree-cb-parent:checked::after {
content: "";
position: absolute;
left: 5px;
top: 2px;
width: 4px;
height: 8px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.tree-cb-child {
appearance: none;
-webkit-appearance: none;
width: 15px;
height: 15px;
margin: 0;
cursor: pointer;
flex-shrink: 0;
border: 1px solid #94a3b8;
border-radius: 4px;
background: #fff;
vertical-align: middle;
position: relative;
box-sizing: border-box;
}
.tree-cb-child:hover {
border-color: var(--primary);
}
.tree-cb-child:checked {
background: #64748b;
border-color: #64748b;
}
.tree-cb-child:checked::after {
content: "";
position: absolute;
left: 4px;
top: 1px;
width: 3px;
height: 6px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.tree-badge {
display: inline-block;
margin-left: 8px;
padding: 1px 6px;
font-size: 11px;
font-weight: 700;
border-radius: 4px;
vertical-align: middle;
}
.tree-badge.parent {
background: var(--primary-bg);
color: var(--primary-dark);
}
.tree-badge.child {
background: #e2e8f0;
color: #475569;
}
.backdrop {
position: fixed;
inset: 0;
background: rgba(15, 23, 42, 0.35);
display: none;
align-items: center;
justify-content: center;
z-index: 50;
padding: 16px;
}
.backdrop.open { display: flex; align-items: flex-start; justify-content: center; padding-top: 32px; padding-bottom: 32px; }
.dialog {
width: 100%;
max-width: 400px;
background: var(--surface);
border-radius: var(--radius);
border: 1px solid var(--border);
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12);
padding: 20px;
}
.dialog.dialog-lg {
max-width: 720px;
max-height: calc(100vh - 64px);
overflow: hidden;
display: flex;
flex-direction: column;
padding: 0;
}
.dialog.dialog-view-xl {
max-width: 920px;
width: 100%;
max-height: calc(100vh - 48px);
overflow: hidden;
display: flex;
flex-direction: column;
padding: 0;
}
.dialog-view-xl .dialog-scroll {
overflow-y: auto;
padding: 0;
flex: 1;
}
.view-hero {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 16px;
padding: 20px 22px;
background: linear-gradient(135deg, var(--primary-bg) 0%, var(--surface) 55%);
border-bottom: 1px solid var(--border);
}
.view-hero-img {
width: 72px;
height: 72px;
border-radius: 12px;
object-fit: cover;
border: 1px solid var(--border);
flex-shrink: 0;
}
.view-hero-main { flex: 1; min-width: 200px; }
.view-hero-main h2 {
margin: 0 0 8px;
font-size: 18px;
font-weight: 700;
letter-spacing: -0.02em;
}
.view-hero-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.view-hero-meta {
margin: 10px 0 0;
font-size: 13px;
color: var(--text-muted);
line-height: 1.5;
}
.view-section {
padding: 18px 22px;
border-bottom: 1px solid var(--border);
}
.view-section:last-child { border-bottom: none; }
.view-section h3 {
margin: 0 0 14px;
font-size: 13px;
font-weight: 700;
color: var(--text-muted);
letter-spacing: 0.06em;
}
.view-kv-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px 28px;
}
@media (max-width: 640px) {
.view-kv-grid { grid-template-columns: 1fr; }
}
.view-kv {
display: grid;
grid-template-columns: 96px 1fr;
gap: 8px 12px;
font-size: 13px;
align-items: start;
}
.view-kv dt {
margin: 0;
color: var(--text-muted);
font-weight: 500;
}
.view-kv dd {
margin: 0;
color: var(--text);
word-break: break-word;
}
.view-bento {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
@media (max-width: 640px) {
.view-bento { grid-template-columns: 1fr; }
}
.view-card {
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 14px 16px;
background: var(--surface);
}
.view-card h4 {
margin: 0 0 8px;
font-size: 12px;
font-weight: 700;
color: var(--text-muted);
}
.view-card p {
margin: 0;
font-size: 13px;
color: var(--text);
line-height: 1.55;
}
.view-timeline {
position: relative;
padding-left: 18px;
border-left: 2px solid var(--border);
margin-left: 8px;
}
.view-tl-item {
position: relative;
padding-bottom: 18px;
font-size: 13px;
}
.view-tl-item:last-child { padding-bottom: 0; }
.view-tl-item::before {
content: "";
position: absolute;
left: -25px;
top: 4px;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--primary);
border: 2px solid var(--surface);
box-shadow: 0 0 0 1px var(--primary);
}
.view-tl-item.muted::before {
background: var(--border);
box-shadow: none;
}
.view-tl-time { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.view-tl-title { font-weight: 600; color: var(--text); }
.view-tl-desc { color: var(--text-muted); margin-top: 4px; line-height: 1.45; }
.view-gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
@media (max-width: 640px) {
.view-gallery { grid-template-columns: repeat(2, 1fr); }
}
.view-gallery a {
display: block;
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border);
aspect-ratio: 4/3;
}
.view-gallery img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.view-route-bar {
height: 120px;
border-radius: var(--radius);
background: linear-gradient(90deg, #ecfdf5 0%, #d1fae5 40%, #a7f3d0 100%);
border: 1px solid var(--border);
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
margin-bottom: 12px;
}
.view-route-pin {
text-align: center;
font-size: 12px;
color: var(--text);
max-width: 120px;
}
.view-route-pin strong { display: block; font-size: 13px; margin-bottom: 4px; }
.view-route-line {
flex: 1;
height: 3px;
background: var(--primary);
opacity: 0.45;
margin: 0 12px;
border-radius: 2px;
}
.view-subtable-wrap {
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
}
.view-subtable {
width: 100%;
border-collapse: collapse;
font-size: 12px;
}
.view-subtable th,
.view-subtable td {
padding: 8px 12px;
text-align: center;
border-bottom: 1px solid var(--border);
}
.view-subtable th {
background: var(--table-head);
color: var(--text-muted);
font-weight: 600;
}
.view-subtable tr:last-child td { border-bottom: none; }
.view-subtable .ta-left { text-align: left; }
.dialog-lg .dialog-scroll {
overflow-y: auto;
padding: 20px 22px;
flex: 1;
}
.dialog-lg .dialog-head {
padding: 16px 22px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.dialog-lg .dialog-foot,
.dialog-view-xl .dialog-foot {
padding: 12px 22px;
border-top: 1px solid var(--border);
display: flex;
justify-content: flex-end;
gap: 8px;
flex-shrink: 0;
background: var(--table-head);
}
.dialog h3 { margin: 0 0 6px; font-size: 17px; font-weight: 600; }
.dialog .note { margin: 0 0 16px; font-size: 13px; color: var(--text-muted); }
.field { margin-bottom: 12px; }
.field label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 6px; }
.field .input { width: 100%; min-width: 0; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.form-section {
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px dashed var(--border);
}
.form-section:last-of-type { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.form-section h4 {
margin: 0 0 12px;
font-size: 13px;
font-weight: 700;
color: var(--text-muted);
letter-spacing: 0.04em;
}
.form-grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px 16px;
}
@media (max-width: 560px) {
.form-grid-2 { grid-template-columns: 1fr; }
}
.textarea {
width: 100%;
min-height: 72px;
padding: 10px 12px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 14px;
font-family: inherit;
resize: vertical;
}
.textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
}
.upload-zone {
border: 1px dashed var(--border);
border-radius: 8px;
padding: 16px;
text-align: center;
background: var(--row-alt);
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
}
.upload-zone:hover { border-color: var(--primary); background: var(--primary-bg); }
.upload-zone input[type="file"] { position: absolute; width: 0; height: 0; opacity: 0; pointer-events: none; }
.upload-zone-text { font-size: 13px; color: var(--text-muted); margin: 0; }
.upload-zone-btn { margin-top: 8px; }
.upload-preview {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 12px;
}
.upload-item {
position: relative;
width: 72px;
height: 72px;
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border);
background: var(--surface);
}
.upload-item img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.upload-item .rm {
position: absolute;
top: 2px;
right: 2px;
width: 20px;
height: 20px;
border: none;
border-radius: 4px;
background: rgba(0, 0, 0, 0.55);
color: #fff;
font-size: 14px;
line-height: 1;
cursor: pointer;
padding: 0;
}
.mini-table-wrap {
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
}
.mini-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.mini-table th {
background: var(--table-head);
padding: 8px 10px;
text-align: left;
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
border-bottom: 1px solid var(--border);
}
.mini-table td {
padding: 6px 8px;
border-bottom: 1px solid var(--border);
vertical-align: middle;
}
.mini-table tr:last-child td { border-bottom: none; }
.mini-table .input {
min-width: 0;
width: 100%;
height: 32px;
font-size: 13px;
}
.mini-table .input-clear-wrap .input {
padding-right: 30px;
height: 32px;
}
.mini-table .input-clear {
right: 2px;
width: 26px;
height: 26px;
}
.mini-table .btn-icon {
height: 28px;
padding: 0 8px;
font-size: 12px;
}
</style>
</head>
<body>
<div class="top-progress-bar" id="topProgressBar" aria-hidden="true"></div>
<div class="layout">
<aside class="sidebar">
<div class="logo">山峡化工<span>生产一体化门户</span></div>
<nav class="nav" aria-label="主导航">
<details class="nav-details" open>
<summary class="nav-summary">工作台</summary>
<div class="nav-sub">
<a href="#" data-tab-id="overview">运营概览</a>
</div>
</details>
<details class="nav-details" open>
<summary class="nav-summary">业务管理</summary>
<div class="nav-sub">
<a href="#" data-tab-id="order">订单管理</a>
<a class="active" href="#" data-tab-id="delivery">配送调度</a>
<a href="#" data-tab-id="product">商品中心</a>
<a href="#" data-tab-id="warehouse">仓储与库存</a>
</div>
</details>
<details class="nav-details" open>
<summary class="nav-summary">数据中心</summary>
<div class="nav-sub">
<a href="#" data-tab-id="report">报表中心</a>
<a href="#" data-tab-id="analysis">经营分析</a>
</div>
</details>
<details class="nav-details" open>
<summary class="nav-summary">系统设置</summary>
<div class="nav-sub">
<a href="#" data-tab-id="user">用户与权限</a>
<a href="#" data-tab-id="dict">参数与字典</a>
<a href="#" data-tab-id="log">操作日志</a>
</div>
</details>
</nav>
<div class="nav-foot"><a href="./index.html">返回配色入口</a></div>
</aside>
<div class="main">
<header class="header">
<div class="crumb">工作台 / <b id="crumbCurrent">配送调度</b></div>
</header>
<main class="content">
<div class="tabs-strip" id="tabsStrip" role="tablist" aria-label="已打开页面"></div>
<div class="tab-panel-mount" id="tabPanelMount">
<div class="tab-panel is-active" data-tab-id="delivery" id="tabPanelDelivery">
<header class="page-head">
<h1>配送调度</h1>
<p class="desc">点击左侧菜单会在上方打开对应 Tab,并重新加载该页内容(演示)。按钮操作带有加载动效。</p>
<p class="page-refresh-meta" style="margin:8px 0 0;font-size:12px;color:var(--text-muted)">
最近加载:<time id="deliveryRefreshedAt">—</time>
</p>
</header>
<section class="stats" aria-label="概览">
<div class="stat">
<div class="stat-hd">
<span class="stat-ic" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 18V6a2 2 0 00-2-2H4a2 2 0 00-2 2v11a1 1 0 001 1h2"/><path d="M15 18h2"/><path d="M19 18h2v-3.34a1 1 0 00-.14-.51l-2.86-4.65a1 1 0 00-.86-.5H14"/><circle cx="7" cy="18" r="2"/><circle cx="17" cy="18" r="2"/></svg>
</span>
</div>
<div class="stat-main">
<div class="k">在途车辆</div>
<div class="stat-side">
<div class="v">42</div>
<div class="hint">较昨日 +3</div>
</div>
</div>
</div>
<div class="stat">
<div class="stat-hd">
<span class="stat-ic" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/></svg>
</span>
</div>
<div class="stat-main">
<div class="k">待分配</div>
<div class="stat-side">
<div class="v">18</div>
<div class="hint">30 分钟内处理</div>
</div>
</div>
</div>
<div class="stat">
<div class="stat-hd">
<span class="stat-ic" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
</span>
</div>
<div class="stat-main">
<div class="k">准时送达</div>
<div class="stat-side">
<div class="v">97%</div>
<div class="hint">目标 ≥ 96%</div>
</div>
</div>
</div>
<div class="stat warn">
<div class="stat-hd">
<span class="stat-ic" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
</span>
</div>
<div class="stat-main">
<div class="k">异常告警</div>
<div class="stat-side">
<div class="v">4</div>
<div class="hint">待处理</div>
</div>
</div>
</div>
<div class="stat">
<div class="stat-hd">
<span class="stat-ic" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 17l6-6 4 4 8-8"/><path d="M21 10V4h-6"/></svg>
</span>
</div>
<div class="stat-main">
<div class="k">平均履约时长</div>
<div class="stat-side">
<div class="v">42m</div>
<div class="hint">环比 -5.1%</div>
</div>
</div>
</div>
<div class="stat">
<div class="stat-hd">
<span class="stat-ic" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="7" width="18" height="13" rx="2"/><path d="M16 3v4M8 3v4"/><path d="M3 12h18"/></svg>
</span>
</div>
<div class="stat-main">
<div class="k">今日交易额</div>
<div class="stat-side">
<div class="v">¥128k</div>
<div class="hint">较昨日 +6.2%</div>
</div>
</div>
</div>
</section>
<section class="panel">
<div class="panel-top">
<h2 style="margin:0">
<svg class="panel-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 6h13"/><path d="M8 12h13"/><path d="M8 18h13"/><path d="M3 6h.01"/><path d="M3 12h.01"/><path d="M3 18h.01"/></svg>
任务列表
</h2>
</div>
<div class="filter-block">
<form class="filter-form" action="#" method="get" onsubmit="return false;">
<p class="filter-hint">默认展示常用条件;特殊场景可展开全部。</p>
<div class="filter-grid">
<div class="filter-field">
<label for="f-no">任务号</label>
<div class="input-clear-wrap">
<input id="f-no" type="text" class="input" placeholder="精确或模糊" autocomplete="off" />
<button type="button" class="input-clear" aria-label="清除任务号" title="清除">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
</div>
</div>
<div class="filter-field">
<label for="f-route">线路 / 站点</label>
<div class="input-clear-wrap">
<input id="f-route" type="text" class="input" placeholder="关键词" autocomplete="off" />
<button type="button" class="input-clear" aria-label="清除线路关键词" title="清除">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
</div>
</div>
<div class="filter-field">
<label for="f-status">任务状态</label>
<select id="f-status" class="select" aria-label="任务状态">
<option value="">全部</option>
<option value="1">待分配</option>
<option value="2">执行中</option>
<option value="3">待装车</option>
<option value="4">已完成</option>
<option value="5">已取消</option>
</select>
</div>
<div class="filter-field">
<label for="f-driver">司机</label>
<select id="f-driver" class="select" aria-label="司机">
<option value="">全部</option>
<option value="wq">王强</option>
<option value="lm">李敏</option>
<option value="zs">张三</option>
</select>
</div>
</div>
<div id="filter-extra" class="filter-extra" hidden>
<div class="filter-grid">
<div class="filter-field">
<label for="f-region">所属区域</label>
<select id="f-region" class="select" aria-label="所属区域">
<option value="">全部</option>
<option value="hd">华东</option>
<option value="hn">华南</option>
<option value="hb">华北</option>
</select>
</div>
<div class="filter-field">
<label for="f-priority">优先级</label>
<select id="f-priority" class="select" aria-label="优先级">
<option value="">全部</option>
<option value="high">高</option>
<option value="mid">中</option>
<option value="low">低</option>
</select>
</div>
<div class="filter-field">
<label for="f-date-from">计划日期从</label>
<input id="f-date-from" type="date" class="input" />
</div>
<div class="filter-field">
<label for="f-date-to">计划日期至</label>
<input id="f-date-to" type="date" class="input" />
</div>
</div>
</div>
<div class="filter-actions">
<button
type="button"
class="btn btn-link"
id="toggleFilter"
aria-expanded="false"
aria-controls="filter-extra"
>
展开更多条件
</button>
<div class="filter-actions-end">
<button type="reset" class="btn">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 109-9 9.75 9.75 0 00-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
重置条件
</button>
<button type="submit" class="btn btn-primary">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
查询
</button>
</div>
</div>
</form>
</div>
<div class="list-body">
<div class="list-toolbar">
<div class="list-toolbar-row" role="toolbar" aria-label="列表操作">
<span class="list-toolbar-selected" id="selectedCountBadge" aria-live="polite">已选<span class="sel-num" id="selectedCountNum">0</span>条</span>
<button type="button" class="btn btn-primary" id="btnNew">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
新增任务
</button>
<button type="button" class="btn" id="btnBatchDelete">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
批量删除
</button>
<button type="button" class="btn" id="btnImport">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
导入
</button>
<input type="file" id="importFileInput" accept=".xlsx,.xls,.csv" style="display:none" aria-hidden="true" />
<button type="button" class="btn" id="btnExportList">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-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">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="8.5" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/></svg>
分配司机
</button>
<button type="button" class="btn">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
标记完成
</button>
<button type="button" class="btn">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
导出所选
</button>
</div>
</div>
<div class="table-scroll">
<table class="data-table">
<thead>
<tr>
<th class="th-check" scope="col">
<div class="th-check-inner">
<input type="checkbox" class="row-check" id="tableCheckAll" aria-label="全选当前页" title="全选" />
</div>
</th>
<th>任务号</th>
<th>线路</th>
<th>站点 / 货品</th>
<th>状态</th>
<th>司机</th>
<th>计划时间</th>
<th>备注</th>
<th>创建时间</th>
<th>数量</th>
<th>金额(元)</th>
<th class="th-op">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="td-check">
<div class="th-check-inner">
<input type="checkbox" class="row-check" name="taskRow" value="TK2026041501" aria-label="选择 TK2026041501" />
</div>
</td>
<td>TK2026041501</td>
<td class="td-left">华东仓 → 城配点 A</td>
<td class="td-cell-media td-left">
<div class="cell-media">
<img class="cell-media-img" src="https://picsum.photos/seed/sqxp-a/80/80" width="40" height="40" alt="" />
<div class="cell-media-stack">
<div class="cell-media-text">虹桥集散 · 厢式 3 号</div>
<div class="cell-media-sub">精品蔬菜箱 · 温控启用 · 需轻放易损品超长说明用于列表省略号演示……</div>
</div>
</div>
</td>
<td><span class="tag">执行中</span></td>
<td>王强</td>
<td style="color:var(--text-muted)">14:00 – 16:00</td>
<td class="td-left" style="color:var(--text-muted)">—</td>
<td style="color:var(--text-muted)">2026-04-15 09:12</td>
<td class="col-qty" data-qty="128">128</td>
<td class="col-amt" data-amt="15880">¥15,880.00</td>
<td class="td-op">
<div class="cell-ops">
<button type="button" class="btn-op primary js-btn-view">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
查看
</button>
<button type="button" class="btn-op">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
编辑
</button>
</div>
</td>
</tr>
<tr>
<td class="td-check">
<div class="th-check-inner">
<input type="checkbox" class="row-check" name="taskRow" value="TK2026041502" aria-label="选择 TK2026041502" />
</div>
</td>
<td>TK2026041502</td>
<td class="td-left">冷链 → 门店 B</td>
<td class="td-cell-media td-left">
<div class="cell-media">
<img class="cell-media-img" src="https://picsum.photos/seed/sqxp-b/80/80" width="40" height="40" alt="" />
<div class="cell-media-stack">
<div class="cell-media-text">浦东冷库 · 冷藏车 2 号</div>
<div class="cell-media-sub">冷冻水产 · 温控 -18℃ · 到点需扫码交接超长字段演示……</div>
</div>
</div>
</td>
<td><span class="tag muted">待装车</span></td>
<td>李敏</td>
<td style="color:var(--text-muted)">16:30 前</td>
<td class="td-left" style="color:var(--text-muted)">需保温</td>
<td style="color:var(--text-muted)">2026-04-15 08:40</td>
<td class="col-qty" data-qty="86">86</td>
<td class="col-amt" data-amt="9320.5">¥9,320.50</td>
<td class="td-op">
<div class="cell-ops">
<button type="button" class="btn-op primary js-btn-view">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
查看
</button>
<button type="button" class="btn-op">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
编辑
</button>
</div>
</td>
</tr>
</tbody>
<tfoot>
<tr class="total-row">
<td class="td-check" aria-hidden="true"></td>
<td class="td-total-label" colspan="8">本页合计</td>
<td class="col-qty" id="totalQty">0</td>
<td class="col-amt" id="totalAmt">¥0.00</td>
<td class="td-op" aria-hidden="true"></td>
</tr>
</tfoot>
</table>
</div>
<nav class="list-pager" aria-label="列表分页">
<div class="pager-meta">
<span id="pagerRange">第 0–0 条,共 0 条</span>
</div>
<div class="pager-controls">
<label class="pager-size-label" for="pageSizeSel">每页</label>
<select id="pageSizeSel" class="select pager-select" aria-label="每页条数">
<option value="10" selected>10</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
<span class="pager-size-suffix">条</span>
<button type="button" class="btn pager-btn" id="pagerPrev">上一页</button>
<div class="pager-nums" id="pagerNums" aria-label="页码"></div>
<button type="button" class="btn pager-btn" id="pagerNext">下一页</button>
</div>
</nav>
</div>
</section>
<section class="panel" aria-labelledby="tree-panel-title">
<div class="panel-top">
<h2 id="tree-panel-title" style="margin:0">
<svg class="panel-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v18"/><path d="M3 12h18"/><path d="M7 7h.01"/><path d="M17 7h.01"/><path d="M7 17h.01"/><path d="M17 17h.01"/></svg>
树形列表示例
</h2>
</div>
<p class="tree-panel-note">
共三层:<strong>父列表</strong>(汇总单维度)→ <strong>子列表</strong>(配送任务维度)→ <strong>孙列表</strong>(拣货批次维度,可选展开)。<strong>每一层都是独立表格、列字段互不共用</strong>;<strong>各级展开/收起均在首列</strong>(与勾选同一列,无下级列)。勾选分别为 <code>tree-cb-parent</code> / <code>tree-cb-child</code> / <code>tree-cb-grandchild</code>。
</p>
<div class="table-scroll">
<table class="data-table tree-parent-grid">
<thead>
<tr>
<th class="td-tree-lead" scope="col">展开 / 选择</th>
<th class="th-tree-parent-left" scope="col" style="min-width:160px">汇总单号</th>
<th class="th-tree-parent-left" scope="col" style="min-width:200px">计划线路 / 区域</th>
<th scope="col" style="width:88px">含子单数</th>
<th scope="col" style="min-width:120px">汇总金额</th>
<th scope="col" style="min-width:100px">对账状态</th>
<th class="th-op" scope="col" style="min-width:120px">父级操作</th>
</tr>
</thead>
<tbody>
<tr class="tree-row-parent" data-tree-id="g1">
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<button type="button" class="tree-expand tree-expand-root" aria-expanded="true" title="展开或收起子列表">
<svg class="tree-expand-ic" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<input type="checkbox" class="tree-cb-parent" name="treeParent" value="g1" aria-label="选择汇总单 H2026041501" />
</div>
</td>
<td class="td-left">H2026041501 <span class="tree-badge parent">父</span></td>
<td class="td-left">华东仓 → 城配早班(多站)</td>
<td>3</td>
<td class="col-amt">¥42,360.00</td>
<td><span class="tag muted">待对账</span></td>
<td class="td-op">
<button type="button" class="btn-op primary">拆单复核</button>
</td>
</tr>
<tr class="tree-row-children-wrap" data-tree-parent="g1">
<td class="td-tree-nested" colspan="7">
<div class="tree-nested-inner">
<div class="tree-nested-label">子列表 · 配送任务(列与父表独立;可再展开孙)</div>
<table class="tree-child-table">
<thead>
<tr>
<th class="td-tree-lead" scope="col">展开 / 选择</th>
<th class="th-child-left" scope="col" style="min-width:128px">配送任务号</th>
<th class="th-child-left" scope="col" style="min-width:120px">目的站点</th>
<th scope="col" style="width:64px">温区</th>
<th scope="col" style="width:56px">件数</th>
<th scope="col" style="min-width:92px">任务金额</th>
<th class="th-child-left" scope="col" style="min-width:64px">司机</th>
<th class="th-op" scope="col" style="min-width:88px">任务操作</th>
</tr>
</thead>
<tbody>
<tr class="tree-l1-row">
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<button type="button" class="tree-expand tree-expand-nested" aria-expanded="false" data-expand-target="g1-l2-t1" title="展开或收起孙列表(拣货批次)">
<svg class="tree-expand-ic" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<input type="checkbox" class="tree-cb-child" name="treeChild" value="g1-c1" aria-label="选择任务 TK2026041501" />
</div>
</td>
<td class="td-child-left">TK2026041501</td>
<td class="td-child-left">虹桥门店</td>
<td>冷藏</td>
<td>62</td>
<td class="col-amt">¥14,200.00</td>
<td class="td-child-left">王强</td>
<td class="td-op">
<button type="button" class="btn-op">改派</button>
</td>
</tr>
<tr class="tree-l2-wrap is-hidden" data-expand-for="g1-l2-t1">
<td class="td-tree-nested-l2" colspan="8">
<div class="tree-nested-inner tree-nested-deep">
<div class="tree-nested-label">孙列表 · 拣货批次明细(列与父表、子表均不同)</div>
<table class="tree-grandchild-table">
<thead>
<tr>
<th class="td-tree-lead" scope="col" style="width:56px">展开 / 选择</th>
<th class="th-gc-left" scope="col" style="min-width:120px">拣货批次号</th>
<th class="th-gc-left" scope="col" style="min-width:100px">SKU</th>
<th class="th-gc-left" scope="col" style="min-width:120px">品名</th>
<th scope="col" style="width:72px">规格</th>
<th scope="col" style="width:52px">件数</th>
<th scope="col" style="width:52px">重量(kg)</th>
<th scope="col" style="min-width:88px">批次小计</th>
<th class="th-op" scope="col" style="min-width:80px">批次操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<span class="tree-expand-spacer" aria-hidden="true"></span>
<input type="checkbox" class="tree-cb-grandchild" name="treeGc" value="g1-gc1" aria-label="选择批次 B2026041501" />
</div>
</td>
<td class="td-gc-left">B2026041501-A</td>
<td class="td-gc-left">VG-10086</td>
<td class="td-gc-left">精品黄瓜</td>
<td>5kg/箱</td>
<td>20</td>
<td>100</td>
<td class="col-amt">¥360.00</td>
<td class="td-op">
<button type="button" class="btn-op">复核</button>
</td>
</tr>
<tr>
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<span class="tree-expand-spacer" aria-hidden="true"></span>
<input type="checkbox" class="tree-cb-grandchild" name="treeGc" value="g1-gc2" aria-label="选择批次 B2026041501" />
</div>
</td>
<td class="td-gc-left">B2026041501-B</td>
<td class="td-gc-left">TM-22031</td>
<td class="td-gc-left">番茄礼盒</td>
<td>3kg/箱</td>
<td>12</td>
<td>36</td>
<td class="col-amt">¥390.00</td>
<td class="td-op">
<button type="button" class="btn-op">复核</button>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr class="tree-l1-row">
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<span class="tree-expand-spacer" aria-hidden="true"></span>
<input type="checkbox" class="tree-cb-child" name="treeChild" value="g1-c2" aria-label="选择任务 TK2026041502" />
</div>
</td>
<td class="td-child-left">TK2026041502</td>
<td class="td-child-left">浦东门店</td>
<td>冷冻</td>
<td>78</td>
<td class="col-amt">¥18,960.00</td>
<td class="td-child-left">李敏</td>
<td class="td-op">
<button type="button" class="btn-op">改派</button>
</td>
</tr>
<tr class="tree-l1-row">
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<span class="tree-expand-spacer" aria-hidden="true"></span>
<input type="checkbox" class="tree-cb-child" name="treeChild" value="g1-c3" aria-label="选择任务 TK2026041503" />
</div>
</td>
<td class="td-child-left">TK2026041503</td>
<td class="td-child-left">嘉定门店</td>
<td>常温</td>
<td>46</td>
<td class="col-amt">¥9,200.00</td>
<td class="td-child-left">张三</td>
<td class="td-op">
<button type="button" class="btn-op">改派</button>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr class="tree-row-parent" data-tree-id="g2">
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<button type="button" class="tree-expand tree-expand-root" aria-expanded="false" title="展开或收起子列表">
<svg class="tree-expand-ic" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<input type="checkbox" class="tree-cb-parent" name="treeParent" value="g2" aria-label="选择汇总单 H2026041502" />
</div>
</td>
<td class="td-left">H2026041502 <span class="tree-badge parent">父</span></td>
<td class="td-left">冷链仓 → 午后波次</td>
<td>2</td>
<td class="col-amt">¥28,100.00</td>
<td><span class="tag">对账中</span></td>
<td class="td-op">
<button type="button" class="btn-op primary">拆单复核</button>
</td>
</tr>
<tr class="tree-row-children-wrap is-hidden" data-tree-parent="g2">
<td class="td-tree-nested" colspan="7">
<div class="tree-nested-inner">
<div class="tree-nested-label">子列表 · 配送任务(列与父表独立;可再展开孙)</div>
<table class="tree-child-table">
<thead>
<tr>
<th class="td-tree-lead" scope="col">展开 / 选择</th>
<th class="th-child-left" scope="col" style="min-width:128px">配送任务号</th>
<th class="th-child-left" scope="col" style="min-width:120px">目的站点</th>
<th scope="col" style="width:64px">温区</th>
<th scope="col" style="width:56px">件数</th>
<th scope="col" style="min-width:92px">任务金额</th>
<th class="th-child-left" scope="col" style="min-width:64px">司机</th>
<th class="th-op" scope="col" style="min-width:88px">任务操作</th>
</tr>
</thead>
<tbody>
<tr class="tree-l1-row">
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<button type="button" class="tree-expand tree-expand-nested" aria-expanded="false" data-expand-target="g2-l2-t1" title="展开或收起孙列表(拣货批次)">
<svg class="tree-expand-ic" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
</button>
<input type="checkbox" class="tree-cb-child" name="treeChild" value="g2-c1" aria-label="选择任务 TK2026041504" />
</div>
</td>
<td class="td-child-left">TK2026041504</td>
<td class="td-child-left">宝山前置仓</td>
<td>冷冻</td>
<td>40</td>
<td class="col-amt">¥11,200.00</td>
<td class="td-child-left">赵磊</td>
<td class="td-op">
<button type="button" class="btn-op">改派</button>
</td>
</tr>
<tr class="tree-l2-wrap is-hidden" data-expand-for="g2-l2-t1">
<td class="td-tree-nested-l2" colspan="8">
<div class="tree-nested-inner tree-nested-deep">
<div class="tree-nested-label">孙列表 · 拣货批次明细(列与父表、子表均不同)</div>
<table class="tree-grandchild-table">
<thead>
<tr>
<th class="td-tree-lead" scope="col" style="width:56px">展开 / 选择</th>
<th class="th-gc-left" scope="col" style="min-width:120px">拣货批次号</th>
<th class="th-gc-left" scope="col" style="min-width:100px">SKU</th>
<th class="th-gc-left" scope="col" style="min-width:120px">品名</th>
<th scope="col" style="width:72px">规格</th>
<th scope="col" style="width:52px">件数</th>
<th scope="col" style="width:52px">重量(kg)</th>
<th scope="col" style="min-width:88px">批次小计</th>
<th class="th-op" scope="col" style="min-width:80px">批次操作</th>
</tr>
</thead>
<tbody>
<tr>
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<span class="tree-expand-spacer" aria-hidden="true"></span>
<input type="checkbox" class="tree-cb-grandchild" name="treeGc" value="g2-gc1" aria-label="选择批次 B2026041504" />
</div>
</td>
<td class="td-gc-left">B2026041504-X</td>
<td class="td-gc-left">FS-88302</td>
<td class="td-gc-left">冷冻水产拼箱</td>
<td>混箱</td>
<td>18</td>
<td>42</td>
<td class="col-amt">¥5,600.00</td>
<td class="td-op">
<button type="button" class="btn-op">复核</button>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr class="tree-l1-row">
<td class="td-tree-lead">
<div class="td-tree-lead-inner">
<span class="tree-expand-spacer" aria-hidden="true"></span>
<input type="checkbox" class="tree-cb-child" name="treeChild" value="g2-c2" aria-label="选择任务 TK2026041505" />
</div>
</td>
<td class="td-child-left">TK2026041505</td>
<td class="td-child-left">闵行门店</td>
<td>冷藏</td>
<td>54</td>
<td class="col-amt">¥16,900.00</td>
<td class="td-child-left">孙洁</td>
<td class="td-op">
<button type="button" class="btn-op">改派</button>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</section>
</div>
</div>
</main>
</div>
</div>
<div class="backdrop" id="backdrop" role="dialog" aria-modal="true" aria-labelledby="dlgTitle">
<div class="dialog dialog-lg" id="dlgNewTask">
<div class="dialog-head">
<h3 id="dlgTitle" style="margin:0">新建配送任务</h3>
<p class="note" style="margin:6px 0 0">复杂表单示意:基础信息、图片凭证、装车明细与备注。</p>
</div>
<div class="dialog-scroll">
<div class="form-section">
<h4>基础信息</h4>
<div class="form-grid-2">
<div class="field" style="margin:0">
<label for="dlgTaskName">任务名称</label>
<div class="input-clear-wrap">
<input id="dlgTaskName" type="text" class="input" placeholder="如:城配 A 线早班" autocomplete="off" />
<button type="button" class="input-clear" aria-label="清除任务名称" title="清除">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
</div>
</div>
<div class="field" style="margin:0">
<label for="dlgCarrier">承运商</label>
<select id="dlgCarrier" class="select" style="width:100%;min-width:0">
<option value="">请选择</option>
<option value="1">山峡化工物流</option>
<option value="2">合作车队 A</option>
</select>
</div>
<div class="field" style="margin:0">
<label for="dlgPhone">调度电话</label>
<div class="input-clear-wrap">
<input id="dlgPhone" type="tel" class="input" placeholder="11 位手机号" autocomplete="off" />
<button type="button" class="input-clear" aria-label="清除电话" title="清除">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
</div>
</div>
<div class="field" style="margin:0">
<label for="dlgEta">要求到达</label>
<div class="input-clear-wrap">
<input id="dlgEta" type="datetime-local" class="input" />
<button type="button" class="input-clear" aria-label="清除到达时间" title="清除">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
</div>
</div>
</div>
</div>
<div class="form-section">
<h4>现场凭证(图片上传)</h4>
<div class="upload-zone" id="uploadZone" role="button" tabindex="0">
<p class="upload-zone-text">支持多张,示意:装车单、封签、车况等</p>
<button type="button" class="btn btn-block upload-zone-btn" id="btnPickFiles">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
选择图片
</button>
<input id="dlgUpload" type="file" accept="image/*" multiple style="display:none" />
</div>
<div class="upload-preview" id="uploadPreview" aria-live="polite"></div>
</div>
<div class="form-section">
<h4>装车明细</h4>
<div class="mini-table-wrap">
<table class="mini-table">
<thead>
<tr>
<th style="width:48px">#</th>
<th>商品名称</th>
<th style="width:100px">数量</th>
<th style="width:80px">单位</th>
<th style="width:72px">操作</th>
</tr>
</thead>
<tbody id="dlgLinesBody">
<tr>
<td>1</td>
<td>
<div class="input-clear-wrap">
<input type="text" class="input" name="lineName[]" placeholder="如:精品黄瓜" autocomplete="off" />
<button type="button" class="input-clear" aria-label="清除商品名称" title="清除">
<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
</button>
</div>
</td>
<td><input type="number" class="input" name="lineQty[]" placeholder="0" min="0" step="1" /></td>
<td>
<select class="select" name="lineUnit[]" style="width:100%;min-width:0">
<option>件</option>
<option>箱</option>
<option>kg</option>
</select>
</td>
<td><button type="button" class="btn btn-icon js-remove-line">删除</button></td>
</tr>
</tbody>
</table>
</div>
<button type="button" class="btn" id="btnAddLine" style="margin-top:10px">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
添加一行
</button>
</div>
<div class="form-section">
<h4>备注说明</h4>
<label for="dlgRemark" class="sr-only">备注</label>
<textarea id="dlgRemark" class="textarea" placeholder="特殊要求、限行说明、客户交接注意事项等"></textarea>
</div>
</div>
<div class="dialog-foot">
<button type="button" class="btn" id="btnCancel">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
取消
</button>
<button type="button" class="btn btn-primary" id="btnSaveDemo">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
保存
</button>
</div>
</div>
</div>
<div class="backdrop" id="backdropView" role="dialog" aria-modal="true" aria-labelledby="viewTitleTaskNo">
<div class="dialog dialog-view-xl" id="dlgView">
<div class="dialog-head" style="padding:0;border:none">
<div class="view-hero">
<img id="viewHeroImg" class="view-hero-img" src="https://picsum.photos/seed/view-hero/144/144" width="72" height="72" alt="" />
<div class="view-hero-main">
<h2 id="viewTitleTaskNo">任务详情</h2>
<div class="view-hero-tags">
<span class="tag" id="viewStatusTag">执行中</span>
<span class="tag muted" id="viewPriorityTag">优先级 · 中</span>
<span class="tag muted">区域 · 华东</span>
</div>
<p class="view-hero-meta" id="viewHeroMeta">
线路:<span id="viewRouteLine">—</span> · 司机 <span id="viewDriverName">—</span> · 计划 <span id="viewPlanBrief">—</span>
</p>
</div>
</div>
</div>
<div class="dialog-scroll">
<div class="view-section">
<h3>基础信息</h3>
<dl class="view-kv-grid">
<div class="view-kv"><dt>任务号</dt><dd id="viewTaskNo">—</dd></div>
<div class="view-kv"><dt>创建时间</dt><dd id="viewCreatedAt">—</dd></div>
<div class="view-kv"><dt>线路</dt><dd id="viewRouteFull">—</dd></div>
<div class="view-kv"><dt>计划时间</dt><dd id="viewPlanTime">—</dd></div>
<div class="view-kv"><dt>站点 / 货品</dt><dd id="viewStationBrief">—</dd></div>
<div class="view-kv"><dt>备注</dt><dd id="viewRemarkFull">—</dd></div>
</dl>
</div>
<div class="view-section">
<h3>费用与件量</h3>
<div class="view-bento">
<div class="view-card">
<h4>本单数量(件)</h4>
<p id="viewQtyBig" style="font-size:22px;font-weight:700;color:var(--primary-dark)">0</p>
</div>
<div class="view-card">
<h4>本单金额(元)</h4>
<p id="viewAmtBig" style="font-size:22px;font-weight:700;color:var(--primary-dark)">¥0.00</p>
</div>
<div class="view-card" style="grid-column:1/-1">
<h4>费用说明</h4>
<p>含装卸、路桥及温控附加费;实际结算以回单签收后财务对账为准。以下为系统根据当前线路与货品类型估算的分摊明细。</p>
</div>
</div>
</div>
<div class="view-section">
<h3>路由示意</h3>
<div class="view-route-bar">
<div class="view-route-pin"><strong>始发仓</strong>华东中心仓</div>
<div class="view-route-line" aria-hidden="true"></div>
<div class="view-route-pin"><strong>经停</strong><span id="viewRouteMid">城配集散</span></div>
<div class="view-route-line" aria-hidden="true"></div>
<div class="view-route-pin"><strong>目的地</strong><span id="viewRouteEnd">门店/站点</span></div>
</div>
<p style="margin:0;font-size:12px;color:var(--text-muted)">示意图仅供调度参考,导航以司机端实时路径为准。</p>
</div>
<div class="view-section">
<h3>执行轨迹</h3>
<div class="view-timeline">
<div class="view-tl-item">
<div class="view-tl-time">2026-04-15 08:20</div>
<div class="view-tl-title">任务创建 · 调度已排班</div>
<div class="view-tl-desc">系统自动生成运单,已推送至承运商接口。</div>
</div>
<div class="view-tl-item">
<div class="view-tl-time" id="viewTlTime1">2026-04-15 09:00</div>
<div class="view-tl-title">司机接单 · 前往提货点</div>
<div class="view-tl-desc" id="viewTlDesc1">车辆已出库,预计途中耗时以实时路况为准。</div>
</div>
<div class="view-tl-item muted">
<div class="view-tl-time">待发生</div>
<div class="view-tl-title">到站卸货 · 签收确认</div>
<div class="view-tl-desc">完成后将同步回单照片与电子签名。</div>
</div>
</div>
</div>
<div class="view-section">
<h3>现场影像</h3>
<div class="view-gallery">
<a href="https://picsum.photos/seed/v1/800/600" target="_blank" rel="noopener noreferrer"><img src="https://picsum.photos/seed/v1/400/300" width="400" height="300" alt="装车" loading="lazy" /></a>
<a href="https://picsum.photos/seed/v2/800/600" target="_blank" rel="noopener noreferrer"><img src="https://picsum.photos/seed/v2/400/300" width="400" height="300" alt="封签" loading="lazy" /></a>
<a href="https://picsum.photos/seed/v3/800/600" target="_blank" rel="noopener noreferrer"><img src="https://picsum.photos/seed/v3/400/300" width="400" height="300" alt="车况" loading="lazy" /></a>
<a href="https://picsum.photos/seed/v4/800/600" target="_blank" rel="noopener noreferrer"><img src="https://picsum.photos/seed/v4/400/300" width="400" height="300" alt="回单" loading="lazy" /></a>
</div>
</div>
<div class="view-section">
<h3>装车明细(示意)</h3>
<div class="view-subtable-wrap">
<table class="view-subtable">
<thead>
<tr>
<th class="ta-left">品名</th>
<th>规格</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ta-left">精品黄瓜</td>
<td>5kg/箱</td>
<td>20</td>
<td class="money-val">¥18.00</td>
<td class="money-val">¥360.00</td>
</tr>
<tr>
<td class="ta-left">番茄(礼盒)</td>
<td>3kg/箱</td>
<td>12</td>
<td class="money-val">¥32.50</td>
<td class="money-val">¥390.00</td>
</tr>
<tr>
<td class="ta-left">叶菜组合</td>
<td>2kg/袋</td>
<td>30</td>
<td class="money-val">¥9.80</td>
<td class="money-val">¥294.00</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="dialog-foot">
<button type="button" class="btn" id="btnViewClose">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>
关闭
</button>
<button type="button" class="btn btn-primary" id="btnViewPrintDemo">
<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 01-2-2v-5a2 2 0 012-2h16a2 2 0 012 2v5a2 2 0 01-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
打印预览(演示)
</button>
</div>
</div>
</div>
<script>
(function () {
var el = document.getElementById("backdrop");
var dlgNew = document.getElementById("dlgNewTask");
var backdropView = document.getElementById("backdropView");
var dlgView = document.getElementById("dlgView");
var uploadInput = document.getElementById("dlgUpload");
var uploadPreview = document.getElementById("uploadPreview");
var uploadZone = document.getElementById("uploadZone");
var btnPickFiles = document.getElementById("btnPickFiles");
function syncInputClearButtons(container) {
(container || document).querySelectorAll(".input-clear-wrap").forEach(function (wrap) {
var input = wrap.querySelector("input");
var btn = wrap.querySelector(".input-clear");
if (input && btn) btn.hidden = !input.value;
});
}
document.addEventListener("input", function (e) {
var wrap = e.target.closest(".input-clear-wrap");
if (!wrap || !e.target.matches("input")) return;
var btn = wrap.querySelector(".input-clear");
if (btn) btn.hidden = !e.target.value;
});
document.addEventListener("click", function (e) {
var btn = e.target.closest(".input-clear");
if (!btn) return;
var wrap = btn.closest(".input-clear-wrap");
if (!wrap) return;
var input = wrap.querySelector("input");
if (!input) return;
e.preventDefault();
input.value = "";
input.dispatchEvent(new Event("input", { bubbles: true }));
input.focus();
btn.hidden = true;
});
syncInputClearButtons();
var taskTableTbody = document.querySelector(".data-table tbody");
function getVisibleRowChecks() {
var out = [];
if (!taskTableTbody) return out;
[].forEach.call(taskTableTbody.querySelectorAll("tr"), function (tr) {
if (tr.classList.contains("is-hidden")) return;
var cb = tr.querySelector(".row-check");
if (cb) out.push(cb);
});
return out;
}
function syncSelectedCountBadge() {
var numEl = document.getElementById("selectedCountNum");
if (!numEl || !taskTableTbody) return;
var n = 0;
taskTableTbody.querySelectorAll(".row-check").forEach(function (cb) {
if (cb.checked) n++;
});
numEl.textContent = String(n);
}
function syncTableCheckAllUI() {
var all = document.getElementById("tableCheckAll");
if (!all) return;
var list = getVisibleRowChecks();
var n = 0;
list.forEach(function (cb) {
if (cb.checked) n++;
});
all.checked = n === list.length && list.length > 0;
all.indeterminate = n > 0 && n < list.length;
syncSelectedCountBadge();
}
(function initTaskTableSelectAll() {
var all = document.getElementById("tableCheckAll");
if (!all || !taskTableTbody) return;
all.addEventListener("change", function () {
getVisibleRowChecks().forEach(function (cb) {
cb.checked = all.checked;
});
all.indeterminate = false;
syncSelectedCountBadge();
});
taskTableTbody.addEventListener("change", function (e) {
if (!e.target.classList.contains("row-check")) return;
syncTableCheckAllUI();
});
})();
(function seedDemoTableRows() {
var tb = document.querySelector(".data-table tbody");
if (!tb || !tb.rows.length) return;
var templateRow = tb.rows[0];
var drivers = ["王强", "李敏", "张三", "赵磊", "孙洁", "周洋"];
for (var i = 0; i < 16; i++) {
var n = 3 + i;
var tr = templateRow.cloneNode(true);
var id = "TK20260415" + (n < 10 ? "0" : "") + n;
if (tr.cells[1]) tr.cells[1].textContent = id;
var cb = tr.querySelector(".row-check");
if (cb) {
cb.value = id;
cb.setAttribute("aria-label", "选择 " + id);
cb.checked = false;
}
if (tr.cells[2]) {
var code = String.fromCharCode(65 + (i % 26));
tr.cells[2].textContent =
"区域 " + code + " → 城配站 #" + (i + 1) + "(列多时横向滚动)";
tr.cells[2].classList.add("td-left");
}
if (tr.cells[3]) {
var wrap = tr.cells[3].querySelector(".cell-media");
if (wrap) {
var img = wrap.querySelector(".cell-media-img");
var t1 = wrap.querySelector(".cell-media-text");
var t2 = wrap.querySelector(".cell-media-sub");
if (img) img.src = "https://picsum.photos/seed/" + encodeURIComponent(id) + "/80/80";
if (t1) t1.textContent = "城配集散 · 厢式 " + ((i % 9) + 1) + " 号";
if (t2) {
t2.textContent =
"混装配载 · 批次 " +
(i + 1) +
" · 超长说明用于测试列表格内省略号与左对齐图文混排……";
}
}
tr.cells[3].classList.add("td-cell-media", "td-left");
}
if (tr.cells[5]) tr.cells[5].textContent = drivers[i % drivers.length];
if (tr.cells[6]) {
tr.cells[6].textContent = 9 + (i % 6) + ":00 – " + (11 + (i % 6)) + ":00";
}
if (tr.cells[7]) {
tr.cells[7].textContent =
i % 4 === 0 ? "优先卸货" : i % 4 === 1 ? "" : "—";
tr.cells[7].classList.add("td-left");
}
if (tr.cells[8]) {
var hh = 8 + (i % 9);
var mm = (i * 7) % 60;
tr.cells[8].textContent =
"2026-04-15 " + (hh < 10 ? "0" : "") + hh + ":" + (mm < 10 ? "0" : "") + mm;
}
if (tr.cells[9]) {
var q = 5 + ((i * 3) % 42);
tr.cells[9].textContent = String(q);
tr.cells[9].setAttribute("data-qty", String(q));
tr.cells[9].className = "col-qty";
}
if (tr.cells[10]) {
var amt = Math.round((320 + i * 217.35) * 100) / 100;
tr.cells[10].setAttribute("data-amt", String(amt));
tr.cells[10].textContent =
"¥" +
amt.toLocaleString("zh-CN", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
tr.cells[10].className = "col-amt";
}
tb.appendChild(tr);
}
})();
function refreshTaskTableTotals() {
var tb = taskTableTbody;
var qtyEl = document.getElementById("totalQty");
var amtEl = document.getElementById("totalAmt");
if (!tb || !qtyEl || !amtEl) return;
var qtySum = 0;
var amtSum = 0;
tb.querySelectorAll("tr").forEach(function (tr) {
if (tr.classList.contains("is-hidden")) return;
var qCell = tr.querySelector(".col-qty");
var aCell = tr.querySelector(".col-amt");
if (qCell) {
var qRaw = qCell.getAttribute("data-qty") || qCell.textContent.replace(/,/g, "");
var qv = parseFloat(qRaw, 10);
if (!isNaN(qv)) qtySum += qv;
}
if (aCell) {
var aRaw =
aCell.getAttribute("data-amt") ||
aCell.textContent.replace(/[¥¥,\s]/g, "").trim();
var av = parseFloat(aRaw, 10);
if (!isNaN(av)) amtSum += av;
}
});
qtyEl.textContent = String(Math.round(qtySum));
var amtRounded = Math.round(amtSum * 100) / 100;
amtEl.textContent =
"¥" +
amtRounded.toLocaleString("zh-CN", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
amtEl.setAttribute("data-amt", String(amtRounded));
}
(function initTaskTablePager() {
var tb = taskTableTbody;
var prev = document.getElementById("pagerPrev");
var next = document.getElementById("pagerNext");
var nums = document.getElementById("pagerNums");
var rangeEl = document.getElementById("pagerRange");
var sizeSel = document.getElementById("pageSizeSel");
if (!tb || !prev || !next || !nums || !rangeEl || !sizeSel) return;
var state = { page: 1 };
function rowList() {
return [].slice.call(tb.querySelectorAll("tr"));
}
function pageCount() {
var total = rowList().length;
var size = parseInt(sizeSel.value, 10) || 10;
return Math.max(1, Math.ceil(total / size) || 1);
}
function render() {
var rows = rowList();
var total = rows.length;
var size = parseInt(sizeSel.value, 10) || 10;
var pages = Math.max(1, Math.ceil(total / size) || 1);
if (state.page > pages) state.page = pages;
if (state.page < 1) state.page = 1;
var start = (state.page - 1) * size;
var end = Math.min(start + size, total);
rows.forEach(function (tr, i) {
if (i >= start && i < end) tr.classList.remove("is-hidden");
else tr.classList.add("is-hidden");
});
if (total === 0) {
rangeEl.textContent = "共 0 条";
} else {
rangeEl.textContent =
"第 " + (start + 1) + "–" + end + " 条,共 " + total + " 条";
}
prev.disabled = state.page <= 1;
next.disabled = state.page >= pages;
nums.innerHTML = "";
var cur = state.page;
function addNumBtn(p, isCur) {
var b = document.createElement("button");
b.type = "button";
b.className = "btn pager-num" + (isCur ? " is-current" : "");
b.textContent = String(p);
b.setAttribute("aria-label", "第 " + p + " 页");
if (isCur) {
b.setAttribute("aria-current", "page");
b.disabled = true;
} else {
b.addEventListener("click", function () {
state.page = p;
render();
});
}
nums.appendChild(b);
}
if (pages <= 7) {
for (var p = 1; p <= pages; p++) addNumBtn(p, p === cur);
} else {
var marks = {};
[1, pages, cur - 1, cur, cur + 1].forEach(function (x) {
if (x >= 1 && x <= pages) marks[x] = true;
});
var order = Object.keys(marks)
.map(function (k) {
return parseInt(k, 10);
})
.sort(function (a, b) {
return a - b;
});
var last = 0;
order.forEach(function (p) {
if (p - last > 1) {
var gap = document.createElement("span");
gap.className = "pager-gap";
gap.textContent = "…";
nums.appendChild(gap);
}
addNumBtn(p, p === cur);
last = p;
});
}
refreshTaskTableTotals();
syncTableCheckAllUI();
}
prev.addEventListener("click", function () {
state.page--;
render();
});
next.addEventListener("click", function () {
state.page++;
render();
});
sizeSel.addEventListener("change", function () {
state.page = 1;
render();
});
render();
})();
function openTaskView(tr) {
if (!tr || !backdropView) return;
function cell(i) {
return tr.cells[i] || null;
}
var taskNo = cell(1) ? cell(1).textContent.trim() : "—";
var elTask = document.getElementById("viewTaskNo");
var elTitle = document.getElementById("viewTitleTaskNo");
if (elTask) elTask.textContent = taskNo;
if (elTitle) elTitle.textContent = "任务 · " + taskNo;
var route = cell(2) ? cell(2).textContent.trim() : "—";
if (document.getElementById("viewRouteFull")) {
document.getElementById("viewRouteFull").textContent = route;
}
if (document.getElementById("viewRouteLine")) {
document.getElementById("viewRouteLine").textContent = route;
}
var mText = "";
var mSub = "";
var imgSrc = "https://picsum.photos/seed/view-hero/144/144";
var c3 = cell(3);
if (c3) {
var t1 = c3.querySelector(".cell-media-text");
var t2 = c3.querySelector(".cell-media-sub");
var img = c3.querySelector(".cell-media-img");
if (t1) mText = t1.textContent.trim();
if (t2) mSub = t2.textContent.trim();
if (img) {
var src = img.getAttribute("src") || "";
if (src) imgSrc = src.replace(/\/\d+\/\d+(\?.*)?$/, "/144/144");
}
var elSB = document.getElementById("viewStationBrief");
if (elSB) elSB.textContent = mText + (mSub ? " · " + mSub : "");
}
var heroImg = document.getElementById("viewHeroImg");
if (heroImg) heroImg.src = imgSrc;
var st = cell(4);
var tagEl = document.getElementById("viewStatusTag");
if (st && tagEl) {
var span = st.querySelector(".tag");
tagEl.textContent = span ? span.textContent.trim() : st.textContent.trim();
tagEl.className = span ? span.className : "tag";
}
var driver = cell(5) ? cell(5).textContent.trim() : "—";
if (document.getElementById("viewDriverName")) {
document.getElementById("viewDriverName").textContent = driver;
}
var plan = cell(6) ? cell(6).textContent.trim() : "—";
if (document.getElementById("viewPlanTime")) {
document.getElementById("viewPlanTime").textContent = plan;
}
if (document.getElementById("viewPlanBrief")) {
document.getElementById("viewPlanBrief").textContent = plan;
}
var remark = cell(7) ? cell(7).textContent.trim() : "";
if (document.getElementById("viewRemarkFull")) {
document.getElementById("viewRemarkFull").textContent = remark || "—";
}
var created = cell(8) ? cell(8).textContent.trim() : "—";
if (document.getElementById("viewCreatedAt")) {
document.getElementById("viewCreatedAt").textContent = created;
}
var qty = cell(9) ? cell(9).textContent.trim() : "0";
var amt = cell(10) ? cell(10).textContent.trim() : "¥0.00";
if (document.getElementById("viewQtyBig")) {
document.getElementById("viewQtyBig").textContent = qty;
}
if (document.getElementById("viewAmtBig")) {
document.getElementById("viewAmtBig").textContent = amt;
}
var parts = route.split("→");
if (document.getElementById("viewRouteMid")) {
document.getElementById("viewRouteMid").textContent =
parts.length > 1 ? parts[0].trim().slice(0, 14) : "经停点";
}
if (document.getElementById("viewRouteEnd")) {
document.getElementById("viewRouteEnd").textContent =
parts.length > 1 ? parts[parts.length - 1].trim() : route.slice(0, 18);
}
if (document.getElementById("viewTlTime1")) {
document.getElementById("viewTlTime1").textContent = created || "—";
}
if (document.getElementById("viewTlDesc1")) {
document.getElementById("viewTlDesc1").textContent =
"司机「" + driver + "」执行本单,线路:" + route + "。";
}
backdropView.classList.add("open");
var sc = dlgView && dlgView.querySelector(".dialog-scroll");
if (sc) sc.scrollTop = 0;
}
function closeTaskView() {
if (backdropView) backdropView.classList.remove("open");
}
if (taskTableTbody) {
taskTableTbody.addEventListener("click", function (e) {
if (!e.target.closest(".js-btn-view")) return;
var tr = e.target.closest("tr");
if (tr) openTaskView(tr);
});
}
var btnViewClose = document.getElementById("btnViewClose");
var btnViewPrintDemo = document.getElementById("btnViewPrintDemo");
if (btnViewClose) btnViewClose.onclick = closeTaskView;
if (btnViewPrintDemo) {
btnViewPrintDemo.onclick = function () {
alert("演示:可在此调用 window.print() 或对接导出 PDF。");
};
}
if (backdropView) {
backdropView.onclick = function (e) {
if (e.target === backdropView) closeTaskView();
};
}
if (dlgView) {
dlgView.addEventListener("click", function (e) {
e.stopPropagation();
});
}
function clearUploadPreview() {
if (!uploadPreview) return;
uploadPreview.querySelectorAll(".upload-item").forEach(function (item) {
var u = item.dataset.url;
if (u) URL.revokeObjectURL(u);
item.remove();
});
}
function openDialog() {
el.classList.add("open");
document.getElementById("dlgTaskName").focus();
}
function closeDialog() {
el.classList.remove("open");
clearUploadPreview();
}
document.getElementById("btnNew").onclick = openDialog;
var importFileInput = document.getElementById("importFileInput");
var btnBatchDelete = document.getElementById("btnBatchDelete");
var btnImport = document.getElementById("btnImport");
var btnExportList = document.getElementById("btnExportList");
if (btnBatchDelete) {
btnBatchDelete.onclick = function () {
alert("演示:将删除当前勾选的记录,接入接口后在此提交批量删除。");
};
}
if (btnImport && importFileInput) {
btnImport.onclick = function () {
importFileInput.click();
};
importFileInput.addEventListener("change", function () {
if (importFileInput.files && importFileInput.files.length) {
alert("演示:已选择文件「" + importFileInput.files[0].name + "」,可在此解析并上传。");
}
importFileInput.value = "";
});
}
if (btnExportList) {
btnExportList.onclick = function () {
alert("演示:导出当前列表(可按筛选条件导出结果集)。");
};
}
document.getElementById("btnCancel").onclick = closeDialog;
el.onclick = function (e) {
if (e.target === el) closeDialog();
};
document.addEventListener("keydown", function (e) {
if (e.key !== "Escape") return;
if (backdropView && backdropView.classList.contains("open")) {
closeTaskView();
return;
}
if (el.classList.contains("open")) closeDialog();
});
if (dlgNew) {
dlgNew.addEventListener("click", function (e) {
e.stopPropagation();
});
}
function addPreviewFile(file) {
if (!file.type.startsWith("image/")) return;
var url = URL.createObjectURL(file);
var wrap = document.createElement("div");
wrap.className = "upload-item";
wrap.dataset.url = url;
var img = document.createElement("img");
img.src = url;
img.alt = "预览";
var rm = document.createElement("button");
rm.type = "button";
rm.className = "rm";
rm.setAttribute("aria-label", "移除");
rm.textContent = "×";
rm.onclick = function () {
URL.revokeObjectURL(url);
wrap.remove();
};
wrap.appendChild(img);
wrap.appendChild(rm);
uploadPreview.appendChild(wrap);
}
function onFilesSelected(files) {
for (var i = 0; i < files.length; i++) {
addPreviewFile(files[i]);
}
uploadInput.value = "";
}
if (uploadInput && uploadPreview) {
uploadInput.addEventListener("change", function () {
if (uploadInput.files && uploadInput.files.length) {
onFilesSelected(uploadInput.files);
}
});
}
if (btnPickFiles && uploadInput) {
btnPickFiles.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
uploadInput.click();
});
}
if (uploadZone && uploadInput) {
uploadZone.addEventListener("click", function (e) {
if (e.target === uploadZone || (e.target.classList && e.target.classList.contains("upload-zone-text"))) {
uploadInput.click();
}
});
uploadZone.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
uploadInput.click();
}
});
}
var dlgLinesBody = document.getElementById("dlgLinesBody");
function renumberLines() {
if (!dlgLinesBody) return;
var rows = dlgLinesBody.querySelectorAll("tr");
for (var i = 0; i < rows.length; i++) {
rows[i].querySelector("td:first-child").textContent = String(i + 1);
}
}
function bindRemove(btn) {
btn.addEventListener("click", function () {
var tr = btn.closest("tr");
if (tr && dlgLinesBody.querySelectorAll("tr").length > 1) {
tr.remove();
renumberLines();
}
});
}
if (dlgLinesBody) {
dlgLinesBody.querySelectorAll(".js-remove-line").forEach(function (b) {
bindRemove(b);
});
}
var btnAddLine = document.getElementById("btnAddLine");
if (btnAddLine && dlgLinesBody) {
btnAddLine.addEventListener("click", function () {
var tr = document.createElement("tr");
tr.innerHTML =
'<td></td>' +
'<td><div class="input-clear-wrap">' +
'<input type="text" class="input" name="lineName[]" placeholder="如:精品黄瓜" autocomplete="off" />' +
'<button type="button" class="input-clear" aria-label="清除商品名称" title="清除">' +
'<svg class="ic-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg>' +
'</button></div></td>' +
'<td><input type="number" class="input" name="lineQty[]" placeholder="0" min="0" step="1" /></td>' +
'<td><select class="select" name="lineUnit[]" style="width:100%;min-width:0"><option>件</option><option>箱</option><option>kg</option></select></td>' +
'<td><button type="button" class="btn btn-icon js-remove-line">删除</button></td>';
bindRemove(tr.querySelector(".js-remove-line"));
dlgLinesBody.appendChild(tr);
renumberLines();
});
}
var btnSaveDemo = document.getElementById("btnSaveDemo");
if (btnSaveDemo) {
btnSaveDemo.addEventListener("click", function () {
alert("演示页:此处可提交表单或调接口。");
});
}
var toggleFilter = document.getElementById("toggleFilter");
var filterExtra = document.getElementById("filter-extra");
if (toggleFilter && filterExtra) {
toggleFilter.addEventListener("click", function () {
if (filterExtra.hasAttribute("hidden")) {
filterExtra.removeAttribute("hidden");
toggleFilter.setAttribute("aria-expanded", "true");
toggleFilter.textContent = "收起更多条件";
} else {
filterExtra.setAttribute("hidden", "");
toggleFilter.setAttribute("aria-expanded", "false");
toggleFilter.textContent = "展开更多条件";
}
});
var filterForm = document.querySelector(".filter-form");
if (filterForm) {
filterForm.addEventListener("reset", function () {
filterExtra.setAttribute("hidden", "");
toggleFilter.setAttribute("aria-expanded", "false");
toggleFilter.textContent = "展开更多条件";
setTimeout(function () {
syncInputClearButtons(filterForm);
}, 0);
});
}
}
(function initTreeTableExpand() {
document.querySelectorAll(".tree-expand-root").forEach(function (btn) {
btn.addEventListener("click", function () {
var parentTr = btn.closest("tr.tree-row-parent");
if (!parentTr) return;
var pid = parentTr.getAttribute("data-tree-id");
if (!pid) return;
var expanded = btn.getAttribute("aria-expanded") === "true";
var wrap = document.querySelector('tr.tree-row-children-wrap[data-tree-parent="' + pid + '"]');
if (wrap) wrap.classList.toggle("is-hidden", expanded);
btn.setAttribute("aria-expanded", expanded ? "false" : "true");
});
});
document.querySelectorAll(".tree-expand-nested").forEach(function (btn) {
btn.addEventListener("click", function () {
var tid = btn.getAttribute("data-expand-target");
if (!tid) return;
var expanded = btn.getAttribute("aria-expanded") === "true";
var wrap = document.querySelector('tr.tree-l2-wrap[data-expand-for="' + tid + '"]');
if (wrap) wrap.classList.toggle("is-hidden", expanded);
btn.setAttribute("aria-expanded", expanded ? "false" : "true");
});
});
})();
function setTopProgress(on) {
var bar = document.getElementById("topProgressBar");
if (!bar) return;
bar.classList.toggle("is-active", !!on);
}
function demoDelay(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
function buildPlaceholderPanel(title) {
return (
'<header class="page-head">' +
"<h1>" +
title +
"</h1>" +
'<p class="desc">演示占位页:真实项目中为对应路由页面。从左侧菜单进入时会先显示加载,再刷新本页数据。</p>' +
'<p class="page-refresh-meta" style="margin:8px 0 0;font-size:12px;color:var(--text-muted)">最近加载:<time class="placeholder-refreshed-at">—</time></p>' +
"</header>" +
'<section class="panel" style="padding:24px">' +
'<p style="margin:0;color:var(--text-muted);line-height:1.6">「' +
title +
"」模块内容区。可对接 Vue Router、KeepAlive 或 qiankun 子应用。</p>" +
"</section>"
);
}
function ensurePlaceholderTabPanel(tabId, title) {
var mount = document.getElementById("tabPanelMount");
if (!mount) return null;
var panel = mount.querySelector('.tab-panel[data-tab-id="' + tabId + '"]');
if (!panel) {
panel = document.createElement("div");
panel.className = "tab-panel";
panel.setAttribute("data-tab-id", tabId);
panel.id = "tabPanel_" + tabId;
panel.innerHTML = buildPlaceholderPanel(title);
mount.appendChild(panel);
}
return panel;
}
function activateTabStrip(tabId) {
var strip = document.getElementById("tabsStrip");
if (strip) {
strip.querySelectorAll(".tab-chip").forEach(function (chip) {
var on = chip.getAttribute("data-tab-id") === tabId;
chip.classList.toggle("is-active", on);
chip.setAttribute("aria-selected", on ? "true" : "false");
});
}
var mount = document.getElementById("tabPanelMount");
if (mount) {
mount.querySelectorAll(".tab-panel").forEach(function (p) {
p.classList.toggle("is-active", p.getAttribute("data-tab-id") === tabId);
});
}
}
function ensureTabChip(tabId, title) {
var strip = document.getElementById("tabsStrip");
if (!strip) return null;
var chip = strip.querySelector('.tab-chip[data-tab-id="' + tabId + '"]');
if (!chip) {
chip = document.createElement("button");
chip.type = "button";
chip.className = "tab-chip";
chip.setAttribute("data-tab-id", tabId);
chip.setAttribute("role", "tab");
chip.setAttribute("aria-selected", "false");
chip.textContent = title;
chip.addEventListener("click", function () {
activateTabStrip(tabId);
var crumb = document.getElementById("crumbCurrent");
if (crumb) crumb.textContent = chip.textContent;
document.querySelectorAll(".nav a[data-tab-id]").forEach(function (a) {
a.classList.toggle("active", a.getAttribute("data-tab-id") === tabId);
});
});
strip.appendChild(chip);
} else {
chip.textContent = title;
}
return chip;
}
function refreshTabContent(tabId, title) {
var panel =
tabId === "delivery"
? document.getElementById("tabPanelDelivery")
: ensurePlaceholderTabPanel(tabId, title);
if (!panel) return Promise.resolve();
panel.classList.add("is-refreshing");
setTopProgress(true);
return demoDelay(380 + Math.random() * 220).then(function () {
panel.classList.remove("is-refreshing");
setTopProgress(false);
if (tabId === "delivery") {
var t = document.getElementById("deliveryRefreshedAt");
if (t) {
t.textContent = new Date().toLocaleString("zh-CN", { hour12: false });
}
refreshTaskTableTotals();
} else {
var pt = panel.querySelector(".placeholder-refreshed-at");
if (pt) {
pt.textContent = new Date().toLocaleString("zh-CN", { hour12: false });
}
}
});
}
function openTabFromMenu(tabId, title) {
if (tabId !== "delivery") {
ensurePlaceholderTabPanel(tabId, title);
}
ensureTabChip(tabId, title);
activateTabStrip(tabId);
var crumb = document.getElementById("crumbCurrent");
if (crumb) crumb.textContent = title;
document.querySelectorAll(".nav a[data-tab-id]").forEach(function (a) {
a.classList.toggle("active", a.getAttribute("data-tab-id") === tabId);
});
return refreshTabContent(tabId, title);
}
(function initMainTabs() {
var nav = document.querySelector(".nav");
if (nav) {
nav.querySelectorAll("a[data-tab-id]").forEach(function (a) {
a.addEventListener("click", function (e) {
e.preventDefault();
var id = a.getAttribute("data-tab-id");
var title = a.textContent.trim();
if (id) openTabFromMenu(id, title);
});
});
}
ensureTabChip("delivery", "配送调度");
activateTabStrip("delivery");
var t0 = document.getElementById("deliveryRefreshedAt");
if (t0) {
t0.textContent = new Date().toLocaleString("zh-CN", { hour12: false });
}
})();
document.addEventListener(
"click",
function (e) {
var btn = e.target.closest("button");
if (!btn || btn.disabled) return;
if (btn.classList.contains("input-clear")) return;
if (btn.classList.contains("tree-expand")) return;
if (btn.classList.contains("tab-chip")) return;
if (btn.classList.contains("btn-link")) return;
if (btn.classList.contains("pager-num") && btn.classList.contains("is-current")) return;
if (btn.closest(".th-check-inner") && btn.type === "button") return;
if (
btn.matches(
".btn, .btn-primary, .btn-op, .pager-btn, .btn-icon, .upload-zone-btn, .btn-block"
)
) {
var wasDisabled = btn.disabled;
btn.classList.add("is-loading");
if (!wasDisabled) btn.disabled = true;
window.clearTimeout(btn._demoLoadT);
btn._demoLoadT = window.setTimeout(function () {
btn.classList.remove("is-loading");
if (!wasDisabled) btn.disabled = false;
}, 550);
}
},
true
);
})();
</script>
</body>
</html>