vue 实现可以合并拆分插入行和列的表格
时间: 2024-05-15 07:19:24 浏览: 20
以下是一个可以合并拆分插入行和列的表格的 Vue 实现示例:
```html
<template>
<table>
<thead>
<tr>
<th></th>
<th v-for="col in cols">{{ col }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<td>{{ row }}</td>
<td v-for="(col, colIndex) in cols" :key="colIndex">
<span v-if="!isMerged(rowIndex, colIndex)">{{ data[rowIndex][colIndex] }}</span>
<span v-else>{{ getMergedValue(rowIndex, colIndex) }}</span>
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
data() {
return {
rows: ["row1", "row2", "row3"],
cols: ["col1", "col2", "col3"],
data: [
["data1", "data2", "data3"],
["data4", "data5", "data6"],
["data7", "data8", "data9"]
],
mergedCells: [
{ startRow: 0, endRow: 1, startCol: 1, endCol: 2, value: "merged1" },
{ startRow: 1, endRow: 2, startCol: 0, endCol: 1, value: "merged2" }
]
};
},
methods: {
isMerged(rowIndex, colIndex) {
return this.mergedCells.some(
cell =>
rowIndex >= cell.startRow &&
rowIndex <= cell.endRow &&
colIndex >= cell.startCol &&
colIndex <= cell.endCol
);
},
getMergedValue(rowIndex, colIndex) {
const mergedCell = this.mergedCells.find(
cell =>
rowIndex >= cell.startRow &&
rowIndex <= cell.endRow &&
colIndex >= cell.startCol &&
colIndex <= cell.endCol
);
return mergedCell ? mergedCell.value : "";
},
merge() {
const selectedCells = this.getSelectedCells();
if (selectedCells.length > 1) {
const startRow = Math.min(...selectedCells.map(cell => cell.row));
const endRow = Math.max(...selectedCells.map(cell => cell.row));
const startCol = Math.min(...selectedCells.map(cell => cell.col));
const endCol = Math.max(...selectedCells.map(cell => cell.col));
const value = this.data[startRow][startCol];
this.mergedCells.push({ startRow, endRow, startCol, endCol, value });
this.removeSelectedCells(selectedCells);
}
},
split() {
const selectedCell = this.getSelectedCells()[0];
if (selectedCell) {
const mergedCellIndex = this.mergedCells.findIndex(
cell =>
selectedCell.row >= cell.startRow &&
selectedCell.row <= cell.endRow &&
selectedCell.col >= cell.startCol &&
selectedCell.col <= cell.endCol
);
if (mergedCellIndex !== -1) {
const mergedCell = this.mergedCells[mergedCellIndex];
this.mergedCells.splice(mergedCellIndex, 1);
for (let row = mergedCell.startRow; row <= mergedCell.endRow; row++) {
for (let col = mergedCell.startCol; col <= mergedCell.endCol; col++) {
if (row !== selectedCell.row || col !== selectedCell.col) {
this.data[row][col] = "";
}
}
}
}
}
},
insertRow() {
const selectedCell = this.getSelectedCells()[0];
if (selectedCell) {
const rowIndex = selectedCell.row;
this.rows.splice(rowIndex + 1, 0, `newRow${this.rows.length}`);
this.data.splice(rowIndex + 1, 0, new Array(this.cols.length).fill(""));
this.mergedCells.forEach(cell => {
if (cell.startRow >= rowIndex) {
cell.startRow++;
cell.endRow++;
} else if (cell.startRow <= rowIndex && cell.endRow >= rowIndex) {
const newCell = { ...cell };
newCell.endRow++;
this.mergedCells.push(newCell);
}
});
}
},
insertCol() {
const selectedCell = this.getSelectedCells()[0];
if (selectedCell) {
const colIndex = selectedCell.col;
this.cols.splice(colIndex + 1, 0, `newCol${this.cols.length}`);
this.data.forEach(row => row.splice(colIndex + 1, 0, ""));
this.mergedCells.forEach(cell => {
if (cell.startCol >= colIndex) {
cell.startCol++;
cell.endCol++;
} else if (cell.startCol <= colIndex && cell.endCol >= colIndex) {
const newCell = { ...cell };
newCell.endCol++;
this.mergedCells.push(newCell);
}
});
}
},
deleteRow() {
const selectedCell = this.getSelectedCells()[0];
if (selectedCell) {
const rowIndex = selectedCell.row;
this.rows.splice(rowIndex, 1);
this.data.splice(rowIndex, 1);
this.mergedCells = this.mergedCells.filter(
cell => cell.startRow !== rowIndex && cell.endRow !== rowIndex
);
this.mergedCells.forEach(cell => {
if (cell.startRow > rowIndex) {
cell.startRow--;
cell.endRow--;
} else if (cell.startRow <= rowIndex && cell.endRow >= rowIndex) {
const newCell = { ...cell };
newCell.endRow--;
this.mergedCells.push(newCell);
}
});
}
},
deleteCol() {
const selectedCell = this.getSelectedCells()[0];
if (selectedCell) {
const colIndex = selectedCell.col;
this.cols.splice(colIndex, 1);
this.data.forEach(row => row.splice(colIndex, 1));
this.mergedCells = this.mergedCells.filter(
cell => cell.startCol !== colIndex && cell.endCol !== colIndex
);
this.mergedCells.forEach(cell => {
if (cell.startCol > colIndex) {
cell.startCol--;
cell.endCol--;
} else if (cell.startCol <= colIndex && cell.endCol >= colIndex) {
const newCell = { ...cell };
newCell.endCol--;
this.mergedCells.push(newCell);
}
});
}
},
getSelectedCells() {
const selectedCells = [];
const selectedRows = Array.from(this.$el.querySelectorAll(".selected-row"));
selectedRows.forEach(selectedRow => {
const row = selectedRow.getAttribute("data-row");
const selectedCols = Array.from(selectedRow.querySelectorAll(".selected-col"));
selectedCols.forEach(selectedCol => {
const col = selectedCol.getAttribute("data-col");
selectedCells.push({ row: +row, col: +col });
});
});
return selectedCells;
},
removeSelectedCells(selectedCells) {
selectedCells.forEach(selectedCell => {
const selectedRow = this.$el.querySelector(`[data-row="${selectedCell.row}"]`);
if (selectedRow) {
const selectedCol = selectedRow.querySelector(`[data-col="${selectedCell.col}"]`);
if (selectedCol) {
selectedCol.classList.remove("selected-col");
if (!Array.from(selectedRow.querySelectorAll(".selected-col")).length) {
selectedRow.classList.remove("selected-row");
}
}
}
});
}
},
mounted() {
document.addEventListener("mouseup", () => {
const selectedCells = this.getSelectedCells();
if (selectedCells.length === 1) {
const selectedCell = selectedCells[0];
this.$el.querySelectorAll(".selected-row").forEach(selectedRow => {
if (selectedRow.getAttribute("data-row") !== String(selectedCell.row)) {
selectedRow.classList.remove("selected-row");
}
});
this.$el.querySelectorAll(".selected-col").forEach(selectedCol => {
if (selectedCol.getAttribute("data-col") !== String(selectedCell.col)) {
selectedCol.classList.remove("selected-col");
}
});
}
});
}
};
</script>
<style>
table {
border-collapse: collapse;
font-size: 14px;
}
th,
td {
border: 1px solid #ccc;
padding: 8px;
text-align: center;
}
th {
background-color: #eee;
}
.selected-row td {
background-color: #e0e0e0;
}
.selected-col {
background-color: #e0e0e0;
}
</style>
```
该示例中,`rows` 和 `cols` 数组分别用于渲染表格的行和列,`data` 数组用于存储每个单元格的数据,`mergedCells` 数组用于存储合并的单元格。`isMerged` 方法用于判断某个单元格是否属于合并的单元格,`getMergedValue` 方法用于获取某个合并的单元格的值。`merge` 方法用于合并选中的单元格,`split` 方法用于拆分选中的合并单元格,`insertRow` 和 `insertCol` 方法用于插入行和列,`deleteRow` 和 `deleteCol` 方法用于删除行和列。`getSelectedCells` 方法用于获取选中的单元格,`removeSelectedCells` 方法用于取消选中的单元格。在 mounted 钩子中,监听鼠标抬起事件,取消多选时的不必要选中状态。
在模板中,使用 `v-for` 渲染表格的行和列,使用 `v-if` 和 `v-else` 显示单元格的值或合并单元格的值。使用 `data-row` 和 `data-col` 属性标记行和列的索引,使用 `selected-row` 和 `selected-col` 类名标记选中的行和列。