shanxia-mp-pl-plan-webview-demo.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  6. <title>山峡化工 · 运输计划(WebView H5 Demo)</title>
  7. <style>
  8. :root {
  9. --brand: #0a9f6d;
  10. --brand-dark: #087f57;
  11. --brand-soft: #e9f9f2;
  12. --barley: #d6a343;
  13. --barley-soft: #fff7e8;
  14. --bg: #f5f8f7;
  15. --surface: #ffffff;
  16. --text: #1f2a26;
  17. --muted: #6b7b74;
  18. --placeholder: #9aa8a1;
  19. --border: #dcebe4;
  20. --danger: #d14343;
  21. --warning: #cf8a19;
  22. --success: #189b65;
  23. --radius: 14px;
  24. --shadow: 0 6px 16px rgba(10, 159, 109, 0.08);
  25. --header: linear-gradient(145deg, #087f57 0%, #0a9f6d 72%, #d6a343 165%);
  26. }
  27. * { box-sizing: border-box; margin: 0; padding: 0; }
  28. body {
  29. background: #dfe8e3;
  30. color: var(--text);
  31. font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif;
  32. font-size: 14px;
  33. line-height: 1.45;
  34. }
  35. .stage {
  36. max-width: 980px;
  37. margin: 0 auto;
  38. padding: 20px 16px 40px;
  39. }
  40. .banner {
  41. background: #1f2a26;
  42. color: #fff;
  43. border-radius: 12px;
  44. padding: 14px 16px;
  45. margin-bottom: 16px;
  46. font-size: 13px;
  47. }
  48. .banner strong { color: #9fe3c4; }
  49. .banner code {
  50. background: rgba(255,255,255,.12);
  51. padding: 1px 6px;
  52. border-radius: 4px;
  53. font-size: 12px;
  54. }
  55. .grid {
  56. display: grid;
  57. grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  58. gap: 18px;
  59. align-items: start;
  60. }
  61. .phone {
  62. max-width: 390px;
  63. margin: 0 auto;
  64. background: var(--bg);
  65. border-radius: 28px;
  66. overflow: hidden;
  67. box-shadow: 0 18px 40px rgba(0,0,0,.14);
  68. border: 8px solid #24312c;
  69. min-height: 760px;
  70. position: relative;
  71. }
  72. .phone-label {
  73. text-align: center;
  74. font-size: 12px;
  75. color: #4d5c56;
  76. margin-bottom: 8px;
  77. font-weight: 600;
  78. }
  79. .status-bar {
  80. height: 28px;
  81. background: var(--brand-dark);
  82. color: rgba(255,255,255,.85);
  83. display: flex;
  84. align-items: center;
  85. justify-content: space-between;
  86. padding: 0 16px;
  87. font-size: 11px;
  88. }
  89. .header {
  90. background: var(--header);
  91. color: #fff;
  92. padding: 10px 14px 14px;
  93. }
  94. .header-row {
  95. display: flex;
  96. align-items: center;
  97. justify-content: space-between;
  98. gap: 8px;
  99. }
  100. .back { opacity: .92; font-size: 13px; }
  101. .header h1 { font-size: 20px; font-weight: 700; margin-top: 8px; }
  102. .header .hint {
  103. margin-top: 4px;
  104. font-size: 11px;
  105. opacity: .85;
  106. }
  107. .search {
  108. margin-top: 12px;
  109. background: rgba(255,255,255,.95);
  110. color: var(--placeholder);
  111. border-radius: 999px;
  112. padding: 10px 14px;
  113. display: flex;
  114. align-items: center;
  115. gap: 8px;
  116. font-size: 13px;
  117. }
  118. .search .dot {
  119. width: 8px; height: 8px; border-radius: 50%;
  120. background: var(--brand);
  121. flex-shrink: 0;
  122. }
  123. .date-row {
  124. display: flex;
  125. gap: 8px;
  126. overflow-x: auto;
  127. padding: 10px 12px 0;
  128. -webkit-overflow-scrolling: touch;
  129. }
  130. .chip {
  131. flex: 0 0 auto;
  132. padding: 6px 12px;
  133. border-radius: 999px;
  134. background: #fff;
  135. border: 1px solid var(--border);
  136. color: var(--muted);
  137. font-size: 12px;
  138. }
  139. .chip.active {
  140. background: var(--brand-soft);
  141. border-color: #b7ebce;
  142. color: var(--brand-dark);
  143. font-weight: 600;
  144. }
  145. .tabs {
  146. display: flex;
  147. gap: 6px;
  148. overflow-x: auto;
  149. padding: 10px 12px;
  150. -webkit-overflow-scrolling: touch;
  151. }
  152. .tab {
  153. flex: 0 0 auto;
  154. padding: 7px 12px;
  155. border-radius: 999px;
  156. background: #fff;
  157. border: 1px solid var(--border);
  158. color: var(--muted);
  159. font-size: 12px;
  160. cursor: pointer;
  161. }
  162. .tab.active {
  163. background: var(--brand);
  164. border-color: var(--brand);
  165. color: #fff;
  166. font-weight: 600;
  167. }
  168. .toolbar {
  169. display: flex;
  170. gap: 8px;
  171. padding: 0 12px 10px;
  172. overflow-x: auto;
  173. }
  174. .btn {
  175. flex: 0 0 auto;
  176. height: 34px;
  177. padding: 0 12px;
  178. border-radius: 8px;
  179. border: 1px solid var(--border);
  180. background: #fff;
  181. color: var(--text);
  182. font-size: 12px;
  183. cursor: pointer;
  184. display: inline-flex;
  185. align-items: center;
  186. gap: 4px;
  187. }
  188. .btn-primary {
  189. background: var(--brand);
  190. border-color: var(--brand);
  191. color: #fff;
  192. font-weight: 600;
  193. }
  194. .btn-danger {
  195. color: var(--danger);
  196. border-color: #f0c2c2;
  197. background: #fff7f7;
  198. }
  199. .list {
  200. padding: 0 12px 88px;
  201. display: flex;
  202. flex-direction: column;
  203. gap: 10px;
  204. }
  205. .card {
  206. background: var(--surface);
  207. border-radius: var(--radius);
  208. box-shadow: var(--shadow);
  209. padding: 12px 12px 10px;
  210. border: 1px solid rgba(220,235,228,.8);
  211. cursor: pointer;
  212. }
  213. .card:active { transform: scale(.995); }
  214. .card-head {
  215. display: flex;
  216. justify-content: space-between;
  217. gap: 8px;
  218. align-items: flex-start;
  219. }
  220. .plan-no {
  221. font-size: 15px;
  222. font-weight: 700;
  223. color: var(--brand-dark);
  224. }
  225. .sub {
  226. margin-top: 2px;
  227. font-size: 12px;
  228. color: var(--muted);
  229. }
  230. .tag {
  231. flex-shrink: 0;
  232. font-size: 11px;
  233. padding: 3px 8px;
  234. border-radius: 999px;
  235. font-weight: 600;
  236. color: #fff;
  237. }
  238. .tag-warn { background: #d97706; }
  239. .tag-ok { background: #059669; }
  240. .tag-mute { background: #64748b; }
  241. .tag-wait { background: #cf8a19; }
  242. .meta {
  243. margin-top: 8px;
  244. display: grid;
  245. gap: 4px;
  246. font-size: 12px;
  247. color: var(--muted);
  248. }
  249. .meta b { color: var(--text); font-weight: 500; }
  250. .products {
  251. margin-top: 8px;
  252. background: var(--brand-soft);
  253. border-radius: 10px;
  254. padding: 8px 10px;
  255. font-size: 12px;
  256. color: var(--brand-dark);
  257. }
  258. .card-foot {
  259. margin-top: 10px;
  260. display: flex;
  261. justify-content: flex-end;
  262. gap: 8px;
  263. flex-wrap: wrap;
  264. }
  265. .fab-bar {
  266. position: absolute;
  267. left: 0; right: 0; bottom: 0;
  268. padding: 10px 12px calc(12px + env(safe-area-inset-bottom, 0px));
  269. background: linear-gradient(180deg, transparent, var(--bg) 28%);
  270. display: flex;
  271. gap: 8px;
  272. }
  273. .fab-bar .btn { flex: 1; justify-content: center; height: 42px; font-size: 13px; }
  274. .fab-bar .btn-primary { flex: 1.35; }
  275. /* detail */
  276. .detail-body { padding: 12px; padding-bottom: 96px; }
  277. .section {
  278. background: #fff;
  279. border-radius: var(--radius);
  280. padding: 12px;
  281. margin-bottom: 10px;
  282. box-shadow: var(--shadow);
  283. }
  284. .section h3 {
  285. font-size: 13px;
  286. margin-bottom: 8px;
  287. color: var(--brand-dark);
  288. }
  289. .kv { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; font-size: 13px; border-bottom: 1px dashed var(--border); }
  290. .kv:last-child { border-bottom: none; }
  291. .kv span:first-child { color: var(--muted); flex-shrink: 0; }
  292. .kv span:last-child { text-align: right; }
  293. .actions-grid {
  294. display: grid;
  295. grid-template-columns: 1fr 1fr;
  296. gap: 8px;
  297. }
  298. .actions-grid .btn { height: 40px; justify-content: center; }
  299. .note {
  300. margin-top: 8px;
  301. font-size: 11px;
  302. color: var(--muted);
  303. background: var(--barley-soft);
  304. border-radius: 8px;
  305. padding: 8px 10px;
  306. }
  307. .hidden { display: none !important; }
  308. .choice-hint {
  309. margin-top: 18px;
  310. background: #fff;
  311. border-radius: 12px;
  312. padding: 14px 16px;
  313. border: 1px solid #cfe3d9;
  314. font-size: 13px;
  315. color: #355248;
  316. }
  317. .choice-hint ol { margin: 8px 0 0 18px; }
  318. </style>
  319. </head>
  320. <body>
  321. <div class="stage">
  322. <div class="banner">
  323. <strong>运输计划 · 小程序 WebView H5 Demo</strong><br />
  324. 对齐管理端 <code>/#/pl/plan</code> 核心能力;小程序侧仅壳页 + <code>web-view</code> 加载本 H5,业务改动发 H5 即可,无需反复提审。<br />
  325. 文件路径:<code>bak/mobile-ui/shanxia-mp-pl-plan-webview-demo.html</code>
  326. </div>
  327. <div class="grid">
  328. <!-- LIST -->
  329. <div>
  330. <div class="phone-label">① 列表页(默认)</div>
  331. <div class="phone" id="phone-list">
  332. <div class="status-bar"><span>09:41</span><span>WebView</span></div>
  333. <header class="header">
  334. <div class="header-row">
  335. <span class="back">← 工作台</span>
  336. <button class="btn btn-primary" type="button" style="height:30px">+ 新增</button>
  337. </div>
  338. <h1>运输计划</h1>
  339. <div class="hint">登录态由小程序注入 Token · H5 调同一套 /api</div>
  340. <div class="search"><span class="dot"></span>计划编号 / 供方 / 需方 / 产品 / 车牌号</div>
  341. </header>
  342. <div class="date-row">
  343. <button class="chip active" type="button">今天</button>
  344. <button class="chip" type="button">明天</button>
  345. <button class="chip" type="button">本周</button>
  346. <button class="chip" type="button">本月</button>
  347. <button class="chip" type="button">自定义</button>
  348. </div>
  349. <div class="tabs" id="list-tabs">
  350. <button class="tab active" type="button" data-tab="pending">待安排</button>
  351. <button class="tab" type="button" data-tab="arranged">已安排</button>
  352. <button class="tab" type="button" data-tab="draft">草稿中</button>
  353. <button class="tab" type="button" data-tab="all">全部</button>
  354. <button class="tab" type="button" data-tab="inbound">入库单</button>
  355. <button class="tab" type="button" data-tab="outbound">出库单</button>
  356. <button class="tab" type="button" data-tab="direct">直销单</button>
  357. </div>
  358. <div class="toolbar">
  359. <button class="btn" type="button">复制</button>
  360. <button class="btn" type="button">安排运输</button>
  361. <button class="btn" type="button">等通知</button>
  362. <button class="btn" type="button">打印</button>
  363. <button class="btn btn-danger" type="button">作废</button>
  364. </div>
  365. <div class="list" id="plan-list">
  366. <!-- cards injected -->
  367. </div>
  368. <div class="fab-bar">
  369. <button class="btn" type="button">三方人车</button>
  370. <button class="btn btn-primary" type="button" onclick="showDetail()">查看选中计划</button>
  371. </div>
  372. </div>
  373. </div>
  374. <!-- DETAIL -->
  375. <div>
  376. <div class="phone-label">② 详情 / 操作(点卡片进入)</div>
  377. <div class="phone" id="phone-detail">
  378. <div class="status-bar"><span>09:41</span><span>WebView</span></div>
  379. <header class="header">
  380. <div class="header-row">
  381. <span class="back" style="cursor:pointer" onclick="showList()">← 列表</span>
  382. <span class="tag tag-warn">待安排</span>
  383. </div>
  384. <h1>JH20260914003</h1>
  385. <div class="hint">出库单 · 计划日期 2026-09-14</div>
  386. </header>
  387. <div class="detail-body">
  388. <div class="section">
  389. <h3>基本信息</h3>
  390. <div class="kv"><span>跟进状态</span><span><b>待安排</b></span></div>
  391. <div class="kv"><span>送货时间</span><span>09-14 14:00</span></div>
  392. <div class="kv"><span>车牌号</span><span>皖H·D1234 / 挂 皖H·G88</span></div>
  393. <div class="kv"><span>人员</span><span>司机 张伟 · 押运 李强</span></div>
  394. <div class="kv"><span>跟进人</span><span>王调度</span></div>
  395. </div>
  396. <div class="section">
  397. <h3>产品摘要</h3>
  398. <div class="products" style="margin:0">
  399. 甲醇 32 吨 · 包装吨袋<br />
  400. 供方:安庆山峡 · 需方:合肥某化
  401. </div>
  402. <div class="note">移动端以卡片摘要为主;完整编辑走「编辑计划」全屏表单(同管理端字段,分步/分区展示)。</div>
  403. </div>
  404. <div class="section">
  405. <h3>快捷操作(对齐管理端工具栏)</h3>
  406. <div class="actions-grid">
  407. <button class="btn btn-primary" type="button">安排运输</button>
  408. <button class="btn" type="button">编辑计划</button>
  409. <button class="btn" type="button">复制计划</button>
  410. <button class="btn" type="button">等客户通知</button>
  411. <button class="btn" type="button">打印单据</button>
  412. <button class="btn btn-danger" type="button">作废</button>
  413. </div>
  414. </div>
  415. </div>
  416. <div class="fab-bar">
  417. <button class="btn" type="button" onclick="showList()">返回列表</button>
  418. <button class="btn btn-primary" type="button">安排运输</button>
  419. </div>
  420. </div>
  421. </div>
  422. </div>
  423. <div class="choice-hint">
  424. <strong>请确认 Demo 方向(回复编号即可)</strong>
  425. <ol>
  426. <li><b>A</b> — 列表用「卡片流」如左图(推荐,适合手机一屏信息)</li>
  427. <li><b>B</b> — 列表尽量仿桌面表格(横滑多列,信息更全但操作更重)</li>
  428. <li><b>C</b> — 先只做列表+查看+安排运输,其余打印/作废/三方人车二期再上</li>
  429. </ol>
  430. 也可直接说要改哪里(Tab、按钮优先级、字段展示等)。
  431. </div>
  432. </div>
  433. <script>
  434. const plans = [
  435. {
  436. no: 'JH20260914003',
  437. type: '出库单',
  438. status: '待安排',
  439. tag: 'tag-warn',
  440. date: '2026-09-14',
  441. plate: '未派车',
  442. people: '—',
  443. product: '甲醇 32 吨 · 安庆山峡 → 合肥某化',
  444. tab: 'pending'
  445. },
  446. {
  447. no: 'JH20260914001',
  448. type: '入库单',
  449. status: '已安排',
  450. tag: 'tag-ok',
  451. date: '2026-09-14',
  452. plate: '皖H·D1234',
  453. people: '张伟 / 李强',
  454. product: '乙醇 28 吨 · 张家港 → 安庆仓',
  455. tab: 'arranged'
  456. },
  457. {
  458. no: 'JH20260913018',
  459. type: '直销单',
  460. status: '等通知',
  461. tag: 'tag-wait',
  462. date: '2026-09-13',
  463. plate: '苏E·5M892',
  464. people: '陈师傅',
  465. product: '冰醋酸 20 吨 · 南通 → 芜湖',
  466. tab: 'pending'
  467. },
  468. {
  469. no: 'JH20260912007',
  470. type: '出库单',
  471. status: '草稿中',
  472. tag: 'tag-mute',
  473. date: '2026-09-12',
  474. plate: '—',
  475. people: '—',
  476. product: '待完善产品行',
  477. tab: 'draft'
  478. }
  479. ]
  480. const listEl = document.getElementById('plan-list')
  481. let activeTab = 'pending'
  482. function renderList() {
  483. const rows = activeTab === 'all'
  484. ? plans
  485. : plans.filter((p) => {
  486. if (activeTab === 'pending') return p.status === '待安排' || p.status === '等通知'
  487. if (activeTab === 'arranged') return p.status === '已安排'
  488. if (activeTab === 'draft') return p.status === '草稿中'
  489. if (activeTab === 'inbound') return p.type === '入库单'
  490. if (activeTab === 'outbound') return p.type === '出库单'
  491. if (activeTab === 'direct') return p.type === '直销单'
  492. return true
  493. })
  494. listEl.innerHTML = rows.map((p) => `
  495. <article class="card" onclick="showDetail()">
  496. <div class="card-head">
  497. <div>
  498. <div class="plan-no">${p.no}</div>
  499. <div class="sub">${p.type} · ${p.date}</div>
  500. </div>
  501. <span class="tag ${p.tag}">${p.status}</span>
  502. </div>
  503. <div class="meta">
  504. <div>车牌:<b>${p.plate}</b></div>
  505. <div>人员:<b>${p.people}</b></div>
  506. </div>
  507. <div class="products">${p.product}</div>
  508. <div class="card-foot">
  509. <button class="btn" type="button" onclick="event.stopPropagation()">复制</button>
  510. <button class="btn btn-primary" type="button" onclick="event.stopPropagation()">安排运输</button>
  511. </div>
  512. </article>
  513. `).join('') || '<div style="padding:24px;text-align:center;color:#6b7b74">该 Tab 暂无数据</div>'
  514. }
  515. document.getElementById('list-tabs').addEventListener('click', (e) => {
  516. const btn = e.target.closest('.tab')
  517. if (!btn) return
  518. document.querySelectorAll('#list-tabs .tab').forEach((t) => t.classList.remove('active'))
  519. btn.classList.add('active')
  520. activeTab = btn.dataset.tab
  521. renderList()
  522. })
  523. document.querySelectorAll('.date-row .chip').forEach((chip) => {
  524. chip.addEventListener('click', () => {
  525. document.querySelectorAll('.date-row .chip').forEach((c) => c.classList.remove('active'))
  526. chip.classList.add('active')
  527. })
  528. })
  529. function showDetail() {
  530. document.getElementById('phone-list').classList.add('hidden')
  531. document.getElementById('phone-detail').classList.remove('hidden')
  532. // on dual layout keep both; on narrow, scroll to detail
  533. document.getElementById('phone-detail').scrollIntoView({ behavior: 'smooth', block: 'start' })
  534. }
  535. function showList() {
  536. document.getElementById('phone-list').classList.remove('hidden')
  537. document.getElementById('phone-list').scrollIntoView({ behavior: 'smooth', block: 'start' })
  538. }
  539. renderList()
  540. </script>
  541. </body>
  542. </html>