2026-10-17-protocol-status-change-records-demo.html 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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" />
  6. <title>采集协议单 · 状态变更记录展示 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. --bg-page: #f8fafc;
  16. --panel: #ffffff;
  17. --text: #0f172a;
  18. --text-secondary: #334155;
  19. --text-muted: #64748b;
  20. --border: #e2e8f0;
  21. --radius: 10px;
  22. --radius-sm: 8px;
  23. --warning: #d97706;
  24. --warning-bg: #fffbeb;
  25. --danger: #dc2626;
  26. --danger-bg: #fef2f2;
  27. --info: #64748b;
  28. --info-bg: #f1f5f9;
  29. }
  30. * { box-sizing: border-box; }
  31. body {
  32. margin: 0;
  33. font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  34. font-size: 14px;
  35. line-height: 1.5;
  36. color: var(--text);
  37. background: var(--bg-page);
  38. }
  39. .page { max-width: 1100px; margin: 0 auto; padding: 24px 20px 80px; }
  40. .hero { margin-bottom: 18px; }
  41. .hero h1 {
  42. margin: 0 0 8px;
  43. font-size: 22px;
  44. font-weight: 700;
  45. letter-spacing: -0.02em;
  46. }
  47. .hero h1::before {
  48. content: "";
  49. display: inline-block;
  50. width: 4px;
  51. height: 1.1em;
  52. margin-right: 10px;
  53. vertical-align: -0.15em;
  54. border-radius: 2px;
  55. background: var(--primary);
  56. }
  57. .hero p { margin: 0 0 8px; color: var(--text-muted); max-width: 920px; }
  58. .context-bar { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
  59. .pill {
  60. display: inline-flex; align-items: center;
  61. padding: 4px 10px; border-radius: 999px; font-size: 12px;
  62. background: var(--primary-bg); color: var(--primary-dark); border: 1px solid #a7f3d0;
  63. }
  64. .pill.muted { background: #f1f5f9; color: var(--text-muted); border-color: var(--border); }
  65. .pill.warn { background: var(--warning-bg); color: var(--warning); border-color: #fcd34d; }
  66. .pill.danger { background: var(--danger-bg); color: var(--danger); border-color: #fecaca; }
  67. .note {
  68. margin: 0 0 20px;
  69. padding: 14px 16px;
  70. background: #fff;
  71. border: 1px solid var(--border);
  72. border-radius: var(--radius);
  73. font-size: 13px;
  74. color: var(--text-secondary);
  75. }
  76. .note strong { color: var(--text); }
  77. .note ul { margin: 8px 0 0; padding-left: 18px; }
  78. .note li { margin: 4px 0; }
  79. .tabs {
  80. display: flex;
  81. gap: 0;
  82. margin-bottom: 0;
  83. border-bottom: 1px solid var(--border);
  84. }
  85. .tab-btn {
  86. appearance: none;
  87. border: none;
  88. background: transparent;
  89. padding: 10px 18px;
  90. font: inherit;
  91. font-weight: 500;
  92. color: var(--text-muted);
  93. cursor: pointer;
  94. border-bottom: 2px solid transparent;
  95. margin-bottom: -1px;
  96. }
  97. .tab-btn:hover { color: var(--primary-dark); }
  98. .tab-btn.active {
  99. color: var(--primary-dark);
  100. border-bottom-color: var(--primary);
  101. font-weight: 600;
  102. }
  103. .panel {
  104. background: var(--panel);
  105. border: 1px solid var(--border);
  106. border-top: none;
  107. border-radius: 0 0 var(--radius) var(--radius);
  108. padding: 20px;
  109. }
  110. .section-title {
  111. margin: 0 0 14px;
  112. padding: 8px 12px;
  113. background: linear-gradient(90deg, var(--primary-bg), transparent);
  114. border-radius: var(--radius-sm);
  115. }
  116. .section-title h3 {
  117. margin: 0;
  118. font-size: 15px;
  119. font-weight: 600;
  120. color: var(--text);
  121. }
  122. /* ---- 现状:四列网格挤在一起 ---- */
  123. .data-grid {
  124. display: grid;
  125. grid-template-columns: repeat(4, minmax(0, 1fr));
  126. gap: 12px 16px;
  127. }
  128. .data-item {
  129. min-width: 0;
  130. padding: 10px 12px;
  131. background: #f8fafc;
  132. border: 1px solid var(--border);
  133. border-radius: var(--radius-sm);
  134. }
  135. .data-label {
  136. font-size: 12px;
  137. color: var(--text-muted);
  138. margin-bottom: 4px;
  139. }
  140. .data-value {
  141. font-size: 14px;
  142. color: var(--text);
  143. word-break: break-word;
  144. }
  145. .data-value--muted { color: var(--text-secondary); }
  146. .problem-callout {
  147. margin-top: 14px;
  148. padding: 10px 12px;
  149. background: var(--danger-bg);
  150. border: 1px solid #fecaca;
  151. border-radius: var(--radius-sm);
  152. color: #991b1b;
  153. font-size: 13px;
  154. }
  155. /* ---- 方案:表格单行历史 ---- */
  156. .table-wrap {
  157. border: 1px solid var(--border);
  158. border-radius: var(--radius-sm);
  159. overflow: auto;
  160. }
  161. table.list-table {
  162. width: 100%;
  163. border-collapse: collapse;
  164. font-size: 14px;
  165. }
  166. table.list-table thead th {
  167. background: #f8fafc;
  168. color: var(--text-secondary);
  169. font-weight: 600;
  170. font-size: 13px;
  171. padding: 10px 12px;
  172. border-bottom: 1px solid var(--border);
  173. white-space: nowrap;
  174. text-align: center;
  175. }
  176. table.list-table tbody td {
  177. padding: 10px 12px;
  178. border-bottom: 1px solid var(--border);
  179. text-align: center;
  180. vertical-align: middle;
  181. color: var(--text);
  182. }
  183. table.list-table tbody tr:last-child td { border-bottom: none; }
  184. table.list-table tbody tr:nth-child(even) { background: #fafafa; }
  185. table.list-table tbody tr:hover { background: var(--primary-bg); }
  186. table.list-table .col-desc {
  187. text-align: left;
  188. max-width: 280px;
  189. color: var(--text-secondary);
  190. }
  191. .tag {
  192. display: inline-flex;
  193. align-items: center;
  194. padding: 2px 8px;
  195. border-radius: 4px;
  196. font-size: 12px;
  197. font-weight: 500;
  198. line-height: 1.6;
  199. }
  200. .tag.success { background: var(--primary-bg); color: var(--primary-dark); }
  201. .tag.warning { background: var(--warning-bg); color: var(--warning); }
  202. .tag.danger { background: var(--danger-bg); color: var(--danger); }
  203. .tag.info { background: var(--info-bg); color: var(--info); }
  204. .tag.primary { background: #eff6ff; color: #2563eb; }
  205. .highlight-row td { box-shadow: inset 3px 0 0 var(--primary); }
  206. .caption {
  207. margin-top: 12px;
  208. font-size: 12px;
  209. color: var(--text-muted);
  210. }
  211. .caption em {
  212. font-style: normal;
  213. color: var(--primary-dark);
  214. font-weight: 600;
  215. }
  216. .compare {
  217. display: none;
  218. }
  219. .compare.active { display: block; }
  220. /* 方案 B:卡片行(备选) */
  221. .record-list { display: flex; flex-direction: column; gap: 10px; }
  222. .record-row {
  223. display: grid;
  224. grid-template-columns: 88px 1fr 160px 100px;
  225. gap: 12px;
  226. align-items: start;
  227. padding: 12px 14px;
  228. border: 1px solid var(--border);
  229. border-radius: var(--radius-sm);
  230. background: #fff;
  231. }
  232. .record-row:hover { border-color: #a7f3d0; background: var(--primary-bg); }
  233. .record-type { padding-top: 2px; }
  234. .record-main .time {
  235. font-weight: 600;
  236. color: var(--text);
  237. margin-bottom: 2px;
  238. }
  239. .record-main .desc { color: var(--text-secondary); font-size: 13px; }
  240. .record-meta {
  241. font-size: 12px;
  242. color: var(--text-muted);
  243. text-align: right;
  244. line-height: 1.6;
  245. }
  246. .record-meta .from-to { color: var(--text-secondary); }
  247. @media (max-width: 720px) {
  248. .data-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  249. .record-row { grid-template-columns: 1fr; }
  250. .record-meta { text-align: left; }
  251. }
  252. </style>
  253. </head>
  254. <body>
  255. <div class="page">
  256. <header class="hero">
  257. <h1>采集协议单 · 状态变更记录展示 Demo</h1>
  258. <p>
  259. 对照用户反馈:① 变更记录有时挤在一行;② 同种类型第二条覆盖第一条。
  260. 下方用同一份模拟数据展示「现状」与「建议方案」。
  261. </p>
  262. <div class="context-bar">
  263. <span class="pill">Fresh Mint</span>
  264. <span class="pill muted">对齐审核记录表格风格</span>
  265. <span class="pill warn">问题 1:网格挤行</span>
  266. <span class="pill danger">问题 2:主表字段覆盖写</span>
  267. </div>
  268. </header>
  269. <div class="note">
  270. <strong>模拟场景:</strong>协议经历多次状态流转(含两次「过期」类操作示意)。
  271. <ul>
  272. <li>现状:主表只存各类型最新时间/描述 → 第二次「过期」会盖掉第一次;四列网格把多类字段摊平,视觉上不像「一条记录一行」。</li>
  273. <li>建议:改成与「审核记录」一致的表格流水;每次变更追加一行;同类型可出现多条。</li>
  274. </ul>
  275. </div>
  276. <div class="tabs" role="tablist">
  277. <button type="button" class="tab-btn active" data-tab="current">现状(有问题)</button>
  278. <button type="button" class="tab-btn" data-tab="table">方案 A · 表格流水(推荐)</button>
  279. <button type="button" class="tab-btn" data-tab="card">方案 B · 卡片行</button>
  280. </div>
  281. <!-- 现状 -->
  282. <section class="panel compare active" id="panel-current">
  283. <div class="section-title"><h3>状态变更记录</h3></div>
  284. <div class="data-grid">
  285. <div class="data-item">
  286. <div class="data-label">协议生效时间</div>
  287. <div class="data-value">2026-03-01 10:20:00</div>
  288. </div>
  289. <div class="data-item">
  290. <div class="data-label">协议生效描述</div>
  291. <div class="data-value data-value--muted">审核通过后生效</div>
  292. </div>
  293. <div class="data-item">
  294. <div class="data-label">协议过期时间</div>
  295. <div class="data-value">2026-09-15 18:00:00</div>
  296. </div>
  297. <div class="data-item">
  298. <div class="data-label">协议过期描述</div>
  299. <div class="data-value data-value--muted">第二次标记过期(已覆盖第一次)</div>
  300. </div>
  301. <div class="data-item">
  302. <div class="data-label">作废类型</div>
  303. <div class="data-value">客户主动解约</div>
  304. </div>
  305. <div class="data-item">
  306. <div class="data-label">协议作废时间</div>
  307. <div class="data-value">2026-10-01 09:30:00</div>
  308. </div>
  309. <div class="data-item">
  310. <div class="data-label">协议作废描述</div>
  311. <div class="data-value data-value--muted">客户书面申请作废</div>
  312. </div>
  313. <div class="data-item">
  314. <div class="data-label">协议转科研时间</div>
  315. <div class="data-value">—</div>
  316. </div>
  317. </div>
  318. <div class="problem-callout">
  319. 问题示意:① 生效/过期/作废字段混在同一 4 列网格,作废 3 个字段更容易「挤成一行」;
  320. ② 第一次过期(假设 2026-06-01「合同到期提醒」)已被第二次过期描述覆盖,界面上看不到历史。
  321. </div>
  322. </section>
  323. <!-- 方案 A:表格 -->
  324. <section class="panel compare" id="panel-table">
  325. <div class="section-title"><h3>状态变更记录</h3></div>
  326. <div class="table-wrap">
  327. <table class="list-table">
  328. <thead>
  329. <tr>
  330. <th style="width:68px">序号</th>
  331. <th style="min-width:110px">变更类型</th>
  332. <th style="min-width:160px">变更时间</th>
  333. <th style="min-width:120px">状态流转</th>
  334. <th style="min-width:200px">变更说明</th>
  335. <th style="min-width:100px">操作人</th>
  336. </tr>
  337. </thead>
  338. <tbody>
  339. <tr>
  340. <td>1</td>
  341. <td><span class="tag success">生效</span></td>
  342. <td>2026-03-01 10:20:00</td>
  343. <td>待生效 → 生效中</td>
  344. <td class="col-desc">审核通过后生效</td>
  345. <td>张三</td>
  346. </tr>
  347. <tr class="highlight-row">
  348. <td>2</td>
  349. <td><span class="tag warning">过期</span></td>
  350. <td>2026-06-01 12:00:00</td>
  351. <td>生效中 → 已过期</td>
  352. <td class="col-desc">合同到期提醒(第一次,保留)</td>
  353. <td>李四</td>
  354. </tr>
  355. <tr>
  356. <td>3</td>
  357. <td><span class="tag success">生效</span></td>
  358. <td>2026-07-10 09:00:00</td>
  359. <td>已过期 → 生效中</td>
  360. <td class="col-desc">续签后重新生效</td>
  361. <td>张三</td>
  362. </tr>
  363. <tr class="highlight-row">
  364. <td>4</td>
  365. <td><span class="tag warning">过期</span></td>
  366. <td>2026-09-15 18:00:00</td>
  367. <td>生效中 → 已过期</td>
  368. <td class="col-desc">第二次标记过期(不再覆盖第一条)</td>
  369. <td>王五</td>
  370. </tr>
  371. <tr>
  372. <td>5</td>
  373. <td><span class="tag danger">作废</span></td>
  374. <td>2026-10-01 09:30:00</td>
  375. <td>已过期 → 已作废</td>
  376. <td class="col-desc">作废类型:客户主动解约;客户书面申请作废</td>
  377. <td>赵六</td>
  378. </tr>
  379. </tbody>
  380. </table>
  381. </div>
  382. <p class="caption">
  383. 高亮行为同类型「过期」的两条记录 —— 演示 <em>追加不覆盖</em>。
  384. 列结构对齐详情里「审核记录」:序号 / 类型 Tag / 时间 / 说明 / 操作人。
  385. </p>
  386. </section>
  387. <!-- 方案 B:卡片行 -->
  388. <section class="panel compare" id="panel-card">
  389. <div class="section-title"><h3>状态变更记录</h3></div>
  390. <div class="record-list">
  391. <div class="record-row">
  392. <div class="record-type"><span class="tag success">生效</span></div>
  393. <div class="record-main">
  394. <div class="time">2026-03-01 10:20:00</div>
  395. <div class="desc">审核通过后生效</div>
  396. </div>
  397. <div class="record-meta">
  398. <div class="from-to">待生效 → 生效中</div>
  399. <div>操作人:张三</div>
  400. </div>
  401. </div>
  402. <div class="record-row" style="border-color:#a7f3d0;background:var(--primary-bg)">
  403. <div class="record-type"><span class="tag warning">过期</span></div>
  404. <div class="record-main">
  405. <div class="time">2026-06-01 12:00:00</div>
  406. <div class="desc">合同到期提醒(第一次,保留)</div>
  407. </div>
  408. <div class="record-meta">
  409. <div class="from-to">生效中 → 已过期</div>
  410. <div>操作人:李四</div>
  411. </div>
  412. </div>
  413. <div class="record-row">
  414. <div class="record-type"><span class="tag success">生效</span></div>
  415. <div class="record-main">
  416. <div class="time">2026-07-10 09:00:00</div>
  417. <div class="desc">续签后重新生效</div>
  418. </div>
  419. <div class="record-meta">
  420. <div class="from-to">已过期 → 生效中</div>
  421. <div>操作人:张三</div>
  422. </div>
  423. </div>
  424. <div class="record-row" style="border-color:#a7f3d0;background:var(--primary-bg)">
  425. <div class="record-type"><span class="tag warning">过期</span></div>
  426. <div class="record-main">
  427. <div class="time">2026-09-15 18:00:00</div>
  428. <div class="desc">第二次标记过期(不再覆盖第一条)</div>
  429. </div>
  430. <div class="record-meta">
  431. <div class="from-to">生效中 → 已过期</div>
  432. <div>操作人:王五</div>
  433. </div>
  434. </div>
  435. <div class="record-row">
  436. <div class="record-type"><span class="tag danger">作废</span></div>
  437. <div class="record-main">
  438. <div class="time">2026-10-01 09:30:00</div>
  439. <div class="desc">作废类型:客户主动解约;客户书面申请作废</div>
  440. </div>
  441. <div class="record-meta">
  442. <div class="from-to">已过期 → 已作废</div>
  443. <div>操作人:赵六</div>
  444. </div>
  445. </div>
  446. </div>
  447. <p class="caption">卡片行更偏时间线阅读;若要与审核记录视觉统一,优先方案 A。</p>
  448. </section>
  449. </div>
  450. <script>
  451. const buttons = document.querySelectorAll(".tab-btn");
  452. const panels = {
  453. current: document.getElementById("panel-current"),
  454. table: document.getElementById("panel-table"),
  455. card: document.getElementById("panel-card"),
  456. };
  457. buttons.forEach((btn) => {
  458. btn.addEventListener("click", () => {
  459. buttons.forEach((b) => b.classList.remove("active"));
  460. btn.classList.add("active");
  461. Object.keys(panels).forEach((key) => {
  462. panels[key].classList.toggle("active", key === btn.dataset.tab);
  463. });
  464. });
  465. });
  466. </script>
  467. </body>
  468. </html>