vue待实现excel选中单元格功能

vue实现表格选中效果,问题图片如下
合并单元格选中会出错,感兴趣的可以实现下

img


<template>
    <el-affix position="top" :offset="0">
        <div w-w="full" w-h="20" style="display: flex;background: white">
            <el-space size="large">
                <div>
                    <SvgIcon name="save" title="合并单元格" @click="setMerge"></SvgIcon>

                </div>
            </el-space>
        </div>
    </el-affix>
    <table  class="container" style="user-select: none;"
            @mousedown="startSelection"
            @mousemove="updateSelection"
            @mouseup="endSelection">
        <tr>
            <td></td>
            <td  v-for="(item,index) in data[0]" :key="index" align="center" style="background-color: #f5f7f9;position: relative">
                {{ word[index] + (index + 1) }}
            </td>
        </tr>
        <tr v-for="(row, ri) in data"  style="position: relative" >
            <td style="min-width: 40px; background-color: #f5f7f9;" align="center" :value="false" >
                {{ ri + 1 }}
            </td>
<!--            :row="`${word[ci + Number(data[ri][ci].merge.colSpan > 1 ? data[ri][ci].merge.colSpan : 0)]}${ri + 1}`"-->
<!--            -->
            <template v-for="(col, ci) in row" >
                <td  :class="getCellClass(ri, ci)"
                     :col="`${ci + 1}`"
                     :row="`${ri + 1}`"
                     :rowspan="data[ri][ci].merge.rowSpan"
                     :colspan="data[ri][ci].merge.colSpan"
                     style="min-width: 100px; height: min(34px);"
                     >
                </td>
            </template>
        </tr>
    </table>
</template>

<script setup>

import SvgIcon from "@/components/SvgIcon.vue";

const isSelecting = ref(false) // 是否正在进行选中操作
const selectionStart = ref({row: -1, col: -1}) // 选中区域的起始单元格
const selectionEnd = ref({row: -1, col: -1}) // 选中区域的结束单元格
const rowIndex = ref(0);
const colIndex = ref(0);
const data = ref([])
const word = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
const tableData = ref([])
import {computed, ref} from "vue";
import {defaultTableData} from "@/request/defaultTableData.js";
const setMerge = () => {
    let { startRow, endRow, startCol, endCol } = getRowCol();

    // 更新数据结构以处理合并单元格
    for (let i = startRow - 1; i < endRow; i++) {
        for (let j = startCol - 1; j < endCol; j++) {
            if (i !== startRow - 1 || j !== startCol - 1) {
                // 清除合并单元格的数据
                data.value[i][j] = null;
            }
        }
    }

    data.value[startRow - 1][startCol - 1].merge.rowSpan = endRow - startRow + 1;
    data.value[startRow - 1][startCol - 1].merge.colSpan = endCol - startCol + 1;

    // 调整表格结构,移除多余的单元格
    adjustTableStructure();
}
// 调整表格结构的函数
const adjustTableStructure = () => {
    // 从数据数组中移除多余的单元格
    data.value = data.value.map((row) => row.filter((cell) => cell !== null));
};
const createTable = () => {
    tableData.value = defaultTableData

    data.value = tableData.value[0].data
    console.log(data.value)
}
createTable()
function startSelection(event) {
    console.log(event.target)
    if (event.target.getAttribute("value") === "false") return
    // 开始选中操作
    isSelecting.value = true;
    activeCol.value = 0
    activeRow.value = 0
    selectionStart.value = getCellIndex(event.target);
    console.log(selectionStart.value)
    selectionEnd.value = {...selectionStart.value};
}

let debounceTimer = null;

function updateSelection(event) {
    if (isSelecting.value) {
        // 清除之前的计时器
        if (debounceTimer !== null) {
            clearTimeout(debounceTimer);
        }

        // 创建一个新的计时器,延迟执行 updateSelection
        debounceTimer = setTimeout(() => {
            activeCol.value = 0
            activeRow.value = 0
            selectionEnd.value = getCellIndex(event.target);
            console.log(selectionEnd.value);
        }, 1); // 在这里设置你希望的延迟时间(毫秒)
    }
}

function endSelection() {
    // 结束选中操作
    isSelecting.value = false;
    console.log(data.value)
    console.log(selectionStart.value)
    console.log(selectionEnd.value)
    console.log(activeRow.value, activeCol.value)
}
const activeRow = ref(0)
const activeCol = ref(0)

