| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" />
- <title>司机链接 · 运输记录 Demo</title>
- <style>
- :root {
- --green: #059669;
- --green-deep: #047857;
- --green-soft: #ecfdf5;
- --text: #1e293b;
- --muted: #64748b;
- --line: #e2e8f0;
- --bg: #f1f5f9;
- --warn: #d97706;
- --danger: #dc2626;
- --card: #fff;
- --radius: 12px;
- --safe-bottom: env(safe-area-inset-bottom, 0px);
- }
- * { box-sizing: border-box; }
- body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Segoe UI", sans-serif;
- background: #0f172a;
- color: var(--text);
- min-height: 100vh;
- }
- .stage {
- max-width: 1100px;
- margin: 0 auto;
- padding: 24px 16px 48px;
- }
- .stage-head {
- color: #e2e8f0;
- margin-bottom: 20px;
- }
- .stage-head h1 {
- margin: 0 0 8px;
- font-size: 22px;
- font-weight: 700;
- }
- .stage-head p {
- margin: 0;
- color: #94a3b8;
- font-size: 14px;
- line-height: 1.6;
- }
- .phones {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
- gap: 20px;
- }
- .phone-wrap {
- background: #1e293b;
- border-radius: 20px;
- padding: 12px;
- border: 1px solid #334155;
- }
- .phone-label {
- color: #cbd5e1;
- font-size: 13px;
- font-weight: 600;
- margin: 0 0 10px 4px;
- }
- .phone {
- width: 100%;
- max-width: 390px;
- margin: 0 auto;
- background: var(--bg);
- border-radius: 18px;
- overflow: hidden;
- min-height: 720px;
- display: flex;
- flex-direction: column;
- box-shadow: 0 20px 40px rgba(0,0,0,.35);
- position: relative;
- }
- .nav {
- background: var(--green);
- color: #fff;
- height: 48px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- font-weight: 600;
- position: relative;
- flex-shrink: 0;
- }
- .nav .back {
- position: absolute;
- left: 12px;
- top: 50%;
- transform: translateY(-50%);
- background: transparent;
- border: 0;
- color: #fff;
- font-size: 14px;
- cursor: pointer;
- padding: 8px;
- }
- .scroll {
- flex: 1;
- overflow: auto;
- padding: 12px 12px 96px;
- }
- .scroll.no-footer-pad { padding-bottom: 12px; }
- .card {
- background: var(--card);
- border-radius: var(--radius);
- margin-bottom: 12px;
- overflow: hidden;
- border: 1px solid #eef2f7;
- }
- .card-title {
- padding: 10px 14px 0;
- font-size: 13px;
- color: var(--muted);
- font-weight: 600;
- }
- .row {
- display: flex;
- justify-content: space-between;
- gap: 12px;
- padding: 12px 14px;
- border-bottom: 1px solid #f1f5f9;
- font-size: 14px;
- }
- .row:last-child { border-bottom: 0; }
- .row .k { color: var(--muted); flex-shrink: 0; }
- .row .v { color: var(--text); text-align: right; word-break: break-all; }
- .empty {
- padding: 64px 24px;
- text-align: center;
- color: var(--muted);
- }
- .empty .icon {
- width: 64px;
- height: 64px;
- margin: 0 auto 16px;
- border-radius: 50%;
- background: #e2e8f0;
- display: grid;
- place-items: center;
- font-size: 28px;
- color: #94a3b8;
- }
- .empty .title {
- font-size: 15px;
- color: #475569;
- margin-bottom: 6px;
- }
- .footer {
- position: absolute;
- left: 0;
- right: 0;
- bottom: 0;
- padding: 10px 12px calc(10px + var(--safe-bottom));
- background: rgba(255,255,255,.96);
- border-top: 1px solid var(--line);
- display: flex;
- flex-direction: column;
- gap: 8px;
- backdrop-filter: blur(8px);
- }
- .btn {
- height: 44px;
- border-radius: 999px;
- border: 0;
- font-size: 15px;
- font-weight: 600;
- cursor: pointer;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- gap: 6px;
- }
- .btn-primary {
- background: var(--green);
- color: #fff;
- }
- .btn-primary:active { background: var(--green-deep); }
- .btn-ghost {
- background: #fff;
- color: var(--green);
- border: 1px solid #a7f3d0;
- }
- .btn-ghost:active { background: var(--green-soft); }
- .btn-row {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 8px;
- }
- .filters {
- background: var(--card);
- border-radius: var(--radius);
- padding: 12px;
- margin-bottom: 12px;
- border: 1px solid #eef2f7;
- }
- .filter-row {
- display: flex;
- align-items: center;
- gap: 8px;
- margin-bottom: 10px;
- flex-wrap: wrap;
- }
- .filter-row:last-child { margin-bottom: 0; }
- .chip {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- height: 32px;
- padding: 0 12px;
- border-radius: 999px;
- border: 1px solid var(--line);
- background: #fff;
- font-size: 13px;
- color: var(--text);
- cursor: pointer;
- user-select: none;
- }
- .chip.on {
- background: var(--green-soft);
- border-color: #6ee7b7;
- color: var(--green-deep);
- font-weight: 600;
- }
- .chip input { display: none; }
- .date-inputs {
- display: grid;
- grid-template-columns: 1fr auto 1fr;
- gap: 8px;
- align-items: center;
- width: 100%;
- }
- .date-inputs input {
- height: 36px;
- border: 1px solid var(--line);
- border-radius: 8px;
- padding: 0 10px;
- font-size: 13px;
- color: var(--text);
- background: #fff;
- width: 100%;
- }
- .date-inputs span { color: var(--muted); font-size: 12px; }
- .hint {
- font-size: 12px;
- color: var(--muted);
- margin-top: 8px;
- }
- .record {
- background: var(--card);
- border-radius: var(--radius);
- border: 1px solid #eef2f7;
- padding: 14px;
- margin-bottom: 10px;
- cursor: pointer;
- transition: border-color .15s, box-shadow .15s;
- }
- .record:active, .record:hover {
- border-color: #6ee7b7;
- box-shadow: 0 0 0 3px rgba(16,185,129,.12);
- }
- .record-top {
- display: flex;
- justify-content: space-between;
- gap: 8px;
- align-items: flex-start;
- margin-bottom: 8px;
- }
- .record-no {
- font-size: 15px;
- font-weight: 700;
- }
- .tag {
- display: inline-flex;
- align-items: center;
- height: 22px;
- padding: 0 8px;
- border-radius: 6px;
- font-size: 12px;
- font-weight: 600;
- color: #fff;
- flex-shrink: 0;
- }
- .tag.ok { background: var(--green); }
- .tag.arch { background: #64748b; }
- .tag.void { background: var(--danger); }
- .tag.warn { background: var(--warn); }
- .record-meta {
- font-size: 13px;
- color: var(--muted);
- line-height: 1.55;
- }
- .record-meta strong { color: var(--text); font-weight: 500; }
- .mini-tabs {
- display: flex;
- gap: 8px;
- margin-bottom: 12px;
- flex-wrap: wrap;
- }
- .mini-tabs button {
- border: 1px solid #334155;
- background: #0f172a;
- color: #94a3b8;
- border-radius: 999px;
- padding: 8px 12px;
- font-size: 12px;
- cursor: pointer;
- }
- .mini-tabs button.active {
- background: var(--green);
- border-color: var(--green);
- color: #fff;
- font-weight: 600;
- }
- .note {
- margin-top: 18px;
- padding: 14px 16px;
- background: #1e293b;
- border: 1px solid #334155;
- border-radius: 12px;
- color: #cbd5e1;
- font-size: 13px;
- line-height: 1.7;
- }
- .note strong { color: #f8fafc; }
- .screen { display: none; flex: 1; flex-direction: column; min-height: 0; }
- .screen.active { display: flex; }
- .product {
- padding: 12px 14px;
- border-top: 1px solid #f1f5f9;
- }
- .product-name { font-weight: 600; margin-bottom: 6px; }
- .product-meta { font-size: 13px; color: var(--muted); }
- </style>
- </head>
- <body>
- <div class="stage">
- <div class="stage-head">
- <h1>司机链接 ·「运输记录」交互 Demo</h1>
- <p>
- 列表默认<strong>今天</strong>。筛选为:单日日期框 +「今天 / 明天 / 后天」快捷按钮。
- 命中:取货日或送货日等于所选日。按<strong>主车车牌</strong>聚合。
- </p>
- <div class="mini-tabs" id="jumpTabs">
- <button type="button" class="active" data-jump="A">A · 正常详情</button>
- <button type="button" data-jump="B">B · 已归档详情</button>
- <button type="button" data-jump="C">C · 运输记录列表</button>
- <button type="button" data-jump="D">D · 作废详情</button>
- </div>
- </div>
- <div class="phones">
- <!-- Phone interactive -->
- <div class="phone-wrap">
- <div class="phone-label">可交互 Demo(点底部按钮 / 点记录)</div>
- <div class="phone" id="phone">
- <!-- Screen: active detail -->
- <div class="screen active" data-screen="detail-active">
- <div class="nav">运输计划</div>
- <div class="scroll">
- <div class="card">
- <div class="row"><span class="k">计划编号</span><span class="v">YS20260821001</span></div>
- <div class="row"><span class="k">计划日期</span><span class="v">2026-08-21</span></div>
- <div class="row"><span class="k">车牌</span><span class="v">苏A·D12345</span></div>
- <div class="row"><span class="k">司机</span><span class="v">张师傅</span></div>
- </div>
- <div class="card">
- <div class="card-title">取货信息</div>
- <div class="row"><span class="k">取货地址</span><span class="v">南京市江宁区××仓库</span></div>
- <div class="row"><span class="k">取货时间</span><span class="v">2026-08-21 09:00</span></div>
- </div>
- <div class="card">
- <div class="card-title">产品回传数量</div>
- <div class="product">
- <div class="product-name">硫酸</div>
- <div class="product-meta">计划 28 吨 · 实际待填</div>
- </div>
- </div>
- </div>
- <div class="footer">
- <div class="btn-row">
- <button class="btn btn-ghost" type="button" data-go="list">运输记录</button>
- <button class="btn btn-primary" type="button">保存回执</button>
- </div>
- </div>
- </div>
- <!-- Screen: archived detail (from link or list) -->
- <div class="screen" data-screen="detail-archived">
- <div class="nav">运输计划</div>
- <div class="scroll no-footer-pad">
- <div class="empty">
- <div class="icon">📦</div>
- <div class="title">运输计划已归档完成</div>
- <div>业务详情不可查看</div>
- </div>
- </div>
- <div class="footer">
- <button class="btn btn-ghost" type="button" data-go="list">运输记录</button>
- </div>
- </div>
- <!-- Screen: voided detail -->
- <div class="screen" data-screen="detail-voided">
- <div class="nav">运输计划</div>
- <div class="scroll no-footer-pad">
- <div class="empty">
- <div class="icon">⛔</div>
- <div class="title">运输计划已作废</div>
- <div>业务详情不可查看</div>
- </div>
- </div>
- <div class="footer">
- <button class="btn btn-ghost" type="button" data-go="list">运输记录</button>
- </div>
- </div>
- <!-- Screen: history list -->
- <div class="screen" data-screen="list">
- <div class="nav">
- <button class="back" type="button" data-go="detail-active">‹ 返回</button>
- 运输记录
- </div>
- <div class="scroll" style="padding-bottom:16px">
- <div class="filters">
- <div class="filter-row" style="gap:8px">
- <button type="button" class="chip on" data-quick="0">今天</button>
- <button type="button" class="chip" data-quick="1">明天</button>
- <button type="button" class="chip" data-quick="2">后天</button>
- </div>
- <div class="filter-row">
- <input type="date" id="pickDate" value="2026-08-21" style="height:36px;border:1px solid var(--line);border-radius:8px;padding:0 10px;font-size:13px;width:100%;background:#fff" />
- </div>
- <div class="hint" id="filterHint">当前:今天 2026-08-21 · 取货或送货命中</div>
- </div>
- <div id="recordList"></div>
- </div>
- </div>
- </div>
- </div>
- <!-- Static reference: footer always -->
- <div class="phone-wrap">
- <div class="phone-label">对照:任意状态底部都有「运输记录」</div>
- <div class="phone">
- <div class="nav">运输计划</div>
- <div class="scroll no-footer-pad">
- <div class="empty">
- <div class="icon">📦</div>
- <div class="title">运输计划已归档完成</div>
- <div>与现状一致,详情不展示</div>
- </div>
- </div>
- <div class="footer">
- <button class="btn btn-ghost" type="button">运输记录</button>
- </div>
- </div>
- </div>
- </div>
- <div class="note">
- <strong>Demo 说明(已确认部分):</strong><br />
- 1)「运输记录」按<strong>主车车牌号</strong>聚合历史与后续安排。<br />
- 2)任意状态底部仅保留「运输记录」;不再提供「返回列表」。<br />
- 3)筛选:单日日期框 +「今天/明天/后天」快捷;<strong>默认今天</strong>。<br />
- 4)命中:取货日或送货日<strong>任一等于</strong>所选日。
- </div>
- </div>
- <script>
- const records = [
- {
- id: "r1",
- planNo: "YS20260821001",
- pickupDate: "2026-08-21",
- deliveryDate: "2026-08-21",
- route: "江宁仓库 → 镇江厂区",
- status: "arranged",
- statusLabel: "进行中",
- tag: "ok",
- screen: "detail-active"
- },
- {
- id: "r2",
- planNo: "YS20260821002",
- pickupDate: "2026-08-21",
- deliveryDate: "2026-08-22",
- route: "仪征码头 → 南京港",
- status: "arranged",
- statusLabel: "进行中",
- tag: "ok",
- screen: "detail-active"
- },
- {
- id: "r3",
- planNo: "YS20260818008",
- pickupDate: "2026-08-18",
- deliveryDate: "2026-08-18",
- route: "徐州仓 → 宿迁站",
- status: "archived",
- statusLabel: "已归档",
- tag: "arch",
- screen: "detail-archived"
- },
- {
- id: "r4",
- planNo: "YS20260815003",
- pickupDate: "2026-08-15",
- deliveryDate: "2026-08-16",
- route: "盐城库 → 南通厂",
- status: "voided",
- statusLabel: "已作废",
- tag: "void",
- screen: "detail-voided"
- },
- {
- id: "r5",
- planNo: "YS20260822001",
- pickupDate: "2026-08-22",
- deliveryDate: "2026-08-22",
- route: "连云港 → 淮安仓",
- status: "arranged",
- statusLabel: "后续安排",
- tag: "warn",
- screen: "detail-active"
- }
- ];
- const todayBase = new Date(2026, 7, 21); // demo fixed "today": 2026-08-21
- function fmt(d) {
- const y = d.getFullYear();
- const m = String(d.getMonth() + 1).padStart(2, "0");
- const day = String(d.getDate()).padStart(2, "0");
- return `${y}-${m}-${day}`;
- }
- function addDays(base, n) {
- const d = new Date(base.getTime());
- d.setDate(d.getDate() + n);
- return d;
- }
- const today = fmt(todayBase);
- const phone = document.getElementById("phone");
- const listEl = document.getElementById("recordList");
- const pickDate = document.getElementById("pickDate");
- const filterHint = document.getElementById("filterHint");
- const quickBtns = document.querySelectorAll("[data-quick]");
- pickDate.value = today;
- function showScreen(name) {
- phone.querySelectorAll(".screen").forEach((el) => {
- el.classList.toggle("active", el.dataset.screen === name);
- });
- document.querySelectorAll("#jumpTabs button").forEach((btn) => {
- const map = {
- A: "detail-active",
- B: "detail-archived",
- C: "list",
- D: "detail-voided"
- };
- btn.classList.toggle("active", map[btn.dataset.jump] === name);
- });
- }
- function matchesDate(r, day) {
- return r.pickupDate === day || r.deliveryDate === day;
- }
- function syncQuickActive(day) {
- quickBtns.forEach((btn) => {
- const offset = Number(btn.dataset.quick);
- const target = fmt(addDays(todayBase, offset));
- btn.classList.toggle("on", target === day);
- });
- }
- function labelFor(day) {
- if (day === fmt(addDays(todayBase, 0))) return `今天 ${day}`;
- if (day === fmt(addDays(todayBase, 1))) return `明天 ${day}`;
- if (day === fmt(addDays(todayBase, 2))) return `后天 ${day}`;
- return day;
- }
- function renderList() {
- const day = (pickDate.value || today).trim();
- syncQuickActive(day);
- const filtered = records.filter((r) => matchesDate(r, day));
- filterHint.textContent = `当前:${labelFor(day)} · 取货或送货命中 · 共 ${filtered.length} 条`;
- if (!filtered.length) {
- listEl.innerHTML = `<div class="empty"><div class="icon">🗂️</div><div class="title">暂无运输记录</div><div>可点「明天/后天」或另选日期</div></div>`;
- return;
- }
- listEl.innerHTML = filtered
- .map(
- (r) => `
- <div class="record" data-screen="${r.screen}" role="button" tabindex="0">
- <div class="record-top">
- <div class="record-no">${r.planNo}</div>
- <span class="tag ${r.tag}">${r.statusLabel}</span>
- </div>
- <div class="record-meta">
- <div>取货:<strong>${r.pickupDate}</strong> · 送货:<strong>${r.deliveryDate}</strong></div>
- <div>线路:${r.route}</div>
- </div>
- </div>`
- )
- .join("");
- listEl.querySelectorAll(".record").forEach((el) => {
- el.addEventListener("click", () => showScreen(el.dataset.screen));
- });
- }
- phone.addEventListener("click", (e) => {
- const btn = e.target.closest("[data-go]");
- if (!btn) return;
- showScreen(btn.dataset.go);
- if (btn.dataset.go === "list") renderList();
- });
- quickBtns.forEach((btn) => {
- btn.addEventListener("click", () => {
- pickDate.value = fmt(addDays(todayBase, Number(btn.dataset.quick)));
- renderList();
- });
- });
- pickDate.addEventListener("change", renderList);
- document.getElementById("jumpTabs").addEventListener("click", (e) => {
- const btn = e.target.closest("button[data-jump]");
- if (!btn) return;
- const map = {
- A: "detail-active",
- B: "detail-archived",
- C: "list",
- D: "detail-voided"
- };
- showScreen(map[btn.dataset.jump]);
- if (btn.dataset.jump === "C") renderList();
- });
- renderList();
- </script>
- </body>
- </html>
|