最近接到一個新需求,一個表單組件, 要求橫向和縱向兩種模式,要求固定表頭和首列。這裏作個記錄。 首先呢, 固然是踩在巨人的肩膀上, 看看其餘的開源庫怎麼實現的固定表頭和首列,vue
其實, 整個表單被分紅了三個部分, 每一種顏色是一個單獨的表格塊,拼接起來的, 每個部分都是用一個容器包裹着一個table表單, 用於限制寬高和處理滾動算法
<div class="wrapper">
<table></table>
</div>
複製代碼
overflo:hidden
,overflow: atuo
, 監聽他的滾動條事件, 而後同步到固定表頭和固定列的滾動條overflow: hidden
而後絕對定位覆蓋在右邊的表單上面這裏我用了兩個子組件tableHead和tableBody來分別渲染thead和tbody部分,數組
這樣就比較清晰了。剩下的就是核心代碼編寫了。bash
接下來遇到的一個問題就是多級表頭了。app
丟代碼dom
// 提取所有的列
const getAllColumns = (columns) => {
const result = [];
columns.forEach((column) => {
if (column.childs && column.childs.length) {
result.push(column);
result.push.apply(result, getAllColumns(column.childs));
// result.push(...getAllColumns(column.childs))
} else {
result.push(column);
}
});
return result;
}
複製代碼
這個函數的做用是將多個樹形結構組成的數組攤平 A、B分別是這個表頭的頂級單元格函數
// 從樹形整理成二維數組
const convertToRows = (originColumns) => {
let maxLevel = 1;
// 遞歸的算出每個子節點的層數, 和colspan
const traverse = (column, parent) => {
if (parent) {
column.level = parent.level + 1;
if (maxLevel < column.level) {
maxLevel = column.level;
}
}
if (column.childs && column.childs.length) {
let colSpan = 0;
column.childs.forEach((subColumn) => {
traverse(subColumn, column);
colSpan += subColumn.colSpan;
});
// 節點的colspan是子節點的和
column.colSpan = colSpan;
} else {
// 不存在則爲一
column.colSpan = 1;
}
};
originColumns.forEach((column) => {
column.level = 1;
traverse(column);
});
const rows = [];
for (let i = 0; i < maxLevel; i++) {
rows.push([]);
}
// 轉化成一維數組的數據,
// 注意, 在調用getAllColumns以前, 已經將每個節點設置好了level
const allColumns = getAllColumns(originColumns);
// 轉化成二維數組, 計算rowspan
allColumns.forEach((column) => {
if (!column.childs.length) {
column.rowSpan = maxLevel - column.level + 1;
} else {
column.rowSpan = 1;
}
rows[column.level - 1].push(column);
});
return rows;
}
複製代碼
這一步將根據level將這個數組變成一個二維數組、每一層的節點在一個數組內ui
// 獲取滾動條寬度
export function getScrollbarWidth() {
if (Vue.prototype.$isServer) return 0;
if (scrollBarWidth !== undefined) return scrollBarWidth;
const outer = document.createElement('div');
outer.className = 'el-scrollbar__wrap';
outer.style.visibility = 'hidden';
outer.style.width = '100px';
outer.style.position = 'absolute';
outer.style.top = '-9999px';
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll';
const inner = document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll;
return scrollBarWidth;
};
複製代碼
在出現橫向滾動條的時候, 左邊絕對定位會覆蓋一部分橫向滾動條, 以下 spa
若是窗口寬度大於表格的理論寬度, 表格就會自動適應, 底部的tbody邊框了。而左邊絕對定位的寬度沒變, 會致使錯位, 因此要給table設置一個寬度, 這個寬度是能夠經過列數*每列的寬計算出來的prototype
若是須要操做欄的話, 能夠經過vue的做用域插槽來實現, 將當列的數據拋出去就行了