function getCellIndex(target) {

    // 根据单元格元素获取其在表格中的索引
    const cell = target.closest("td");
    if (cell) {
        const row = cell.parentElement;
        // 获取tr
        console.log(cell.parentElement)
        // console.log(row.parentElement.children)
        let colspan = 0
        let rowspan = 0

        const colComputed = Array.from(row.parentElement.children).indexOf(row)
        const rowComputed = Math.ceil(Array.from(row.children).indexOf(cell))
        console.log(colComputed,rowComputed)
        console.log(data.value[colComputed-1][rowComputed-1])
        const {merge} = data.value[colComputed-1][rowComputed-1]
        const {colSpan,rowSpan} = merge
        if (colSpan > 1){
            activeCol.value = colSpan - 1
        }
        if (rowSpan > 1){
            activeRow.value = rowSpan - 1
        }

        return {
            row: (colComputed < 1 ? 1 : colComputed),
            col: (rowComputed < 1 ? 1 : rowComputed),
        };
    }
    return {row: 1, col: 1};
}
const getCellClass = computed(() => {
    // 根据选中区域的起始和结束单元格计算类名

    const {startRow, endRow, startCol, endCol} = getRowCol()
    // console.log(startRow, endRow, startCol, endCol)

    return (rowIndex, colIndex) => {
        // console.log(rowIndex, colIndex)
        const classes = [];
        if (rowIndex === startRow - 1 && colIndex >= startCol - 1 && colIndex <= endCol - 1) {
            classes.push("border-top");
        }
        if (rowIndex === endRow - 1 && colIndex >= startCol - 1 && colIndex <= endCol - 1) {
            classes.push("border-bottom");
        }
        if (colIndex === startCol - 1 && rowIndex >= startRow - 1  && rowIndex <= endRow - 1) {
            classes.push("border-left");
        }
        if (colIndex === endCol - 1&& rowIndex >= startRow - 1 && rowIndex <= endRow - 1) {
            classes.push("border-right");
        }
        if (rowIndex > startRow - 2 && rowIndex < endRow && colIndex > startCol - 2 && colIndex < endCol) {
            classes.push("ch-td");
        }

        return classes.join(" ");
    };
});
const getRowCol = () => {
    const {row, col} = selectionStart.value;
    const startRow = Math.min(row, selectionEnd.value.row);
    const endRow = Math.max(row, selectionEnd.value.row);
    const startCol = Math.min(col, selectionEnd.value.col);
    const endCol = Math.max(col, selectionEnd.value.col);
    return {startRow, endRow, startCol, endCol}
}
</script>

<style scoped>

>>> .el-affix .el-affix--fixed {
    height: 24px;
}


table, tr, td, th {
    border: 1px solid #c3cbdd;
    font-size: 14px;
    color: #606266;
}

.selected-cell {
    background-color: lightblue; /* 自定义选中样式 */
}

.border-top {
    border-top: #3a8ee6 2px solid;
}

.border-bottom {
    border-bottom: #3a8ee6 2px solid;
}

.border-left {
    border-left: #3a8ee6 2px solid;
}

.border-right {
    border-right: #3a8ee6 2px solid;

}

.ch-td {
    background-color: #d9d9d9;

}

</style>


【以下回答由 GPT 生成】

根据您提供的代码,您希望实现在Vue.js中选中Excel表格单元格的功能。这需要以下几步来实现:

  1. 创建data属性:首先,在Vue组件的data属性中创建一个用于存储选中的单元格位置的变量。例如,可以创建一个名为"selectedCell"的变量,其初始值为空对象。
data() {
  return {
    selectedCell: {}
  }
},
  1. 处理选择事件:在methods中添加处理选择事件的方法,并绑定到表格的mousedown事件上。该方法会记录鼠标按下位置的单元格坐标到"selectedCell"变量。
methods: {
  startSelection(event, rowIndex, columnIndex) {
    // 确保点击的是单元格而不是其他元素
    if (event.target.nodeName === 'TD') {
      this.selectedCell.startRowIndex = rowIndex;
      this.selectedCell.startColumnIndex = columnIndex;
    }
  },
  // 其他方法
},
  1. 处理拖动事件:在methods中添加处理拖动事件的方法,并绑定到表格的mousemove事件上。该方法会更新"selectedCell"变量中的结束位置坐标。
methods: {
  startSelection(event, rowIndex, columnIndex) {
    // 方法内容...
  },
  updateSelection(event, rowIndex, columnIndex) {
    // 鼠标按下并移动时,更新结束位置坐标
    if (this.selectedCell.startRowIndex !== undefined && this.selectedCell.startColumnIndex !== undefined) {
      this.selectedCell.endRowIndex = rowIndex;
      this.selectedCell.endColumnIndex = columnIndex;
    }
  },
  // 其他方法
},
  1. 处理释放事件:在methods中添加处理释放事件的方法,并绑定到表格的mouseup事件上。该方法将使用"selectedCell"变量中的坐标数据进行后续操作,例如保存选中的单元格数据或者进行合并操作。
methods: {
  startSelection(event, rowIndex, columnIndex) {
    // 方法内容...
  },
  updateSelection(event, rowIndex, columnIndex) {
    // 方法内容...
  },
  endSelection() {
    // 清空选中的单元格数据
    this.selectedCell = {};
  },
  // 其他方法
},
  1. 标记选中的单元格:通过使用computed属性或者在模板中添加class的方式来标记选中的单元格。可以使用之前绑定的"selectedCell"变量来判断哪些单元格是选中的。
computed: {
  isSelectedCell() {
    return (rowIndex, columnIndex) => {
      const startRowIndex = Math.min(this.selectedCell.startRowIndex, this.selectedCell.endRowIndex);
      const endRowIndex = Math.max(this.selectedCell.startRowIndex, this.selectedCell.endRowIndex);
      const startColumnIndex = Math.min(this.selectedCell.startColumnIndex, this.selectedCell.endColumnIndex);
      const endColumnIndex = Math.max(this.selectedCell.startColumnIndex, this.selectedCell.endColumnIndex);

      return rowIndex >= startRowIndex && rowIndex <= endRowIndex &&
             columnIndex >= startColumnIndex && columnIndex <= endColumnIndex;
    }
  }
}
<td v-for="(item,index) in data[0]"
    :key="index" align="center"
    :class="{ 'selected': isSelectedCell(rowIndex, index) }"
    style="background-color: #f5f7f9;position: relative">
  {{ word[index] + (index + 1) }}
</td>

请注意,上述代码只是一个基本示例,您可能需要根据实际情况对其进行调整。此外,您还需要添加一些逻辑以保存选中的单元格数据或执行合并等操作。希望对您有所帮助!



【相关推荐】



如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^