vue + ts 实现表格单元格操作(上)
花了几天手撸一个表格中单元格操作的组件 我是原生 table 的基础上进行的操作,以下我去掉多余的样式,是写核心操作,比较复杂的就是单元格的多选了,希望可以给你们带来一些帮助
<table class="table" :border="0">
<tr v-for="(row, rowIndex) in data.cells" :key="rowIndex" class="table-row" :style="{ height: item.rowHeight[rowIndex] + 'px' }">
<th
v-for="(cell, columnIndex) in row"
:key="cell.id"
:class="[
'table-cell',
{ selected: isCellSelected(rowIndex, columnIndex) },// 标记选中的单元格
{ border: isCellBorder(rowIndex, columnIndex) },// 当前选中的边框颜色
{ hidden: isCellHidden(rowIndex, columnIndex) } // 被合并的单元格进行隐藏
]"
@contextmenu="cellContext($event, rowIndex, columnIndex)" //右键菜单事件
@mouseenter="updateSelection(rowIndex, columnIndex)" // 鼠标多选
@mouseup="endSelection" // 结束选中操作
:rowspan="cell.rowSpan"
:colspan="cell.colSpan">
<div class="cell-content">
<div
ref="textInputsRef"
class="text-input"
@mousedown="startSelection($event, rowIndex, columnIndex)" // 开始选中
:contenteditable="true" // 元素内容可编辑
v-html="htmlStr(cell)"
@focus="focusText($event)"
@blur="blurText(cell.id, cell, $event)"
@input="writeText($event, cell)"></div>
</div>
</th>
</tr>
</table>
那么开始实现上面方法需要的逻辑,这里我用的是vue3 + ts,逻辑分块展示
// 是否开始选中
const isSelecting = ref(false);
// 记录开始选择和结束选择的单元格索引位置
const cellIndex = reactive({
startRowIndex: -1,
startColumnIndex: -1,
endRowIndex: -1,
endColumnIndex: -1
});
// 表格渲染数据
const data = [
{
id: string;
x: number;
y: number;
w: number;
h: number;
style: Record<string, any>;
row: number;
col: number;
size: { height: number; width: number };
colWidth: number[];
rowHeight: number[];
cells: [
[
{
rowIndex: number; //当前单元格行位置
colIndex: number; //当前单元格列位置
isMerge: boolean;
merge?: {
rowIndex: number; //当前单元格被合并后的头的行位置
colIndex: number; //当前单元格被合并后的头的列位置
};
hidden: boolean;
rowSpan: number;
colSpan: number;
}
]
];
}
];
// 当前选中单元格的边框颜色------------------------------------------------------
const isCellBorder = (rowIndex: number, columnIndex: number) => {
const { startRowIndex, startColumnIndex } = cellIndex;
return rowIndex === startRowIndex && columnIndex === startColumnIndex;
};
// 标记选中的单元格------------------------------------------------------
const isCellSelected = (rowIndex: number, columnIndex: number) => {
const { startRowIndex, startColumnIndex, endRowIndex, endColumnIndex } = cellIndex;
if (rowIndex === startRowIndex && columnIndex === startColumnIndex && endRowIndex === startRowIndex && endColumnIndex === startColumnIndex) {
return false;
}
return (
rowIndex >= Math.min(startRowIndex, endRowIndex) &&
rowIndex <= Math.max(startRowIndex, endRowIndex) &&
columnIndex >= Math.min(startColumnIndex, endColumnIndex) &&
columnIndex <= Math.max(startColumnIndex, endColumnIndex)
);
};
// 隐藏被合并的单元格------------------------------------------------------
const isCellHidden = (rowIndex: number, columnIndex: number) => {
return data.cells[rowIndex][columnIndex].hidden;
};
const rootIndex = {
startRow: 0,
endRow: 0,
startCol: 0,
endCol: 0
};
// 开始选中单元格------------------------------------------------------
const startSelection = (e: MouseEvent, rowIndex: number, columnIndex: number) => {
if (e.button === 2) return false;
rootIndex.startRow = rowIndex;
rootIndex.startCol = columnIndex;
cellIndex.startRowIndex = rowIndex;
cellIndex.startColumnIndex = columnIndex;
cellIndex.endColumnIndex = columnIndex;
cellIndex.endRowIndex = rowIndex;
const current = data.cells[rowIndex][columnIndex];
// colSpan > 1 说明是列合并
if (current.colSpan > 1) {
const mergeColIndex = current.colIndex + current.colSpan - 1;
rootIndex.endCol = mergeColIndex;
} else {
rootIndex.endCol = columnIndex;
}
// colSpan > 1 说明是行合并
if (current.rowSpan > 1) {
const mergeRowIndex = current.rowIndex + current.rowSpan - 1;
rootIndex.endRow = mergeRowIndex;
} else {
rootIndex.endRow = rowIndex;
}
isSelecting.value = true;
};
// 这里的鼠标按住多选移动 分为八个方向:上、右、下、左 及 上左、上右、下左、下右
// 鼠标左键按住多选------------------------------------------------------
const updateSelection = (rowIndex: number, columnIndex: number) => {
if (isSelecting.value) {
// 需要处理最后移入的单元格是合并的最后一个单元格
// 最后一行移入合并单元格的最后一个
// endIndex 是加 startIndex 是减
const current = data.cells[rowIndex][columnIndex];
const { startRow, endRow, startCol, endCol } = rootIndex;
const { endColumnIndex, startColumnIndex } = cellIndex;
const first = data.cells[startRow][startCol];
if (rowIndex === startRow) {
cellIndex.startRowIndex = startRow;
// 上移 或 下移 再返回时 需要将 endRowIndex 恢复
// TODO 需要考虑 起始单元格是合并头的问题
cellIndex.endRowIndex = startRow;
}
if (columnIndex === startCol) {
cellIndex.startColumnIndex = startCol;
// 左移 或 右移 再返回时 需要将 endColumnIndex 恢复
// TODO 需要考虑 起始单元格是合并头的问题
cellIndex.endColumnIndex = startCol;
}
if (rowIndex > startRow) {
console.log('向下移============', rowIndex);
cellIndex.endRowIndex = rowIndex;
if (current.colSpan > 0) {
const mergeColIndex = current.colIndex + current.colSpan - 1;
if (mergeColIndex >= endColumnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
// 如果起始单元格是合并头
if (first.colSpan > 1) {
console.log('起始单元格是合并头');
const mergeColIndex = first.colIndex + first.colSpan - 1;
if (mergeColIndex >= endColumnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
if (columnIndex > startCol) {
console.log('右下移================');
cellIndex.endColumnIndex = columnIndex;
if (current.colSpan > 1) {
const mergeColIndex = current.colIndex + current.colSpan - 1;
if (mergeColIndex >= cellIndex.endColumnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
if (current.rowSpan > 1) {
const mergeColIndex = current.rowIndex + current.rowSpan - 1;
if (mergeColIndex >= cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeColIndex;
}
}
// 如果起始单元格是合并头
if (first.colSpan > 1) {
console.log('起始单元格是列合并头');
const mergeColIndex = first.colIndex + first.colSpan - 1;
if (mergeColIndex >= columnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
if (first.rowSpan > 1) {
console.log('起始单元格是行合并头');
const mergeRowIndex = first.rowIndex + first.rowSpan - 1;
if (mergeRowIndex > cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeRowIndex;
}
}
} else if (columnIndex < startCol) {
console.log('左下移===============');
cellIndex.endColumnIndex = columnIndex;
if (current.colSpan > 0) {
cellIndex.endColumnIndex = current.colIndex;
const mergeColIndex = current.colIndex + current.colSpan - 1;
if (mergeColIndex > startColumnIndex) {
cellIndex.startColumnIndex = mergeColIndex;
}
}
if (current.rowSpan > 0) {
// cellIndex.endRowIndex = current.colIndex;
const mergeColIndex = current.rowIndex + current.rowSpan - 1;
if (mergeColIndex >= cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeColIndex;
}
}
// 如果起始单元格是合并头
if (first.colSpan > 1) {
console.log('起始单元格是列合并头');
const mergeColIndex = first.colIndex + first.colSpan - 1;
if (mergeColIndex >= startColumnIndex) {
cellIndex.startColumnIndex = mergeColIndex;
}
}
if (first.rowSpan > 1) {
console.log('起始单元格是行合并头');
const mergeRowIndex = first.rowIndex + first.rowSpan - 1;
if (mergeRowIndex >= cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeRowIndex;
}
}
}
} else if (rowIndex < startRow) {
console.log('向上移======================', rowIndex);
cellIndex.endRowIndex = rowIndex;
if (current.colSpan > 0) {
const mergeColIndex = current.colIndex + current.colSpan - 1;
if (mergeColIndex > endColumnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
// 如果起始单元格是合并头
if (first.colSpan > 1) {
console.log('起始单元格是合并头');
const mergeColIndex = first.colIndex + first.colSpan - 1;
if (mergeColIndex >= endColumnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
if (columnIndex > startCol) {
console.log('右上移====================');
cellIndex.endColumnIndex = columnIndex;
if (current.rowSpan > 1) {
const mergeRowIndex = current.rowIndex + current.rowSpan - 1;
if (mergeRowIndex > cellIndex.startRowIndex) {
cellIndex.startRowIndex = mergeRowIndex;
}
}
if (current.colSpan > 1) {
const mergeColIndex = current.colIndex + current.colSpan - 1;
if (mergeColIndex >= endColumnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
// 如果起始单元格是合并头
if (first.colSpan > 1) {
console.log('起始单元格是列合并头');
const mergeColIndex = first.colIndex + first.colSpan - 1;
if (mergeColIndex >= columnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
if (first.rowSpan > 1) {
console.log('起始单元格是行合并头');
const mergeRowIndex = first.rowIndex + first.rowSpan - 1;
if (mergeRowIndex > cellIndex.startRowIndex) {
cellIndex.startRowIndex = mergeRowIndex;
}
}
} else if (columnIndex < startCol) {
console.log('左上移==================');
cellIndex.endColumnIndex = columnIndex;
if (current.colSpan > 0) {
cellIndex.endColumnIndex = current.colIndex;
const mergeColIndex = current.colIndex + current.colSpan - 1;
if (mergeColIndex > startColumnIndex) {
cellIndex.startColumnIndex = mergeColIndex;
}
}
if (current.rowSpan > 1) {
const mergeRowIndex = current.rowIndex + current.rowSpan - 1;
if (mergeRowIndex > cellIndex.startRowIndex) {
cellIndex.startRowIndex = mergeRowIndex;
}
}
// 如果起始单元格是合并头
if (first.colSpan > 1) {
console.log('起始单元格是列合并头');
const mergeColIndex = first.colIndex + first.colSpan - 1;
if (mergeColIndex >= startColumnIndex) {
cellIndex.startColumnIndex = mergeColIndex;
}
}
if (first.rowSpan > 1) {
console.log('起始单元格是行合并头');
const mergeRowIndex = first.rowIndex + first.rowSpan - 1;
if (mergeRowIndex > cellIndex.startRowIndex) {
cellIndex.startRowIndex = mergeRowIndex;
}
}
}
} else if (columnIndex > startCol) {
console.log('向右移==================', columnIndex);
cellIndex.endColumnIndex = columnIndex;
if (current.rowSpan > 1) {
const mergeRowIndex = current.rowIndex + current.rowSpan - 1;
if (mergeRowIndex > cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeRowIndex;
}
}
if (current.colSpan > 1) {
const mergeColIndex = current.colIndex + current.colSpan - 1;
if (mergeColIndex >= cellIndex.endColumnIndex) {
cellIndex.endColumnIndex = mergeColIndex;
}
}
if (first.rowSpan > 1) {
console.log('起始单元格是行合并头');
const mergeRowIndex = first.rowIndex + first.rowSpan - 1;
if (mergeRowIndex > cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeRowIndex;
}
}
} else if (columnIndex < startCol) {
console.log('向左移===============', columnIndex);
cellIndex.endColumnIndex = columnIndex;
if (current.rowSpan > 1) {
const mergeRowIndex = current.rowIndex + current.rowSpan - 1;
if (mergeRowIndex > cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeRowIndex;
}
}
if (first.rowSpan > 1) {
console.log('起始单元格是行合并头');
const mergeRowIndex = first.rowIndex + first.rowSpan - 1;
if (mergeRowIndex > cellIndex.endRowIndex) {
cellIndex.endRowIndex = mergeRowIndex;
}
}
if (first.colSpan > 1) {
console.log('起始单元格是列合并头');
const mergeColIndex = first.colIndex + first.colSpan - 1;
if (mergeColIndex >= cellIndex.startColumnIndex) {
cellIndex.startColumnIndex = mergeColIndex;
}
}
}
}
};
// 结束单元格多选------------------------------------------------------
const endSelection = () => {
if (isSelecting.value) {
const startRow = Math.min(cellIndex.startRowIndex, cellIndex.endRowIndex);
const endRow = Math.max(cellIndex.startRowIndex, cellIndex.endRowIndex);
const startColumn = Math.min(cellIndex.startColumnIndex, cellIndex.endColumnIndex);
const endColumn = Math.max(cellIndex.startColumnIndex, cellIndex.endColumnIndex);
let arr: { rowIndex: number, colIndex: number, id: string }[] = [];
if (startRow === endRow && startColumn === endColumn) {
arr = [{ rowIndex: startRow, colIndex: startColumn, id: data.cells[startRow][startColumn].id }];
} else {
data.cells.forEach((row, rowIndex) => {
if (rowIndex >= startRow && rowIndex <= endRow) {
row.forEach((col, colIndex) => {
if (colIndex >= startColumn && colIndex <= endColumn) {
arr.push({ rowIndex, colIndex, id: col.id });
}
});
}
});
}
// 将选中的数据返回
// emit('select-cell', data.id, arr);
}
isSelecting.value = false;
};
下一篇:实现删除行、删除列、插入行、插入列、合并单元格、取消合并
转载自:https://juejin.cn/post/7270464435297730572