MENU
- 效果
- 公共数据
- 纯原生
- Style
- JavaScript
- vue+原生table
效果
原生的JavaScript+原生table
null
公共数据
const list = [
{
id: "a1",
title: "第一列",
list: [
{
id: "a11",
parentId: "a1",
title: "第二列",
list: [
{ id: "a111", parentId: "a11", title: "第三列第一行" },
{ id: "a112", parentId: "a11", title: "第三列第二行" },
{ id: "a113", parentId: "a11", title: "第三列第三行" },
],
},
],
},
{
id: "a2",
title: "第一列",
list: [
{
id: "a21",
parentId: "a2",
title: "第二列",
list: [
{ id: "a211", parentId: "a21", title: "第三列第一行" },
{ id: "a212", parentId: "a21", title: "第三列第二行" },
{ id: "a213", parentId: "a21", title: "第三列第三行" },
],
},
{
id: "a22",
parentId: "a2",
title: "第二列",
list: [
{ id: "a221", parentId: "a22", title: "第三列第一行" },
{ id: "a222", parentId: "a22", title: "第三列第二行" },
],
},
],
},
{
id: "a3",
title: "第一列",
list: [
{
id: "a31",
parentId: "a3",
title: "第二列",
list: [{ id: "a311", parentId: "a31", title: "第三列第一行" }],
},
],
},
];
–
纯原生
Style
body {
margin: 0px;
display: flex;
justify-content: center;
}
.merged-table {
border-collapse: collapse;
margin: 20px;
width: 95%;
}
.merged-table td,
.merged-table th {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.merged-table th {
background-color: #f2f2f2;
font-weight: bold;
}
.no-data {
text-align: center;
color: #999;
padding: 20px;
}
JavaScript
function createMergedTable(data, container, options = {}) {
// 清除容器原有内容
container.innerHTML = "";
// 合并配置
const config = {
classNames: ["merged-table", ...(options.classNames || [])],
headers: options.headers || null,
emptyText: options.emptyText || "暂无数据",
};
// 计算行合并数
function computeRowspan(node) {
if (!node.list || node.list.length === 0) {
node.rowspan = 1;
return 1;
}
let total = 0;
for (const child of node.list) total += computeRowspan(child);
node.rowspan = total;
return total;
}
// 收集所有叶子节点路径
function collectLeaves(node, chain = [], result = []) {
const newChain = [...chain, node];
if (!node.list || node.list.length === 0) {
result.push(newChain);
return result;
}
for (const child of node.list) collectLeaves(child, newChain, result);
return result;
}
// 创建表头
function createHeader(table, depth) {
const thead = document.createElement("thead");
const tr = document.createElement("tr");
const headers =
config.headers ||
Array.from({ length: depth }, (_, i) => `层级${i + 1}`).concat([
"明细项",
]);
headers.forEach((text) => {
const th = document.createElement("th");
th.textContent = text;
tr.appendChild(th);
});
thead.appendChild(tr);
table.appendChild(thead);
}
// 创建表格主体
function createTableBody(leaves) {
const table = document.createElement("table");
table.className = config.classNames.join(" ");
// 空数据处理
if (leaves.length === 0) {
const tr = document.createElement("tr");
const td = document.createElement("td");
td.className = "no-data";
td.colSpan = config.headers?.length || 3;
td.textContent = config.emptyText;
tr.appendChild(td);
table.appendChild(tr);
return table;
}
// 确定列数
const columnCount = leaves[0].length;
const mergeColumnCount = Math.max(0, columnCount - 1);
// 创建表头
createHeader(table, mergeColumnCount);
// 初始化列跟踪器
const trackers = Array(mergeColumnCount)
.fill()
.map(() => ({}));
// 创建数据行
const tbody = document.createElement("tbody");
leaves.forEach((chain) => {
const tr = document.createElement("tr");
// 处理需要合并的列
for (let i = 0; i < mergeColumnCount; i++) {
const node = chain[i];
if (!trackers[i][node.id]) {
const td = document.createElement("td");
td.textContent = node.title;
td.rowSpan = node.rowspan;
tr.appendChild(td);
trackers[i][node.id] = node.rowspan - 1;
} else {
trackers[i][node.id]--;
if (trackers[i][node.id] === 0) delete trackers[i][node.id];
}
}
// 添加最后一列(不合并)
const lastTd = document.createElement("td");
lastTd.textContent = chain[chain.length - 1].title;
tr.appendChild(lastTd);
tbody.appendChild(tr);
});
table.appendChild(tbody);
return table;
}
// 执行主流程
try {
// 预处理数据
data.forEach((node) => computeRowspan(node));
const leaves = data.flatMap((node) => collectLeaves(node));
// 生成表格
const table = createTableBody(leaves);
container.appendChild(table);
} catch (error) {
console.error("表格生成失败:", error);
container.innerHTML = `<div class="error">表格生成失败: ${error.message}</div>`;
}
}
// 使用示例
createMergedTable(list, document.body, {
headers: ["一级分类", "二级分类", "具体项目"],
classNames: ["custom-table"],
emptyText: "没有找到数据",
});
vue+原生table
敬请期待…