Files
shanxia2.0/docs/fresh-mint-pl-plan-list-merge-cell-demo.html
T
2026-08-27 14:23:18 +08:00

417 lines
19 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>运输计划列表 · 合并单元格 Demo</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;
--surface: #ffffff;
--table-head: #f8fafc;
--row-alt: #fafafa;
--row-hover: #f1f5f9;
--bg-page: #f1f5f9;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
--radius: 10px;
--warn: #d97706;
--danger: #dc2626;
--load-accent: #059669;
--load-bg: #ecfdf5;
--delivery-accent: #0284c7;
--delivery-bg: #f0f9ff;
--plan-divider: #94a3b8;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
line-height: 1.5;
color: var(--text);
background: var(--bg-page);
}
.page { max-width: 1360px; margin: 0 auto; padding: 24px 20px 64px; }
.hero {
background: linear-gradient(135deg, #ecfdf5 0%, #fff 55%, #f0f9ff 100%);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 20px 24px;
margin-bottom: 16px;
box-shadow: var(--shadow);
}
.hero h1 { margin: 0 0 8px; font-size: 20px; font-weight: 700; }
.hero h1 em { font-style: normal; color: var(--primary-dark); }
.hero p { margin: 0; color: var(--text-muted); font-size: 13px; max-width: 880px; }
.legend {
display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px;
}
.legend-item {
display: inline-flex; align-items: center; gap: 8px;
font-size: 12px; color: var(--text-muted);
padding: 4px 10px; border-radius: 999px;
background: #fff; border: 1px solid var(--border);
}
.legend-swatch {
width: 14px; height: 14px; border-radius: 3px; flex-shrink: 0;
}
.legend-swatch.plan { background: #fff; border: 2px solid var(--plan-divider); }
.legend-swatch.load { background: var(--load-bg); border: 1px solid #a7f3d0; }
.legend-swatch.delivery { background: var(--delivery-bg); border: 1px solid #bae6fd; }
.panel {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
}
.panel-top {
display: flex; align-items: center; gap: 8px;
padding: 14px 18px;
border-bottom: 1px solid var(--border);
}
.panel-top h2 {
margin: 0; font-size: 15px; font-weight: 700;
display: flex; align-items: center; gap: 8px;
}
.panel-top h2::before {
content: ""; width: 3px; height: 15px; border-radius: 2px; background: var(--primary);
}
.toolbar {
display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
padding: 10px 16px; background: var(--table-head); border-bottom: 1px solid var(--border);
}
.btn {
display: inline-flex; align-items: center; justify-content: center;
height: 34px; padding: 0 12px; border-radius: 8px;
font-size: 13px; font-weight: 600; border: 1px solid var(--border);
background: #fff; color: var(--text); cursor: pointer; font-family: inherit;
}
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.sel-hint { font-size: 12px; color: var(--text-muted); margin-right: auto; }
.sel-hint b { color: var(--primary-dark); }
.table-wrap { overflow-x: auto; }
table {
width: 100%; border-collapse: collapse; font-size: 13px;
min-width: 1100px;
}
/* 表头:上下两行,标明左右两区 */
thead .group-row th {
font-size: 11px; font-weight: 700; letter-spacing: .05em;
padding: 10px 12px 6px; border-bottom: none;
}
thead .group-row .g-plan {
text-align: center; color: var(--text-muted);
background: #fff; border-right: 2px solid var(--plan-divider);
}
thead .group-row .g-stop {
text-align: center; color: var(--primary-dark);
background: #f0fdf9;
}
thead .col-row th {
background: var(--table-head);
color: var(--text-muted);
font-size: 12px; font-weight: 600;
text-align: left;
padding: 8px 12px 10px;
border-bottom: 1px solid var(--border);
white-space: nowrap;
}
thead .col-row .th-plan { border-right: 2px solid var(--plan-divider); }
tbody td {
padding: 10px 12px;
border-bottom: 1px solid var(--border);
vertical-align: middle;
}
tbody tr:hover td { background: var(--row-hover); }
/* 左侧计划区:合并单元格 */
td.td-plan {
background: #fff !important;
border-right: 2px solid var(--plan-divider);
vertical-align: middle;
}
tbody tr:hover td.td-plan { background: #fff !important; }
/* 右侧段区 */
td.td-stop { background: #fcfffe; }
tbody tr.row-load td.td-stop { background: var(--load-bg); }
tbody tr.row-delivery td.td-stop { background: #fafcff; }
tbody tr:hover td.td-stop { filter: brightness(0.98); }
/* 每个计划块最后一行:加粗底边,视觉上「一条计划」结束 */
tbody tr.plan-end td { border-bottom: 2px solid var(--plan-divider); }
tbody tr.plan-end td.td-plan { border-bottom: 2px solid var(--plan-divider); }
.link {
color: var(--primary-dark); font-weight: 700;
text-decoration: none; font-size: 14px;
}
.link:hover { text-decoration: underline; }
.tag {
display: inline-flex; padding: 2px 8px; border-radius: 4px;
font-size: 12px; font-weight: 600; white-space: nowrap;
}
.tag-success { background: var(--primary-dark); color: #fff; }
.tag-danger { background: var(--danger); color: #fff; }
.tag-warning { background: var(--warn); color: #fff; }
.muted { color: var(--text-muted); font-size: 12px; }
.stop-tag {
display: inline-flex; align-items: center;
padding: 2px 9px; border-radius: 999px;
font-size: 11px; font-weight: 700;
}
.stop-tag.load {
background: var(--load-bg); color: var(--load-accent);
border: 1px solid #a7f3d0;
}
.stop-tag.delivery {
background: var(--delivery-bg); color: var(--delivery-accent);
border: 1px solid #bae6fd;
}
.customer { font-weight: 600; }
.internal {
display: inline-block; margin-left: 4px;
font-size: 10px; font-weight: 600; color: var(--primary-dark);
background: var(--primary-bg); border: 1px solid #a7f3d0;
border-radius: 4px; padding: 0 4px; vertical-align: middle;
}
.addr { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.qty { color: var(--primary-dark); font-weight: 700; }
.product-stack > div + div { margin-top: 3px; }
.copy-stack { display: flex; flex-direction: column; gap: 4px; }
.copy-stack .btn { height: 28px; font-size: 11px; padding: 0 8px; width: 100%; }
.explain {
margin: 16px 18px 18px;
padding: 12px 14px;
border-radius: 8px;
background: var(--primary-bg);
border: 1px solid #a7f3d0;
font-size: 12px;
color: #065f46;
line-height: 1.65;
}
.explain code {
background: rgba(255,255,255,.7);
padding: 1px 5px; border-radius: 4px;
font-size: 11px;
}
.pager {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 16px; border-top: 1px solid var(--border);
font-size: 12px; color: var(--text-muted);
}
/* 示意:rowspan 标注(仅 demo) */
.rowspan-hint {
display: block; margin-top: 6px;
font-size: 10px; color: #94a3b8;
font-weight: 500; letter-spacing: .02em;
}
</style>
</head>
<body>
<div class="page">
<header class="hero">
<h1>运输计划列表 · <em>合并单元格</em> Demo</h1>
<p>
一个运输计划若含 <strong>1 个装货 + 2 个送货</strong>,表格占 <strong>3 行</strong>。
左侧「计划主信息」列使用 <code>rowspan=3</code> 合并为一块;右侧装货、送货#1、送货#2 各占一行。
列划分与线上一致:计划区(编号/状态/日期/类型/车牌/复制)+ 段区(段次/客户/地址时间/产品/计划数量/地磅)。
</p>
<div class="legend">
<span class="legend-item"><span class="legend-swatch plan"></span>计划主信息(合并单元格)</span>
<span class="legend-item"><span class="legend-swatch load"></span>装货段(1 行)</span>
<span class="legend-item"><span class="legend-swatch delivery"></span>送货段(每段 1 行)</span>
</div>
</header>
<section class="panel">
<div class="panel-top"><h2>运输计划</h2></div>
<div class="toolbar">
<span class="sel-hint">已选 <b>1</b> 项(按<strong>计划</strong>勾选,非按段)</span>
<button type="button" class="btn btn-primary">新增计划</button>
<button type="button" class="btn">安排运输</button>
</div>
<div class="table-wrap">
<table>
<thead>
<tr class="group-row">
<th class="g-plan" colspan="8">计划主信息 · 合并单元格(rowspan = 装货 1 行 + 送货 N 行)</th>
<th class="g-stop" colspan="6">装货 / 送货 · 每段一行</th>
</tr>
<tr class="col-row">
<th class="th-plan" style="width:40px"><input type="checkbox" checked /></th>
<th class="th-plan" style="width:48px">序号</th>
<th class="th-plan" style="width:140px">计划编号</th>
<th class="th-plan" style="width:84px">跟进状态</th>
<th class="th-plan" style="width:96px">计划日期</th>
<th class="th-plan" style="width:76px">单据类型</th>
<th class="th-plan" style="width:100px">车牌号</th>
<th class="th-plan" style="width:100px">复制</th>
<th style="width:76px">段次</th>
<th style="width:160px">客户</th>
<th style="width:180px">地址 / 时间</th>
<th style="width:140px">段内产品</th>
<th style="width:72px">计划数量</th>
<th style="width:64px">地磅</th>
</tr>
</thead>
<tbody>
<!-- ==================== 计划 1:1装 + 2送 → 3行,左侧 rowspan=3 ==================== -->
<tr class="row-load">
<td class="td-plan" rowspan="3"><input type="checkbox" checked /></td>
<td class="td-plan" rowspan="3">
1
<span class="rowspan-hint">rowspan=3</span>
</td>
<td class="td-plan" rowspan="3">
<a class="link" href="#">PL20250826001</a>
<div class="muted">跟进:张三</div>
</td>
<td class="td-plan" rowspan="3"><span class="tag tag-success">已安排</span></td>
<td class="td-plan" rowspan="3">2025-08-26</td>
<td class="td-plan" rowspan="3">出库单</td>
<td class="td-plan" rowspan="3">皖A·12345</td>
<td class="td-plan" rowspan="3">
<div class="copy-stack">
<button type="button" class="btn">复制给司机</button>
<button type="button" class="btn">复制给现场</button>
</div>
</td>
<!-- 右侧:装货行 -->
<td class="td-stop"><span class="stop-tag load">装货</span></td>
<td class="td-stop">
<span class="customer">安庆市山峡化工有限公司</span><span class="internal">内部</span>
</td>
<td class="td-stop"><div class="addr">安徽省安庆市大观区 …</div><div class="addr">08-26 08:00</div></td>
<td class="td-stop">
<div class="product-stack">
<div>硫酸 <span class="muted">32%工业级</span></div>
<div>盐酸 <span class="muted">31%</span></div>
</div>
</td>
<td class="td-stop">
<div class="product-stack">
<div><span class="qty">28 吨</span></div>
<div><span class="qty">12 吨</span></div>
</div>
</td>
<td class="td-stop"><span class="tag tag-success" style="font-size:11px">出库</span></td>
</tr>
<tr class="row-delivery">
<!-- 左侧 8 列已被上行 rowspan 占用,本行只渲染右侧 6 列 -->
<td class="td-stop"><span class="stop-tag delivery">送货 #1</span></td>
<td class="td-stop"><span class="customer">合肥某某贸易有限公司</span></td>
<td class="td-stop"><div class="addr">合肥市蜀山区 …</div><div class="addr">08-26 14:00</div></td>
<td class="td-stop">硫酸 <span class="muted">32%工业级</span></td>
<td class="td-stop"><span class="qty">28 吨</span></td>
<td class="td-stop muted">—</td>
</tr>
<tr class="row-delivery plan-end">
<td class="td-stop"><span class="stop-tag delivery">送货 #2</span></td>
<td class="td-stop"><span class="customer">芜湖某某物流有限公司</span></td>
<td class="td-stop"><div class="addr">芜湖市镜湖区 …</div><div class="addr">08-26 17:30</div></td>
<td class="td-stop">盐酸 <span class="muted">31%</span></td>
<td class="td-stop"><span class="qty">12 吨</span></td>
<td class="td-stop muted">—</td>
</tr>
<!-- ==================== 计划 2:1装 + 1送 → 2行,左侧 rowspan=2 ==================== -->
<tr class="row-load">
<td class="td-plan" rowspan="2"><input type="checkbox" /></td>
<td class="td-plan" rowspan="2">
2
<span class="rowspan-hint">rowspan=2</span>
</td>
<td class="td-plan" rowspan="2"><a class="link" href="#">PL20250826002</a></td>
<td class="td-plan" rowspan="2"><span class="tag tag-danger">待安排</span></td>
<td class="td-plan" rowspan="2">2025-08-26</td>
<td class="td-plan" rowspan="2">入库单</td>
<td class="td-plan" rowspan="2 muted">—</td>
<td class="td-plan" rowspan="2 muted">—</td>
<td class="td-stop"><span class="stop-tag load">装货</span></td>
<td class="td-stop"><span class="customer">江苏某某供应商</span></td>
<td class="td-stop"><div class="addr">南京市江宁区 …</div><div class="addr">08-26 09:00</div></td>
<td class="td-stop">甲醇</td>
<td class="td-stop"><span class="qty">20 吨</span></td>
<td class="td-stop muted">—</td>
</tr>
<tr class="row-delivery plan-end">
<td class="td-stop"><span class="stop-tag delivery">送货 #1</span></td>
<td class="td-stop">
<span class="customer">安徽云栋化工有限公司</span><span class="internal">内部</span>
</td>
<td class="td-stop"><div class="addr">安庆市 …</div><div class="addr">08-26 15:00</div></td>
<td class="td-stop">甲醇</td>
<td class="td-stop"><span class="qty">20 吨</span></td>
<td class="td-stop"><span class="tag tag-success" style="font-size:11px">入库</span></td>
</tr>
<!-- ==================== 计划 3:1装 + 1送 → 2行 ==================== -->
<tr class="row-load">
<td class="td-plan" rowspan="2"><input type="checkbox" /></td>
<td class="td-plan" rowspan="2">3<span class="rowspan-hint">rowspan=2</span></td>
<td class="td-plan" rowspan="2"><a class="link" href="#">PL20250825018</a></td>
<td class="td-plan" rowspan="2"><span class="tag tag-warning">等通知</span></td>
<td class="td-plan" rowspan="2">2025-08-25</td>
<td class="td-plan" rowspan="2">直销单</td>
<td class="td-plan" rowspan="2 muted">—</td>
<td class="td-plan" rowspan="2 muted">—</td>
<td class="td-stop"><span class="stop-tag load">装货</span></td>
<td class="td-stop"><span class="customer">山东某某工厂</span></td>
<td class="td-stop"><div class="addr">济南市 …</div><div class="addr">08-25 10:00</div></td>
<td class="td-stop">双氧水</td>
<td class="td-stop"><span class="qty">15 吨</span></td>
<td class="td-stop muted">—</td>
</tr>
<tr class="row-delivery plan-end">
<td class="td-stop"><span class="stop-tag delivery">送货 #1</span></td>
<td class="td-stop"><span class="customer">南京某某客户</span></td>
<td class="td-stop"><div class="addr">南京市 …</div><div class="addr">08-25 16:00</div></td>
<td class="td-stop">双氧水</td>
<td class="td-stop"><span class="qty">15 吨</span></td>
<td class="td-stop muted">—</td>
</tr>
</tbody>
</table>
</div>
<div class="explain">
<strong>读表方式(以 PL20250826001 为例):</strong><br />
① 左侧合并块 = 一条运输计划(勾选、编号、状态、车牌、复制只出现 1 次)。<br />
② 右侧第 1 行 = 装货段(山峡,硫酸+盐酸,地磅出库)。<br />
③ 右侧第 2 行 = 送货#1(合肥,硫酸 28 吨)。<br />
④ 右侧第 3 行 = 送货#2(芜湖,盐酸 12 吨)。<br />
⑤ 实现:<code>flattenPlanListRows</code> + <code>el-table :span-method</code>,<code>rowspan = 1 + deliveries.length</code>;勾选/分页按计划。<br />
⑥ 已落地页:<code>web-admin/src/views/pl/plan/index.vue</code>(列设置 key <code>pl-plan-list-columns-v4</code>)。
</div>
<div class="pager">
<span>共 128 条计划 · 第 1 / 7 页</span>
<span>分页按<strong>计划</strong>计数,不按段行</span>
</div>
</section>
</div>
</body>
</html>