likes
comments
collection
share

vue + ts 实现表格单元格操作(上)

作者站长头像
站长
· 阅读数 34

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
评论
请登录