fresh-mint-pl-plan-contract-qty-check-demo.html 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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" />
  6. <title>Fresh Mint · 运输计划安排 · 合同数量校验 / 框架提醒 Demo</title>
  7. <link rel="preconnect" href="https://fonts.googleapis.com" />
  8. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  9. <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
  10. <style>
  11. :root {
  12. --primary: #10b981;
  13. --primary-dark: #059669;
  14. --primary-bg: #ecfdf5;
  15. --text: #1e293b;
  16. --text-muted: #64748b;
  17. --border: #d9e8e2;
  18. --surface: #ffffff;
  19. --bg-page: #f1f5f9;
  20. --shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  21. --shadow-lg: 0 12px 40px rgba(15, 23, 42, 0.14);
  22. --radius: 10px;
  23. --warn: #d97706;
  24. --warn-bg: #fffbeb;
  25. --warn-border: #fde68a;
  26. --danger: #dc2626;
  27. --danger-bg: #fef2f2;
  28. --danger-border: #fecaca;
  29. --info: #0284c7;
  30. --info-bg: #f0f9ff;
  31. --info-border: #bae6fd;
  32. --qty-plan: #059669;
  33. }
  34. * { box-sizing: border-box; }
  35. body {
  36. margin: 0;
  37. min-height: 100vh;
  38. font-family: Inter, -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
  39. font-size: 14px;
  40. line-height: 1.5;
  41. color: var(--text);
  42. background: var(--bg-page);
  43. }
  44. .demo-page { max-width: 1180px; margin: 0 auto; padding: 28px 24px 80px; }
  45. .demo-head { margin-bottom: 18px; }
  46. .demo-head h1 { margin: 0 0 8px; font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
  47. .demo-head h1 span { color: var(--primary); }
  48. .demo-head p { margin: 0; color: var(--text-muted); max-width: 900px; }
  49. .meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
  50. .pill {
  51. display: inline-flex; align-items: center; gap: 6px;
  52. padding: 4px 10px; border-radius: 999px; font-size: 12px; font-weight: 500;
  53. background: var(--primary-bg); color: var(--primary-dark); border: 1px solid #a7f3d0;
  54. }
  55. .pill.muted { background: #f1f5f9; color: var(--text-muted); border-color: #e2e8f0; }
  56. .pill.warn { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-border); }
  57. .pill.danger { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-border); }
  58. .brief {
  59. display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px;
  60. }
  61. @media (max-width: 900px) { .brief { grid-template-columns: 1fr; } }
  62. .card {
  63. background: var(--surface); border: 1px solid var(--border);
  64. border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow);
  65. }
  66. .card h2 {
  67. margin: 0 0 8px; font-size: 14px; font-weight: 600;
  68. padding-left: 10px; border-left: 3px solid var(--primary);
  69. color: var(--primary-dark);
  70. }
  71. .card.danger-card h2 { border-left-color: var(--danger); color: var(--danger); }
  72. .card.warn-card h2 { border-left-color: var(--warn); color: var(--warn); }
  73. .card ul { margin: 0; padding-left: 18px; color: var(--text-muted); font-size: 13px; }
  74. .card li { margin: 4px 0; }
  75. .card li strong { color: var(--text); }
  76. .formula {
  77. margin-top: 8px; padding: 8px 10px; border-radius: 8px;
  78. background: var(--primary-bg); color: var(--primary-dark);
  79. font-size: 12px; font-weight: 500;
  80. }
  81. .formula.warn { background: var(--warn-bg); color: #92400e; }
  82. .formula.danger { background: var(--danger-bg); color: #991b1b; }
  83. .scenario-tabs {
  84. display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap;
  85. }
  86. .scenario-tab {
  87. appearance: none; border: 1px solid var(--border); background: #fff;
  88. height: 36px; padding: 0 14px; border-radius: 8px; cursor: pointer;
  89. font-size: 13px; font-weight: 500; color: var(--text);
  90. }
  91. .scenario-tab:hover { border-color: #a7f3d0; color: var(--primary-dark); }
  92. .scenario-tab.active {
  93. background: var(--primary-bg); border-color: #a7f3d0; color: var(--primary-dark);
  94. }
  95. .panel {
  96. background: var(--surface); border: 1px solid var(--border);
  97. border-radius: 12px; box-shadow: var(--shadow-lg); overflow: hidden;
  98. }
  99. .panel-top {
  100. display: flex; align-items: center; justify-content: space-between;
  101. gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--border); flex-wrap: wrap;
  102. }
  103. .panel-top h3 {
  104. margin: 0; font-size: 16px; font-weight: 600;
  105. display: inline-flex; align-items: center; gap: 8px;
  106. }
  107. .panel-top h3::before {
  108. content: ""; width: 3px; height: 16px; border-radius: 2px; background: var(--primary);
  109. }
  110. .toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
  111. .btn {
  112. appearance: none; display: inline-flex; align-items: center; gap: 6px;
  113. height: 32px; padding: 0 12px; border-radius: 8px; font-size: 13px; font-weight: 500;
  114. border: 1px solid var(--border); background: #fff; color: var(--text); cursor: pointer;
  115. }
  116. .btn:hover { border-color: #a7f3d0; color: var(--primary-dark); }
  117. .btn.primary { background: var(--primary); border-color: var(--primary); color: #fff; }
  118. .btn.primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); color: #fff; }
  119. .btn.ghost { background: transparent; }
  120. .btn.warn { background: var(--warn); border-color: var(--warn); color: #fff; }
  121. .btn.warn:hover { filter: brightness(0.95); color: #fff; }
  122. .btn.danger { background: var(--danger); border-color: var(--danger); color: #fff; }
  123. .btn.danger:hover { filter: brightness(0.95); color: #fff; }
  124. .kv-row {
  125. display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
  126. padding: 14px 18px; border-bottom: 1px solid #eef2f7;
  127. }
  128. @media (max-width: 900px) { .kv-row { grid-template-columns: 1fr 1fr; } }
  129. .kv-item .label { font-size: 11px; color: var(--text-muted); margin-bottom: 2px; }
  130. .kv-item .value { font-size: 13px; font-weight: 500; }
  131. .section {
  132. padding: 14px 18px 18px;
  133. }
  134. .section-title {
  135. margin: 0 0 10px; font-size: 13px; font-weight: 600; color: var(--primary-dark);
  136. }
  137. table {
  138. width: 100%; border-collapse: collapse; font-size: 13px;
  139. }
  140. th, td {
  141. border: 1px solid var(--border); padding: 8px 10px; text-align: left;
  142. vertical-align: middle;
  143. }
  144. th {
  145. background: #f8fafc; font-weight: 600; color: #334155; font-size: 12px;
  146. }
  147. th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; }
  148. th.plan-qty, td.plan-qty { color: var(--qty-plan); }
  149. th.plan-qty { background: #ecfdf5; }
  150. td.over { color: var(--danger); font-weight: 600; background: #fef2f2; }
  151. .tag {
  152. display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 4px;
  153. font-size: 12px; font-weight: 500; color: #fff; background: var(--primary-dark);
  154. }
  155. .tag.warn { background: var(--warn); }
  156. .tag.info { background: var(--info); }
  157. .tag.muted { background: transparent; color: var(--text); border: 1px solid #cbd5e1; }
  158. .hint-inline {
  159. margin-top: 10px; padding: 10px 12px; border-radius: 8px;
  160. font-size: 13px; display: none;
  161. }
  162. .hint-inline.show { display: block; }
  163. .hint-inline.danger {
  164. background: var(--danger-bg); border: 1px solid var(--danger-border); color: #991b1b;
  165. }
  166. .hint-inline.warn {
  167. background: var(--warn-bg); border: 1px solid var(--warn-border); color: #92400e;
  168. }
  169. .hint-inline.info {
  170. background: var(--info-bg); border: 1px solid var(--info-border); color: #075985;
  171. }
  172. .hint-inline strong { font-weight: 600; }
  173. /* Modal */
  174. .mask {
  175. position: fixed; inset: 0; background: rgba(15, 23, 42, 0.45);
  176. display: none; align-items: center; justify-content: center;
  177. padding: 24px; z-index: 50;
  178. /* 无背景模糊,对齐规范 */
  179. backdrop-filter: none;
  180. }
  181. .mask.show { display: flex; }
  182. .modal {
  183. width: min(780px, 100%);
  184. background: #fff; border-radius: 12px;
  185. border: 1px solid var(--border); box-shadow: var(--shadow-lg);
  186. overflow: hidden;
  187. }
  188. .modal-header {
  189. display: flex; align-items: center; justify-content: space-between;
  190. padding: 14px 18px; border-bottom: 1px solid var(--border);
  191. }
  192. .modal-header h4 { margin: 0; font-size: 15px; font-weight: 600; }
  193. .modal-body { padding: 16px 18px; }
  194. .modal-body p { margin: 0 0 10px; color: var(--text); font-size: 13px; }
  195. .modal-body .calc {
  196. margin: 12px 0; padding: 10px 12px; border-radius: 8px;
  197. background: #f8fafc; border: 1px solid #e2e8f0; font-size: 13px;
  198. }
  199. .modal-body .calc .row {
  200. display: flex; justify-content: space-between; gap: 12px; padding: 3px 0;
  201. }
  202. .modal-body .calc .row.total {
  203. margin-top: 6px; padding-top: 8px; border-top: 1px dashed #cbd5e1;
  204. font-weight: 600;
  205. }
  206. .modal-body .calc .row.over .v { color: var(--danger); }
  207. .modal-footer {
  208. display: flex; justify-content: flex-end; gap: 8px;
  209. padding: 12px 18px; border-top: 1px solid var(--border);
  210. }
  211. .choice-note {
  212. margin-top: 16px; padding: 12px 14px; border-radius: 10px;
  213. background: #fff; border: 1px dashed #a7f3d0; color: var(--text-muted); font-size: 13px;
  214. }
  215. .choice-note strong { color: var(--primary-dark); }
  216. .hidden { display: none !important; }
  217. </style>
  218. </head>
  219. <body>
  220. <div class="demo-page">
  221. <header class="demo-head">
  222. <h1>运输计划安排 · <span>合同数量校验</span> &amp; 框架合同提醒</h1>
  223. <p>
  224. 预览「安排运输」时的两类提示:① 有数量合同 — 已安排 + 本次是否超合同总量;
  225. ② 框架采购/销售合同(无固定数量)— 每次提醒累计已安排数量。数量口径对齐合同详情三列:
  226. <code>已安排合计 = 已归档 + 未归档已安排</code>(按停靠段 <code>plan_qty</code> / product_id)。
  227. </p>
  228. <div class="meta">
  229. <span class="pill">路径 · /#/pl/plan-module/plan</span>
  230. <span class="pill muted">触发点 · 安排运输 / 保存并安排</span>
  231. <span class="pill danger">场景 A · 超量硬拦截</span>
  232. <span class="pill warn">场景 B · 框架累计提醒</span>
  233. <span class="pill">场景 C · 未超限提醒剩余</span>
  234. </div>
  235. </header>
  236. <div class="brief">
  237. <div class="card danger-card">
  238. <h2>A. 有数量合同(销售/采购等)</h2>
  239. <ul>
  240. <li>合同产品行存在<strong>有效数量</strong>时参与校验</li>
  241. <li>校验式:<strong>已安排合计 + 本次 planQty ≤ 合同数量</strong></li>
  242. <li>超限:安排<strong>前提交拦截</strong>,弹窗说明差额,必须改数量</li>
  243. <li>未超限:安排<strong>成功之后</strong>弹窗提醒<strong>剩余可安排量</strong></li>
  244. <li>汇总口径:有绑合同的段都校验;已安排合计按该合同挂接停靠段(同合同详情三列)</li>
  245. </ul>
  246. <div class="formula danger">超限 → 事前硬拦;未超限 → 成功后提醒剩余</div>
  247. </div>
  248. <div class="card warn-card">
  249. <h2>B. 框架合同(无数量)</h2>
  250. <ul>
  251. <li>类型:<strong>框架销售 / 框架采购</strong>(含历史 framework)</li>
  252. <li>产品数量为「按客户需求」或空 → <strong>不拦数量</strong></li>
  253. <li>安排<strong>成功之后</strong>提醒:本次 + 累计已安排(无「剩余」)</li>
  254. </ul>
  255. <div class="formula warn">成功后提醒,不阻断安排</div>
  256. </div>
  257. </div>
  258. <div class="scenario-tabs" role="tablist">
  259. <button type="button" class="scenario-tab active" data-scenario="fixed" role="tab">场景 A · 有数量合同超量</button>
  260. <button type="button" class="scenario-tab" data-scenario="framework" role="tab">场景 B · 框架合同提醒</button>
  261. <button type="button" class="scenario-tab" data-scenario="ok" role="tab">场景 C · 未超限 · 提醒剩余量</button>
  262. </div>
  263. <!-- 场景 A -->
  264. <section id="panel-fixed" class="panel" data-panel="fixed">
  265. <div class="panel-top">
  266. <h3>安排运输 · 绑定有数量合同</h3>
  267. <div class="toolbar">
  268. <span class="tag">销售合同</span>
  269. <button type="button" class="btn primary" id="btn-arrange-fixed">▶ 安排运输</button>
  270. </div>
  271. </div>
  272. <div class="kv-row">
  273. <div class="kv-item"><div class="label">计划编号</div><div class="value">YP20260830012</div></div>
  274. <div class="kv-item"><div class="label">装货合同</div><div class="value">HT-2026-0318 · 硫酸销售合同</div></div>
  275. <div class="kv-item"><div class="label">合同数量口径</div><div class="value">产品行 quantity</div></div>
  276. <div class="kv-item"><div class="label">策略</div><div class="value">超量硬拦截</div></div>
  277. </div>
  278. <div class="section">
  279. <div class="section-title">本次装货产品 vs 合同用量</div>
  280. <table>
  281. <thead>
  282. <tr>
  283. <th>产品</th>
  284. <th class="num">合同数量</th>
  285. <th class="num plan-qty">已安排合计</th>
  286. <th class="num">本次安排</th>
  287. <th class="num">安排后合计</th>
  288. <th class="num">剩余可安排</th>
  289. <th>结果</th>
  290. </tr>
  291. </thead>
  292. <tbody>
  293. <tr>
  294. <td>98% 硫酸</td>
  295. <td class="num">100.000</td>
  296. <td class="num plan-qty">80.000</td>
  297. <td class="num">30.000</td>
  298. <td class="num over">110.000</td>
  299. <td class="num over">-10.000</td>
  300. <td><span class="tag" style="background:#dc2626">超量</span></td>
  301. </tr>
  302. <tr>
  303. <td>液碱 32%</td>
  304. <td class="num">50.000</td>
  305. <td class="num plan-qty">20.000</td>
  306. <td class="num">10.000</td>
  307. <td class="num">30.000</td>
  308. <td class="num">20.000</td>
  309. <td><span class="tag">正常</span></td>
  310. </tr>
  311. </tbody>
  312. </table>
  313. <div class="hint-inline danger show" id="inline-fixed">
  314. <strong>校验提示:</strong>产品「98% 硫酸」安排后合计 110 超过合同数量 100(超出 10)。请调减本次数量或更换合同后再安排。
  315. </div>
  316. </div>
  317. </section>
  318. <!-- 场景 B -->
  319. <section id="panel-framework" class="panel hidden" data-panel="framework">
  320. <div class="panel-top">
  321. <h3>安排运输 · 框架销售合同</h3>
  322. <div class="toolbar">
  323. <span class="tag warn">框架销售合同</span>
  324. <button type="button" class="btn primary" id="btn-arrange-fw">▶ 安排运输</button>
  325. </div>
  326. </div>
  327. <div class="kv-row">
  328. <div class="kv-item"><div class="label">计划编号</div><div class="value">YP20260830018</div></div>
  329. <div class="kv-item"><div class="label">送货合同</div><div class="value">HT-KJ-2026-008 · 年度框架销售</div></div>
  330. <div class="kv-item"><div class="label">合同数量</div><div class="value">按客户需求(无固定量)</div></div>
  331. <div class="kv-item"><div class="label">校验策略</div><div class="value">仅累计提醒,不拦截</div></div>
  332. </div>
  333. <div class="section">
  334. <div class="section-title">本次产品安排(框架无总量上限)</div>
  335. <table>
  336. <thead>
  337. <tr>
  338. <th>产品</th>
  339. <th class="num">合同数量</th>
  340. <th class="num plan-qty">已安排合计</th>
  341. <th class="num">本次安排</th>
  342. <th class="num plan-qty">安排后已安排</th>
  343. <th>结果</th>
  344. </tr>
  345. </thead>
  346. <tbody>
  347. <tr>
  348. <td>工业盐酸</td>
  349. <td class="num" style="color:var(--text-muted)">按客户需求</td>
  350. <td class="num plan-qty">126.500</td>
  351. <td class="num">28.000</td>
  352. <td class="num plan-qty">154.500</td>
  353. <td><span class="tag warn">提醒</span></td>
  354. </tr>
  355. <tr>
  356. <td>片碱</td>
  357. <td class="num" style="color:var(--text-muted)">按客户需求</td>
  358. <td class="num plan-qty">40.000</td>
  359. <td class="num">12.000</td>
  360. <td class="num plan-qty">52.000</td>
  361. <td><span class="tag warn">提醒</span></td>
  362. </tr>
  363. </tbody>
  364. </table>
  365. <div class="hint-inline warn show">
  366. <strong>框架合同提醒(安排成功后):</strong>无固定数量上限。工业盐酸累计已安排 <strong>154.500</strong>,片碱累计已安排 <strong>52.000</strong>。
  367. </div>
  368. </div>
  369. </section>
  370. <!-- 场景 C:未超限,提醒剩余 -->
  371. <section id="panel-ok" class="panel hidden" data-panel="ok">
  372. <div class="panel-top">
  373. <h3>安排运输 · 有数量且未超限</h3>
  374. <div class="toolbar">
  375. <span class="tag">采购合同</span>
  376. <button type="button" class="btn primary" id="btn-arrange-ok">▶ 安排运输</button>
  377. </div>
  378. </div>
  379. <div class="kv-row">
  380. <div class="kv-item"><div class="label">计划编号</div><div class="value">YP20260830021</div></div>
  381. <div class="kv-item"><div class="label">装货合同</div><div class="value">HT-CG-2026-102 · 液碱采购</div></div>
  382. <div class="kv-item"><div class="label">合同数量</div><div class="value">200.000</div></div>
  383. <div class="kv-item"><div class="label">校验结果</div><div class="value">通过 · 成功后提醒剩余</div></div>
  384. </div>
  385. <div class="section">
  386. <div class="section-title">本次产品安排</div>
  387. <table>
  388. <thead>
  389. <tr>
  390. <th>产品</th>
  391. <th class="num">合同数量</th>
  392. <th class="num plan-qty">已安排合计</th>
  393. <th class="num">本次安排</th>
  394. <th class="num">安排后合计</th>
  395. <th class="num">安排后剩余</th>
  396. <th>结果</th>
  397. </tr>
  398. </thead>
  399. <tbody>
  400. <tr>
  401. <td>液碱 32%</td>
  402. <td class="num">200.000</td>
  403. <td class="num plan-qty">150.000</td>
  404. <td class="num">40.000</td>
  405. <td class="num">190.000</td>
  406. <td class="num plan-qty">10.000</td>
  407. <td><span class="tag">成功后提醒</span></td>
  408. </tr>
  409. </tbody>
  410. </table>
  411. <div class="hint-inline info show">
  412. <strong>未超限:</strong>先完成安排;成功后再弹窗告知「剩余可安排量」(知悉即可,非二次确认)。
  413. </div>
  414. </div>
  415. </section>
  416. <p class="choice-note">
  417. <strong>已确认:</strong>超量事前硬拦;未超限 / 框架 → 安排成功后弹窗提醒;校验范围 = 有绑合同的段,已安排合计口径对齐合同详情三列。点击「安排运输」预览弹窗。
  418. </p>
  419. </div>
  420. <!-- 超量弹窗:硬拦 · 按合同分组表格 -->
  421. <div class="mask" id="mask-over-hard" role="dialog" aria-modal="true">
  422. <div class="modal">
  423. <div class="modal-header">
  424. <h4 style="color:var(--danger)">无法安排运输</h4>
  425. <button type="button" class="btn ghost" data-close>×</button>
  426. </div>
  427. <div class="modal-body">
  428. <p style="margin:0 0 12px;color:var(--text-muted);font-size:13px;">以下产品「已安排 + 本次」超过合同数量,请调整后再安排:</p>
  429. <div style="margin:0 0 14px;">
  430. <div style="font-size:14px;font-weight:600;margin:0 0 8px;padding-left:8px;border-left:3px solid var(--primary);">安徽某某化工 - HT-2026-0318(装货)</div>
  431. <table>
  432. <thead>
  433. <tr>
  434. <th>产品名称</th>
  435. <th>规格型号</th>
  436. <th class="num">合同数量</th>
  437. <th>单位</th>
  438. <th class="num">已安排</th>
  439. <th class="num">剩余</th>
  440. </tr>
  441. </thead>
  442. <tbody>
  443. <tr>
  444. <td>98% 硫酸</td>
  445. <td>工业级</td>
  446. <td class="num">100.000</td>
  447. <td>吨</td>
  448. <td class="num">110.000</td>
  449. <td class="num over">-10.000</td>
  450. </tr>
  451. <tr>
  452. <td>液碱 32%</td>
  453. <td>—</td>
  454. <td class="num">50.000</td>
  455. <td>吨</td>
  456. <td class="num">30.000</td>
  457. <td class="num plan-qty">20.000</td>
  458. </tr>
  459. </tbody>
  460. </table>
  461. </div>
  462. </div>
  463. <div class="modal-footer">
  464. <button type="button" class="btn primary" data-close>✓ 知道了</button>
  465. </div>
  466. </div>
  467. </div>
  468. <!-- 框架提醒 · 按合同分组表格 -->
  469. <div class="mask" id="mask-framework" role="dialog" aria-modal="true">
  470. <div class="modal">
  471. <div class="modal-header">
  472. <h4 style="color:var(--warn)">安排成功 · 框架合同累计</h4>
  473. <button type="button" class="btn ghost" data-close>×</button>
  474. </div>
  475. <div class="modal-body">
  476. <p style="margin:0 0 12px;color:var(--text-muted);font-size:13px;">运输安排已成功。框架合同无固定数量上限,累计已安排如下:</p>
  477. <div style="margin:0 0 14px;">
  478. <div style="font-size:14px;font-weight:600;margin:0 0 8px;padding-left:8px;border-left:3px solid var(--primary);">某某贸易公司 - HT-KJ-2026-008(送货)</div>
  479. <table>
  480. <thead>
  481. <tr>
  482. <th>产品名称</th>
  483. <th>规格型号</th>
  484. <th class="num">合同数量</th>
  485. <th>单位</th>
  486. <th class="num">已安排</th>
  487. <th class="num">剩余</th>
  488. </tr>
  489. </thead>
  490. <tbody>
  491. <tr>
  492. <td>工业盐酸</td>
  493. <td>31%</td>
  494. <td class="num" style="color:var(--text-muted)">按客户需求</td>
  495. <td>吨</td>
  496. <td class="num plan-qty">154.500</td>
  497. <td class="num">—</td>
  498. </tr>
  499. <tr>
  500. <td>片碱</td>
  501. <td>—</td>
  502. <td class="num" style="color:var(--text-muted)">按客户需求</td>
  503. <td>吨</td>
  504. <td class="num plan-qty">52.000</td>
  505. <td class="num">—</td>
  506. </tr>
  507. </tbody>
  508. </table>
  509. </div>
  510. </div>
  511. <div class="modal-footer">
  512. <button type="button" class="btn primary" data-close>✓ 知道了</button>
  513. </div>
  514. </div>
  515. </div>
  516. <!-- 未超限:双侧合同示意 -->
  517. <div class="mask" id="mask-ok" role="dialog" aria-modal="true">
  518. <div class="modal">
  519. <div class="modal-header">
  520. <h4 style="color:var(--primary-dark)">安排成功 · 合同剩余数量</h4>
  521. <button type="button" class="btn ghost" data-close>×</button>
  522. </div>
  523. <div class="modal-body">
  524. <p style="margin:0 0 12px;color:var(--text-muted);font-size:13px;">运输安排已成功。各产品剩余可安排量如下:</p>
  525. <div style="margin:0 0 14px;">
  526. <div style="font-size:14px;font-weight:600;margin:0 0 8px;padding-left:8px;border-left:3px solid var(--primary);">云栋供应商 - HT-CG-2026-102(装货)</div>
  527. <table>
  528. <thead>
  529. <tr>
  530. <th>产品名称</th>
  531. <th>规格型号</th>
  532. <th class="num">合同数量</th>
  533. <th>单位</th>
  534. <th class="num">已安排</th>
  535. <th class="num">剩余</th>
  536. </tr>
  537. </thead>
  538. <tbody>
  539. <tr>
  540. <td>液碱 32%</td>
  541. <td>—</td>
  542. <td class="num">200.000</td>
  543. <td>吨</td>
  544. <td class="num">190.000</td>
  545. <td class="num plan-qty">10.000</td>
  546. </tr>
  547. </tbody>
  548. </table>
  549. </div>
  550. <div style="margin:0 0 4px;">
  551. <div style="font-size:14px;font-weight:600;margin:0 0 8px;padding-left:8px;border-left:3px solid var(--primary);">某某贸易公司 - HT-XS-2026-088(送货)</div>
  552. <table>
  553. <thead>
  554. <tr>
  555. <th>产品名称</th>
  556. <th>规格型号</th>
  557. <th class="num">合同数量</th>
  558. <th>单位</th>
  559. <th class="num">已安排</th>
  560. <th class="num">剩余</th>
  561. </tr>
  562. </thead>
  563. <tbody>
  564. <tr>
  565. <td>液碱 32%</td>
  566. <td>—</td>
  567. <td class="num">180.000</td>
  568. <td>吨</td>
  569. <td class="num">40.000</td>
  570. <td class="num plan-qty">140.000</td>
  571. </tr>
  572. </tbody>
  573. </table>
  574. </div>
  575. </div>
  576. <div class="modal-footer">
  577. <button type="button" class="btn primary" data-close>✓ 知道了</button>
  578. </div>
  579. </div>
  580. </div>
  581. <script>
  582. (function () {
  583. function showPanel(name) {
  584. document.querySelectorAll('[data-panel]').forEach(function (el) {
  585. el.classList.toggle('hidden', el.getAttribute('data-panel') !== name)
  586. })
  587. document.querySelectorAll('.scenario-tab').forEach(function (tab) {
  588. tab.classList.toggle('active', tab.getAttribute('data-scenario') === name)
  589. })
  590. }
  591. document.querySelectorAll('.scenario-tab').forEach(function (tab) {
  592. tab.addEventListener('click', function () {
  593. showPanel(tab.getAttribute('data-scenario'))
  594. })
  595. })
  596. function openMask(id) {
  597. document.getElementById(id).classList.add('show')
  598. }
  599. function closeAll() {
  600. document.querySelectorAll('.mask').forEach(function (m) { m.classList.remove('show') })
  601. }
  602. document.querySelectorAll('[data-close]').forEach(function (btn) {
  603. btn.addEventListener('click', closeAll)
  604. })
  605. document.querySelectorAll('.mask').forEach(function (mask) {
  606. mask.addEventListener('click', function (e) {
  607. if (e.target === mask) closeAll()
  608. })
  609. })
  610. document.getElementById('btn-arrange-fixed').addEventListener('click', function () {
  611. openMask('mask-over-hard')
  612. })
  613. document.getElementById('btn-arrange-fw').addEventListener('click', function () {
  614. openMask('mask-framework')
  615. })
  616. document.getElementById('btn-arrange-ok').addEventListener('click', function () {
  617. openMask('mask-ok')
  618. })
  619. })()
  620. </script>
  621. </body>
  622. </html>