vxeTable 手写复刻区域选择、复制粘贴、拖动柄

vxeTable 复刻区域选择、拖动柄

###更新记录
2024年9月30日17:03:10
补充了 openAreaBox 函数

介绍

vxetable是一个基于 Vue(支持 Vue3) 的 PC 端全功能表格组件,满足你对 table 绝大多数需求,可与任意组件库完美兼容。VXE Table面向现代浏览器,高效的简洁 API 设计,模块化表格、按需加载、扩展接口,为单行编辑表格而设计,支持增删改查及更多扩展,强大的功能的同时兼具性能。

Vxe-table 提供诸多功能:虚拟滚动、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、虚拟列表、模态窗口、自定义模板、渲染器,每一个功能做细了都不容易。

需要的直接点击传送门:https://vxetable.cn/

缘起

我们的系统的表格,需要支持像excel那样区域选择,进行复制粘贴,拖拽区域的右下方需要有一个拖动柄可以进行复制。使用的vxe-table是有这个功能的,但是这个功能需要收费,所以就自己参考着写(CV)了一个。
这些代码仅供学习交流 。如果这些代码能帮助您一点点思路,欢迎点赞收藏,如果觉得侵犯了您的权益,可以联系作者删除。

参考

1. https://blog.csdn.net/wanghanlu_/article/details/132376047?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170591700916800222863864%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=170591700916800222863864&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-132376047-null-null.142^v99^pc_search_result_base9&utm_term=vxetable%E5%8C%BA%E5%9F%9F%E9%80%89%E6%8B%A9&spm=1018.2226.3001.4187
2. https://blog.csdn.net/weixin_45366616/article/details/133739353?spm=1001.2014.3001.5501

实现

实现拖动区域选择,如果是vue2版本可以使用参考1。如果是vue3版本的可以使用参考2。使用了上述的代码,你的vxe-table表格就可以实现区域选择。

我是vue3版本的,所以使用的是参考2。复制下来改吧改吧就能区域选择,但是需要加上按键复制粘贴和拖动柄的功能。接下来就直接上代码。
<template>
    <!-- 正常区域的框 -->
    <div class="vxe-table--cell-area" ref="cellarea">
        <!-- 选择显示的框 -->
        <span class="vxe-table--cell-main-area">
          <!-- 拖动柄的类名 -->
            <span class="vxe-table--cell-main-area-btn"></span>
        </span>
        <span class="vxe-table--cell-active-area"></span>
        <span class="vxe-table--cell-extend-area"></span>
    </div>
    <!-- 左侧fixed区域的框 -->
    <div class="vxe-table--cell-area" ref="leftfixedcellarea">
        <span class="vxe-table--cell-main-area">
            <span class="vxe-table--cell-main-area-btn"></span>
        </span>
        <span class="vxe-table--cell-active-area"></span>
        <span class="vxe-table--cell-extend-area"></span>
    </div>
    <!-- 右侧fixed区域的框 -->
    <div class="vxe-table--cell-area" ref="rightfixedcellarea">
        <span class="vxe-table--cell-main-area">
            <span class="vxe-table--cell-main-area-btn"></span>
        </span>
        <span class="vxe-table--cell-active-area"></span>
        <span class="vxe-table--cell-extend-area"></span>
    </div>
    <div class="apptable table-padding-7">
        <vxe-grid
            v-bind="tableConfig"
            ref="tableCom"
            @edit-closed="editCollectClosedEvent"
            @current-change="currentCollectChangeEvent"
            @checkbox-change="selectCollectChangeEvent"
            @checkbox-all="selectCollectChangeEvent"
        >
        </vxe-grid>
    </div>
</template>
//#region 以下是鼠标选中功能

//鼠标滑动选中
let isSelecting = ref(false); // 是否正在进行选择操作,默认为false
let selectionStart = reactive({ rowIndex: -1, cellIndex: -1 }); // 选择操作起始单元格位置
let selectionEnd = reactive({ rowIndex: -1, cellIndex: -1 }); // 选择操作结束单元格位置

//获取页面ref节点
//获取vxetable表格节点
// let xGrid = ref();
let cellarea = ref();
let leftfixedcellarea = ref();
let rightfixedcellarea = ref();

//返回table的ref名称
const getTablexGrid = () => {
  return tableCom.value;
};
const tableCopy = event => {
  if (event.ctrlKey && event.key === 'c') {
    console.log('Ctrl + C event triggered!');
    if (selectionEnd.rowIndex !== -1) {
      copyEvent(event);
    }
  } else if (event.ctrlKey && event.key === 'v') {
    window.addEventListener('paste', event => {
      if (selectionEnd.rowIndex === -1) {
      } else {
        event.preventDefault();
        let paste = (event.clipboardData || window.clipboardData).getData('text');
        if (isEditView.value) {
          pasteEvent(paste);
        }
      }
    });
  }
};
//添加事件
const addListener = () => {
  //添加多选列
  nextTick(() => {
    if (getTablexGrid().$el) {
      // window.addEventListener('mousedown', tableOutDestroyAreaBox); //给window添加鼠标按下事件,判断是否在表格外,是销毁
      window.addEventListener('mouseup', tbodymouseup); //给window添加鼠标松开事件
      window.addEventListener('keydown', tableCopy); //给window添加键盘按下事件
      let tbody = getTablexGrid() && getTablexGrid().$el.querySelector('.vxe-table--main-wrapper table tbody'); //获取tbody区域

      if (tbody) {
        tbody.addEventListener('mousedown', tbodymousedown); //给表格中的tbody添加鼠标按下事件
        tbody.addEventListener('mousemove', tbodymousemove); //给表格中的tbody添加鼠标移动事件
        tbody.addEventListener('mouseout', throttle(tbodymouseout, 50)); //给表格中的tbody添加鼠标移出事件
        tbody.addEventListener('click', tableCellClick); //添加左键单击事件
        // tbody.addEventListener('copy', (event) => {
        //     copyEvent(event);
        // });
        tbody.oncontextmenu = tableCellMenuClick; //添加右键菜单事件
      }

      let bodyWrapper = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper'); //获取正常区域的body
      if (bodyWrapper) {
        //注意这里的ref名称,这里是非fixed区域的框的名称
        bodyWrapper.appendChild(cellarea.value); //添加范围框元素
      }
      setTimeout(() => {
        //#region 左侧固定列
        let leftfixedtbody = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper table tbody'); //获取fixedtbody区域

        if (leftfixedtbody) {
          leftfixedtbody.addEventListener('mousedown', tbodymousedown); //给表格中的leftfixedtbody添加鼠标按下事件
          leftfixedtbody.addEventListener('mousemove', tbodymousemove); //给表格中的leftfixedtbody添加鼠标移动事件
          leftfixedtbody.addEventListener('mouseout', throttle(tbodymouseout, 50)); //给表格中的leftfixedtbody添加鼠标移出事件
          leftfixedtbody.addEventListener('click', tableCellClick); //添加单击事件
          leftfixedtbody.oncontextmenu = tableCellMenuClick; //添加右键菜单事件
        }

        let leftFixedBodyWrapper = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper');
        if (leftFixedBodyWrapper) {
          //注意这里的ref名称,这里是fixed区域的框的名称
          leftFixedBodyWrapper.appendChild(leftfixedcellarea.value);
        }
        //#endregion

        //#region 右侧固定列
        let rightfixedtbody = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper table tbody'); //获取fixedtbody区域

        if (rightfixedtbody) {
          rightfixedtbody.addEventListener('mousedown', tbodymousedown); //给表格中的rightfixedtbody添加鼠标按下事件
          rightfixedtbody.addEventListener('mousemove', tbodymousemove); //给表格中的rightfixedtbody添加鼠标移动事件
          rightfixedtbody.addEventListener('mouseout', throttle(tbodymouseout, 50)); //给表格中的rightfixedtbody添加鼠标移出事件
          rightfixedtbody.addEventListener('click', tableCellClick); //添加单击事件
          rightfixedtbody.oncontextmenu = tableCellMenuClick; //添加右键菜单事件
        }

        let rightFixedBodyWrapper = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper');
        if (rightFixedBodyWrapper) {
          //注意这里的ref名称,这里是fixed区域的框的名称
          rightFixedBodyWrapper.appendChild(rightfixedcellarea.value);
        }
        //#endregion
      }, 100);
    }
  });
};
// 移除事件
const removeListener = () => {
  //添加多选列
  nextTick(() => {
    if (getTablexGrid().$el) {
      // window.removeEventListener('mousedown', tableOutDestroyAreaBox); //给window添加鼠标按下事件,判断是否在表格外,是销毁
      window.removeEventListener('mouseup', tbodymouseup); //给window添加鼠标松开事件
      window.removeEventListener('keydown', tableCopy); //给window添加键盘按下事件
      window.removeEventListener('paste', event => {
        if (selectionEnd.rowIndex === -1) {
        } else {
          event.preventDefault();
          let paste = (event.clipboardData || window.clipboardData).getData('text');
          if (isEditView.value) {
            pasteEvent(paste);
          }
        }
      });
      let tbody = getTablexGrid() && getTablexGrid().$el.querySelector('.vxe-table--main-wrapper table tbody'); //获取tbody区域

      if (tbody) {
        tbody.removeEventListener('mousedown', tbodymousedown); //给表格中的tbody添加鼠标按下事件
        tbody.removeEventListener('mousemove', tbodymousemove); //给表格中的tbody添加鼠标移动事件
        tbody.removeEventListener('mouseout', throttle(tbodymouseout, 50)); //给表格中的tbody添加鼠标移出事件
        tbody.removeEventListener('click', tableCellClick); //添加左键单击事件
        tbody.oncontextmenu = tableCellMenuClick; //添加右键菜单事件
      }

      let bodyWrapper = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper'); //获取正常区域的body
      if (bodyWrapper) {
        //注意这里的ref名称,这里是非fixed区域的框的名称
        bodyWrapper.appendChild(cellarea.value); //添加范围框元素
      }
      setTimeout(() => {
        //#region 左侧固定列
        let leftfixedtbody = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper table tbody'); //获取fixedtbody区域

        if (leftfixedtbody) {
          leftfixedtbody.removeEventListener('mousedown', tbodymousedown); //给表格中的leftfixedtbody添加鼠标按下事件
          leftfixedtbody.removeEventListener('mousemove', tbodymousemove); //给表格中的leftfixedtbody添加鼠标移动事件
          leftfixedtbody.removeEventListener('mouseout', throttle(tbodymouseout, 50)); //给表格中的leftfixedtbody添加鼠标移出事件
          leftfixedtbody.removeEventListener('click', tableCellClick); //添加单击事件
          leftfixedtbody.oncontextmenu = tableCellMenuClick; //添加右键菜单事件
        }

        let leftFixedBodyWrapper = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper');
        if (leftFixedBodyWrapper) {
          //注意这里的ref名称,这里是fixed区域的框的名称
          leftFixedBodyWrapper.appendChild(leftfixedcellarea.value);
        }
        //#endregion

        //#region 右侧固定列
        let rightfixedtbody = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper table tbody'); //获取fixedtbody区域

        if (rightfixedtbody) {
          rightfixedtbody.removeEventListener('mousedown', tbodymousedown); //给表格中的rightfixedtbody添加鼠标按下事件
          rightfixedtbody.removeEventListener('mousemove', tbodymousemove); //给表格中的rightfixedtbody添加鼠标移动事件
          rightfixedtbody.removeEventListener('mouseout', throttle(tbodymouseout, 50)); //给表格中的rightfixedtbody添加鼠标移出事件
          rightfixedtbody.removeEventListener('click', tableCellClick); //添加单击事件
          rightfixedtbody.oncontextmenu = tableCellMenuClick; //添加右键菜单事件
        }

        let rightFixedBodyWrapper = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper');
        if (rightFixedBodyWrapper) {
          //注意这里的ref名称,这里是fixed区域的框的名称
          rightFixedBodyWrapper.appendChild(rightfixedcellarea.value);
        }
        //#endregion
      }, 100);
    }
  });
};
// 鼠标在填充柄按下事件
let isExtendSelecting = ref(false); // 是否正在进行选择操作,默认为false
let selectionExtendOldStart = reactive({ rowIndex: -1, cellIndex: -1 }); // 选择操作起始单元格位置
let selectionExtendOldEnd = reactive({ rowIndex: -1, cellIndex: -1 }); // 选择操作结束单元格位置
let selectionExtendStart = reactive({ rowIndex: -1, cellIndex: -1 }); // 选择操作起始单元格位置
let selectionExtendEnd = reactive({ rowIndex: -1, cellIndex: -1 }); // 选择操作结束单元格位置
const extendmousedown = (event: MouseEvent) => {
  event.stopPropagation(); //阻止冒泡
  //左键0,中键1,右键2
  if (event.button === 0) {
    //左键按下
    // 记录当前选框的位置
    selectionExtendOldStart = cloneDeep(selectionStart);
    selectionExtendOldEnd = cloneDeep(selectionEnd);
    selectionExtendStart = cloneDeep(selectionStart);
    // selectionExtendOldEnd.rowIndex = selectionEnd.rowIndex;
    // selectionExtendOldEnd.cellIndex = selectionEnd.cellIndex;
    isExtendSelecting.value = true; //标记为正在选择操作
  }
};
//鼠标按下事件
const tbodymousedown = (event: MouseEvent) => {
  event.stopPropagation(); //阻止冒泡
  // getTablexGrid().closeMenu(); //手动关闭右键菜单
  //左键0,中键1,右键2
  if (event.button === 0) {
    //左键按下
    // 记录选择操作起始位置
    selectionStart = getCellPosition(event.target); //设置选择操作起始单元格位置
    if (!isExtendSelecting.value) {
      isSelecting.value = true; //标记为正在选择操作
    }
  }
};
//显示范围框
const openAreaBox = () => {
  var element = tableRef.value.$el.querySelector(
    '.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area'
  );
  if (element) {
    element.style.display = 'block';
  }
  element = tableRef.value.$el.querySelector(
    '.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-area'
  );
  if (element) {
    element.style.display = 'block';
  }
  element = tableRef.value.$el.querySelector(
    '.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-area'
  );
  if (element) {
    element.style.display = 'block';
  }
};
//鼠标移动事件
//todo 这里要节流操作,只在结束时触发一次
const tbodymousemove = (event: MouseEvent) => {
  if (event.button === 0) {
    if (isExtendSelecting.value) {
      selectionExtendEnd = getCellExtendPosition(event.target);
      if (selectionExtendEnd) {
        // 设置样式 打开extend弹窗
        console.log('设置样式 打开extend弹窗');
        setselectedExtendCellArea();
      } else {
        destroyExtendAreaBox();
      }
      return;
    }
    //左键移动
    if (isSelecting.value) {
      //记录选择操作结束位置
      selectionEnd = getCellPosition(event.target);

      //设置样式,并显示范围框
      setselectedCellArea();
    }
    return;
  }
};
const isUp = ref(false);
const isLeft = ref(false);
// 填充柄填充数据方法,复制
const fillDatas = () => {
  console.log(selectionExtendOldStart, selectionExtendOldEnd);
  console.log(selectionStart, selectionEnd);
  let tableData = getTablexGrid().getTableData().visibleData;
  let tableColumn = getTablexGrid().getTableColumn().visibleColumn;
  // 复制的数据集
  let copydata = getDataByArea(selectionExtendOldStart, selectionExtendOldEnd, true);
  // copydata = copydata.reduce((a: string, b) => a + b.join('\t') + '\n', '').slice(0, -1);
  // copydata = copydata.replace(/^\r\n+|\r\n+$/g, '');
  // let snsArr = copydata.split(/\r\n+/);
  // let tArr = snsArr.map((value) => {
  //     return value.split('\t');
  // });
  // console.log(snsArr, tArr);
  const lines = copydata.length;
  const cells = copydata[0].length;
  console.log(lines, cells);
  // 往纵向复制
  if (selectionExtendOldEnd.cellIndex === selectionEnd.cellIndex) {
    if (selectionExtendOldEnd.rowIndex < selectionEnd.rowIndex) {
      // 往下复制
      // 最开始填充的行数
      const needFillStartSelectionRowIndex = selectionExtendOldEnd.rowIndex + 1;
      // 需要填充的总行数
      const needFillLines = selectionEnd.rowIndex - selectionExtendOldEnd.rowIndex;
      for (let i = 0; i < needFillLines; i++) {
        // 获取到被复制的某一行
        let line = copydata[i % lines];
        if (selectionStart.rowIndex + i > tableData.length - 1) break;
        let row = tableData[needFillStartSelectionRowIndex + i];
        for (let j = 0; j < line.length; j++) {
          if (selectionStart.cellIndex + j > tableColumn.length - 1) break;
          let column = tableColumn[selectionStart.cellIndex + j];
          if (column.editRender) {
            row[column.field] = line[j];
          }
        }
      }
    } else if (selectionEnd.rowIndex < selectionExtendOldEnd.rowIndex) {
      isUp.value = true;
      // 往上复制
      console.log('往上复制');
      // 最开始填充的行数
      const needFillStartSelectionRowIndex = selectionStart.rowIndex - 1;
      // 需要填充的总行数
      const needFillLines = selectionStart.rowIndex - selectionEnd.rowIndex;
      for (let i = needFillStartSelectionRowIndex; i >= selectionEnd.rowIndex; i--) {
        // 获取到被复制的某一行
        let line = copydata[(selectionStart.rowIndex - i + 1) % lines];
        if (i < 0) break;
        let row = tableData[i];
        for (let j = 0; j < line.length; j++) {
          if (selectionStart.cellIndex + j > tableColumn.length - 1) break;
          let column = tableColumn[selectionStart.cellIndex + j];
          if (column.editRender) {
            row[column.field] = line[j];
          }
        }
      }
    }
  } else {
    // 横向复制
    if (selectionExtendOldEnd.cellIndex < selectionEnd.cellIndex) {
      // 往右复制
      // 最开始填充的列数
      const needFillStartSelectionCellIndex = selectionExtendOldEnd.cellIndex + 1;
      // 需要填充的总列数
      const needFillLines = selectionEnd.cellIndex - selectionExtendOldEnd.cellIndex;
      // 需要填充的总行数
      // const needFillLinesCount = selectionEnd.rowIndex - selectionStart.rowIndex;
      for (let i = 0; i < lines; i++) {
        // 获取到被复制的某一行
        // let line = copydata[i % needFillLinesCount];
        if (selectionStart.rowIndex + i > tableData.length - 1) break;
        let row = tableData[selectionStart.rowIndex + i];
        for (let j = 0; j < needFillLines; j++) {
          if (needFillStartSelectionCellIndex + j > tableColumn.length - 1) break;
          let column = tableColumn[needFillStartSelectionCellIndex + j];
          if (column.editRender) {
            row[column.field] = copydata[i][j % cells];
          }
        }
      }
    } else if (selectionEnd.cellIndex < selectionExtendOldEnd.cellIndex) {
      isLeft.value = true;
      // 往左复制
      console.log('往左复制');
      // 最开始填充的列数
      const needFillStartSelectionCellIndex = selectionExtendOldStart.cellIndex - 1;
      // 需要填充的总列数
      const needFillLines = selectionExtendOldStart.cellIndex - selectionEnd.cellIndex;
      // 需要填充的总行数
      // const needFillLinesCount = selectionEnd.rowIndex - selectionStart.rowIndex;
      for (let i = 0; i < lines; i++) {
        // 获取到被复制的某一行
        // let line = copydata[i % needFillLinesCount];
        if (selectionStart.rowIndex + i > tableData.length - 1) break;
        let row = tableData[selectionStart.rowIndex + i];
        for (let j = 0; j < needFillLines; j++) {
          if (needFillStartSelectionCellIndex + j > tableColumn.length - 1) break;
          let column = tableColumn[needFillStartSelectionCellIndex - j];
          if (column.editRender) {
            row[column.field] = copydata[i][(selectionExtendOldStart.cellIndex - j + 1) % cells];
          }
        }
      }
    }
  }
  // emit('afterPasteMethod', getTablexGrid().getUpdateRecords());
  nextTick(() => {
    afterPasteMethodFunC(getTablexGrid().getUpdateRecords());
  });
};
//鼠标按键结束事件,添加在了window中
const tbodymouseup = (event: MouseEvent) => {
  if (event.button === 0) {
    //左键松开
    isSelecting.value = false; //标记为停止选择操作
    if (isExtendSelecting.value) {
      if (isUp.value) {
        selectionStart = selectionExtendOldEnd;
      } else {
        selectionStart = selectionExtendOldStart;
      }

      if (isLeft.value) {
        selectionStart = selectionExtendOldEnd;
      } else {
        selectionStart = selectionExtendOldStart;
      }

      if (selectionExtendEnd) {
        selectionEnd.rowIndex = selectionExtendEnd.rowIndex;
        selectionEnd.cellIndex = selectionExtendEnd.cellIndex;
        fillDatas();
        setselectedCellArea();
        destroyExtendAreaBox();
      }
      isExtendSelecting.value = false;
      isUp.value = false;
      isLeft.value = false;
    }
  }
};

let outevent = ref(); //移动事件,不保存,循环定时器内无法监听到新的事件

//鼠标移出表格事件,只在移动的时候会触发,暂停移动不触发
const tbodymouseout = (event: MouseEvent) => {
  outevent.value = event; //保存移动事件

  if (isSelecting.value) {
    //如果正在执行选中操作
    const timer = setInterval(() => {
      //开启循环定时器
      if (isSelecting.value) {
        //判断当前是否正在选择
        //获取表格元素
        var table = getTablexGrid().$el.querySelector('.vxe-table--body-wrapper table'); //获取非固定列(和固定列)的table元素
        if (outevent.value.clientX > table.parentElement.getBoundingClientRect().right - 30) {
          //判断鼠标x轴是否超出表格右侧,向右滚动
          var maxScrollPosition = table.parentElement.scrollWidth - table.parentElement.clientWidth; //获取滚动条最大位置
          if (table.parentElement.scrollLeft < maxScrollPosition) {
            //如果没到滚动条最大位置,执行滚动
            table.parentElement.scrollLeft += 10; //执行水平滚动条向右滚动
          }
        } else if (outevent.value.clientX < table.parentElement.getBoundingClientRect().left + 30) {
          //判断鼠标x轴是否超出表格左侧,向左滚动
          if (table.parentElement.scrollLeft > 0) {
            //如果没到滚动条最大位置,执行滚动
            //鼠标移出表格,滚动水平滚动条
            table.parentElement.scrollLeft -= 10; //执行水平滚动条向右滚动
          }
        }
      } else {
        clearInterval(timer); //清除循环定时器
      }
    }, 200); //这里设置滑动速度
  }
};

//节流函数,todo//改为全局
const throttle = (fn: Function, delay: number) => {
  const canRun = ref(true);
  return (...args: any[]) => {
    if (!canRun.value) return;
    canRun.value = false;
    setTimeout(() => {
      fn(...args);
      canRun.value = true;
    }, delay);
  };
};

// 获取单元格位置(rowIndex, cellIndex)
const getCellPosition = (cell: any) => {
  while (cell.tagName !== 'TD') {
    //将cell指向TD元素
    cell = cell.parentElement;
  }

  let visibleColumn = getTablexGrid().getTableColumn().visibleColumn; //获取处理条件之后的全量表头列
  const cellIndex = visibleColumn.findIndex((col: { id: any }) => {
    //返回colid相等的visibleColumn全量表头列的索引
    return col.id == cell.getAttribute('colid');
  });

  let visibleData = getTablexGrid().getTableData().visibleData; //获取处理条件之后的全量表体数据

  const rowIndex = visibleData.findIndex((row: { Guid: any }) => {
    //返回rowid相等的visibleData全量表体数据
    return row.Guid == cell.parentElement.getAttribute('rowid'); //返回rowid相等的visibleData全量表体数据的索引
  });
  return { rowIndex, cellIndex };
};
// 获取单元格位置(rowIndex, cellIndex)
const getCellExtendPosition = (cell: any) => {
  while (cell.tagName !== 'TD') {
    //将cell指向TD元素
    cell = cell.parentElement;
  }

  let visibleColumn = getTablexGrid().getTableColumn().visibleColumn; //获取处理条件之后的全量表头列
  const cellIndex = visibleColumn.findIndex((col: { id: any }) => {
    //返回colid相等的visibleColumn全量表头列的索引
    return col.id == cell.getAttribute('colid');
  });

  let visibleData = getTablexGrid().getTableData().visibleData; //获取处理条件之后的全量表体数据

  const rowIndex = visibleData.findIndex((row: { Guid: any }) => {
    //返回rowid相等的visibleData全量表体数据
    return row.Guid == cell.parentElement.getAttribute('rowid'); //返回rowid相等的visibleData全量表体数据的索引
  });
  if (cellIndex === selectionExtendOldEnd.cellIndex || rowIndex === selectionExtendOldEnd.rowIndex) {
    return { rowIndex, cellIndex };
  } else {
    return false;
  }
};
//设置框打开
const setselectedCellArea = () => {
  var activeElement = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-active-area'); //正常区域选中边框激活的元素(仅是边框)
  var mainElement = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area'); //正常区域选中边框内整个范围的元素
  var mainElementBtn = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area-btn'); //正常区域选中边框内右下角的点

  var leftFixedActiveElement = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-active-area'); //左侧固定列选中边框激活的元素(仅是边框)
  var leftFixedMainElement = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area'); //左侧固定列选中边框内整个范围的元素
  var leftFixedMainElementBtn = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area-btn'); //左侧固定列选中边框内整个范围的元素

  var rightFixedActiveElement = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-active-area'); //右侧固定列选中边框激活的元素(仅是边框)
  var rightFixedMainElement = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area'); //右侧固定列选中边框内整个范围的元素
  var rightFixedMainElementBtn = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area-btn'); //右侧固定列选中边框内整个范围的元素

  var elements = [activeElement, mainElement, leftFixedActiveElement, leftFixedMainElement, rightFixedActiveElement, rightFixedMainElement];
  var elementsContens = [mainElementBtn, leftFixedMainElementBtn, rightFixedMainElementBtn];
  let area = getAreaBoxPosition();
  if (area) {
    var { width, height, left, top, right } = area;
  } else {
    return;
  }
  elements.forEach((element, index) => {
    if (element) {
      //设置显示范围框的内部元素的样式
      element.style.width = `${width}px`;
      element.style.height = `${height}px`;
      element.style.top = `${top}px`;
      element.style.display = 'block';
      if (index <= elements.length - 1 - 2) {
        //如果不是rightFixedActiveElement或rightFixedMainElement
        element.style.left = `${left}px`;
      } else {
        element.style.right = `${right}px`;
      }
    }
  });
  elementsContens.forEach(element => {
    if (element) {
      element.style.display = 'block';
      if (isEditView.value) {
        element.addEventListener('mousedown', extendmousedown); //给表格中的leftfixedtbody添加鼠标按下事件
      }

      // element.addEventListener('mousemove', extendmousemove); //给表格中的leftfixedtbody添加鼠标移动事件
      // element.addEventListener('mouseout', throttle(extendmouseout, 50)); //给表格中的leftfixedtbody添加鼠标移出事件
      // element.addEventListener('click', extendClick); //添加单击事件
    }
  });

  //显示范围框
  openAreaBox();
};
// 设置extend框打开
const setselectedExtendCellArea = () => {
  var extendElement = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-extend-area');

  var leftFixedExtendElement = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-extend-area'); //左侧固定列选中边框激活的元素(仅是边框)

  var rightFixedExtendElement = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-extend-area'); //右侧固定列选中边框激活的元素(仅是边框)

  var elements = [extendElement, leftFixedExtendElement, rightFixedExtendElement];
  let area = getAreaBoxExtendPosition();
  if (area) {
    var { width, height, left, top, right } = area;
  } else {
    return;
  }
  elements.forEach((element, index) => {
    if (element) {
      //设置显示范围框的内部元素的样式
      element.style.width = `${width}px`;
      element.style.height = `${height}px`;
      element.style.top = `${top}px`;
      element.style.display = 'block';
      if (index <= elements.length - 1 - 1) {
        //如果不是rightFixedActiveElement或rightFixedMainElement
        element.style.left = `${left}px`;
      } else {
        element.style.right = `${right}px`;
      }
    }
  });
};
// 取消extend监听
const removeExtendListener = () => {
  nextTick(() => {
    var mainElementBtn = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area-btn'); //正常区域选中边框内右下角的点
    var leftFixedMainElementBtn = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area-btn'); //左侧固定列选中边框内整个范围的元素
    var rightFixedMainElementBtn = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area-btn'); //右侧固定列选中边框内整个范围的元素
    var elementsContens = [mainElementBtn, leftFixedMainElementBtn, rightFixedMainElementBtn];
    elementsContens.forEach(element => {
      if (element) {
        element.removeEventListener('mousedown', extendmousedown); //给表格中的leftfixedtbody添加鼠标按下事件
        element.style.display = 'none';

        // element.addEventListener('mousemove', extendmousemove); //给表格中的leftfixedtbody添加鼠标移动事件
        // element.addEventListener('mouseout', throttle(extendmouseout, 50)); //给表格中的leftfixedtbody添加鼠标移出事件
        // element.addEventListener('click', extendClick); //添加单击事件
      }
    });
  });
};
//销毁范围框
const destroyExtendAreaBox = () => {
  var element = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-extend-area');
  if (element) {
    element.style.display = 'none';
  }
  element = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-extend-area');
  if (element) {
    element.style.display = 'none';
  }
  element = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-extend-area');
  if (element) {
    element.style.display = 'none';
  }
};
//根据开始位置和结束位置的索引计算框的width,height,left,top(左侧固定列和正常区域和右侧固定列使用)
const getAreaBoxPosition = () => {
  let startRowIndex = selectionStart.rowIndex; //获取选中起始行索引
  let endRowIndex = selectionEnd.rowIndex; //获取选中结束行索引
  let startColumnIndex = selectionStart.cellIndex; //获取选中起始列索引
  let endColumnIndex = selectionEnd.cellIndex; //获取选中结束列索引
  let visibleColumn = getTablexGrid().getTableColumn().visibleColumn; //获取处理条件之后的全量表头列
  let visibleData = getTablexGrid().getTableData().visibleData; //获取处理条件之后的全量表体数据
  if (startColumnIndex < 0 || endColumnIndex < 0 || startRowIndex < 0 || endRowIndex < 0) return;
  var maxColumnIndex = visibleColumn.length - 1; //最大列索引
  var maxRowIndex = visibleData.length - 1; //最大行索引
  if (endColumnIndex > maxColumnIndex) {
    //到最后一列,指向最后一列
    endColumnIndex = maxColumnIndex;
  }
  if (endRowIndex > maxRowIndex) {
    //到最后一行,指向最后一行
    endRowIndex = maxRowIndex;
  }
  let width = 0,
    height = 0,
    left = 0,
    top = 0,
    right = 0;
  visibleColumn.forEach((col: { renderWidth: number }, index: number) => {
    if (startColumnIndex <= endColumnIndex) {
      //开始列索引小于结束列索引,即从左往右选择
      if (index < startColumnIndex) {
        left += col.renderWidth; //距离表格整体左侧边框距离
      }
      if (index > endColumnIndex) {
        //数据索引大于结束列,这里获取距离后面数据的宽度
        right += col.renderWidth; //距离表格整体右侧边框距离,加上当前列
      }
      if (startColumnIndex <= index && index <= endColumnIndex) {
        //开始列索引大于数据索引 和 结束列索引小于数据索引,这里获取选中区域的宽度
        width += col.renderWidth; //选中区域的宽度
      }
    } else {
      //从右往左选择
      if (index < endColumnIndex) {
        left += col.renderWidth; //距离表格整体左侧边框距离
      }
      if (index > startColumnIndex) {
        //数据索引大于开始列,这里获取距离后面数据的宽度
        right += col.renderWidth; //距离表格整体右侧边框距离,加上当前列
      }
      if (startColumnIndex >= index && index >= endColumnIndex) {
        //开始列索引大于数据索引 和 结束列索引小于数据索引,这里获取选中区域的宽度
        width += col.renderWidth; //选中区域的宽度
      }
    }
  });
  if (startRowIndex <= endRowIndex) {
    //开始行索引小于结束行索引,即从上往下选择
    height = (endRowIndex - startRowIndex + 1) * 36; //选中区域的高度
    top = startRowIndex * 36; //距离表格整体顶部边框距离
  } else {
    height = (startRowIndex - endRowIndex + 1) * 36; //选中区域的高度
    if (isUp.value) {
      height = height + (selectionExtendOldEnd.rowIndex - selectionExtendOldStart.rowIndex) * 36;
    }
    top = endRowIndex * 36; //距离表格整体顶部边框距离
  }

  return { width, height, left, top, right };
};
//销毁范围框
const destroyAreaBox = () => {
  var element = getTablexGrid().$el.querySelector('.vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area');
  if (element) {
    element.style.display = 'none';
  }
  element = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-left-wrapper .vxe-table--body-wrapper .vxe-table--cell-area');
  if (element) {
    element.style.display = 'none';
  }
  element = getTablexGrid().$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--fixed-right-wrapper .vxe-table--body-wrapper .vxe-table--cell-area');
  if (element) {
    element.style.display = 'none';
  }
};

//表格单元格点击事件
const tableCellClick = (e: MouseEvent) => {
  if (!isSelecting.value) {
    //非选中状态
    try {
      selectionStart = getCellPosition(e.target); //获取单元格位置
      selectionEnd = selectionStart; //结束位置也是自己
      console.log(selectionStart, selectionEnd);
      //设置样式
      setselectedCellArea();
    } catch (error) {}
  }
};

//表格右键点击事件
const tableCellMenuClick = (e: MouseEvent) => {
  if (!isSelecting.value) {
    //非选中状态
    let currentCellPosition = getCellPosition(e.target); //获取单元格位置
    var horizontalFlag; //是否在范围框的水平判断标记
    var verticalFlag; //是否在范围框的垂直判断标记
    if (selectionStart.cellIndex <= selectionEnd.cellIndex) {
      //如果是从左往右选取
      horizontalFlag = selectionStart.cellIndex <= currentCellPosition.cellIndex && currentCellPosition.cellIndex <= selectionEnd.cellIndex;
    } else {
      //从右往左选取
      horizontalFlag = selectionEnd.cellIndex <= currentCellPosition.cellIndex && currentCellPosition.cellIndex <= selectionStart.cellIndex;
    }
    if (selectionStart.rowIndex <= selectionEnd.rowIndex) {
      //如果是从上往下选取
      verticalFlag = selectionStart.rowIndex <= currentCellPosition.rowIndex && currentCellPosition.rowIndex <= selectionEnd.rowIndex;
    } else {
      //从下往上选取
      verticalFlag = selectionEnd.rowIndex <= currentCellPosition.rowIndex && currentCellPosition.rowIndex <= selectionStart.rowIndex;
    }

    if (horizontalFlag && verticalFlag) {
      //判断如果不在选中区域内,触发表格左键单击事件,更新截取单元格,否则如果在正常触发右键菜单
    } else {
      selectionStart = getCellPosition(e.target); //获取单元格位置
      selectionEnd = selectionStart; //结束位置也是自己
      //设置样式
      setselectedCellArea();
    }
  }
};
// 获取复制的数据
const getDataByArea = (start, end, event) => {
  const data = [];
  for (let i = start.rowIndex; i <= end.rowIndex; i++) {
    const row = [];
    for (let j = start.cellIndex; j <= end.cellIndex; j++) {
      let data = getTablexGrid() && getTablexGrid().getTableData().visibleData[i];
      let head = getTablexGrid() && getTablexGrid().getTableColumn().visibleColumn[j];
      row.push(data[head.field]);
      // if (event === true) {
      //   // 判断是否需要重写属性
      //
      // } else {
      //   row.push(data[head.field]);
      // }
    }
    data.push(row);
  }
  return data;
};
const copyEvent = async (event: any) => {
  //我给大家打印处理:
  console.log('是否正在进行滑动选中操作:', isSelecting.value);
  //左上角坐标
  console.log('单元格起始位置:索引:', selectionStart);
  //右下角坐标
  console.log('单元格结束位置:索引:', selectionEnd);

  //这里需要是visibleData
  // let tableData = getTablexGrid().getTableData().visibleData; //获取处理条件之后的全量表体数据
  // let rowStart = selectionStart.rowIndex; //获取选中起始行索引
  // let rowEnd = selectionEnd.rowIndex; //获取选中结束行索引
  // let selectRows = tableData.filter((col, index: number) => {
  //     //col参数不能改否则会获取不到数据
  //     //这里修改从右下往左上拖动的数据显示
  //     if (rowStart <= rowEnd) {
  //         return rowStart <= index && rowEnd >= index;
  //     } else {
  //         return rowStart >= index && rowEnd <= index;
  //     }
  // });
  // console.log('鼠标选中行:', JSON.stringify(selectRows));

  // //这里需要是visibleColumn
  // let colStart = selectionStart.cellIndex; //获取选中起始列索引
  // let colEnd = selectionEnd.cellIndex; //获取选中结束列索引
  // let tableColumn = getTablexGrid().getTableColumn().visibleColumn; //获取处理条件之后的全量表头列
  // let selectCols = tableColumn.filter((col, index: number) => {
  //     //col参数不能改否则会获取不到数据
  //     //这里修改从右下往左上拖动的数据显示
  //     if (colStart <= colEnd) {
  //         return colStart <= index && colEnd >= index;
  //     } else {
  //         return colStart >= index && colEnd <= index;
  //     }
  // });
  // console.log('鼠标选中列:', JSON.stringify(selectCols));.
  if (tableCom.value) {
    const data = getDataByArea(selectionStart, selectionEnd, event);
    const result = data.reduce((a: string, b) => a + b.join('\t') + '\n', '').slice(0, -1);
    // event.clipboardData.setData('text/plain', result);
    if (XEClipboard.copy(result)) {
      VXETable.modal.message({
        status: 'success',
        content: '复制成功'
      });
    }
    if (getTablexGrid() && getTablexGrid().$el && getTablexGrid().$el.querySelector('.vxe-table--render-wrapper')) {
      selectionStart.cellIndex = -1;
      selectionStart.rowIndex = -1;
      selectionEnd.cellIndex = -1;
      selectionEnd.rowIndex = -1;
      destroyAreaBox();
    }
  }

  // await toClipboard(result);
  // VXETable.modal.message({
  //     status: 'success',
  //     content: '复制成功'
  // });

  // 复制成功取消区域选择框
  // destroyAreaBox();
};
const pasteEvent = async (text: any) => {
  if (text && tableCom.value) {
    let tableData = getTablexGrid() && getTablexGrid().getTableData().visibleData;
    let tableColumn = getTablexGrid() && getTablexGrid().getTableColumn().visibleColumn; //获取处理条件之后的全量表头列
    //去除首尾换行
    text = text.replace(/^\r\n+|\r\n+$/g, '');
    var snsArr = text.split(/\r\n+/);
    var tArr = snsArr.map(value => {
      return value.split('\t');
    });
    for (var i = 0; i < tArr.length; i++) {
      let line = tArr[i];
      if (selectionStart.rowIndex + i > tableData.length - 1) break;
      let row = tableData[selectionStart.rowIndex + i];
      for (var j = 0; j < line.length; j++) {
        if (selectionStart.cellIndex + j > tableColumn.length - 1) break;
        let column = tableColumn[selectionStart.cellIndex + j];
        if (column.editRender) {
          row[column.field] = line[j];
        }
      }
    }
    // 粘贴事件完成以后 你自己的逻辑
  }
};
const getAreaBoxExtendPosition = () => {
    let startRowIndex = selectionStart.rowIndex; //获取选中起始行索引
    let endRowIndex = selectionExtendEnd.rowIndex; //获取选中结束行索引
    let startColumnIndex = selectionStart.cellIndex; //获取选中起始列索引
    let endColumnIndex = selectionExtendEnd.cellIndex; //获取选中结束列索引
    let oldEndRowIndex = selectionEnd.rowIndex; //获取选中结束行索引
    let oldEndColumnIndex = selectionEnd.cellIndex; //获取选中结束行索引
    if (endColumnIndex === oldEndColumnIndex || endRowIndex === oldEndRowIndex) {
        let visibleColumn = getTablexGrid().getTableColumn().visibleColumn; //获取处理条件之后的全量表头列
        let visibleData = getTablexGrid().getTableData().visibleData; //获取处理条件之后的全量表体数据
        if (startColumnIndex < 0 || endColumnIndex < 0 || startRowIndex < 0 || endRowIndex < 0) return;
        var maxColumnIndex = visibleColumn.length - 1; //最大列索引
        var maxRowIndex = visibleData.length - 1; //最大行索引
        if (endColumnIndex > maxColumnIndex) {
            //到最后一列,指向最后一列
            endColumnIndex = maxColumnIndex;
        }
        if (endRowIndex > maxRowIndex) {
            //到最后一行,指向最后一行
            endRowIndex = maxRowIndex;
        }
        let width = 0,
            height = 0,
            left = 0,
            top = 0,
            right = 0;
        visibleColumn.forEach((col: { renderWidth: number }, index: number) => {
            if (startColumnIndex <= endColumnIndex) {
                //开始列索引小于结束列索引,即从左往右选择
                if (index < startColumnIndex) {
                    left += col.renderWidth; //距离表格整体左侧边框距离
                }
                if (index > endColumnIndex) {
                    //数据索引大于结束列,这里获取距离后面数据的宽度
                    right += col.renderWidth; //距离表格整体右侧边框距离,加上当前列
                }
                if (startColumnIndex <= index && index <= endColumnIndex) {
                    //开始列索引大于数据索引 和 结束列索引小于数据索引,这里获取选中区域的宽度
                    width += col.renderWidth; //选中区域的宽度
                }
            } else {
                //从右往左选择
                if (index < endColumnIndex) {
                    left += col.renderWidth; //距离表格整体左侧边框距离
                }
                if (index > startColumnIndex) {
                    //数据索引大于开始列,这里获取距离后面数据的宽度
                    right += col.renderWidth; //距离表格整体右侧边框距离,加上当前列
                }
                if (startColumnIndex >= index && index >= endColumnIndex) {
                    //开始列索引大于数据索引 和 结束列索引小于数据索引,这里获取选中区域的宽度
                    width += col.renderWidth; //选中区域的宽度
                }
            }
        });
        if (startRowIndex <= endRowIndex) {
            //开始行索引小于结束行索引,即从上往下选择
            height = (endRowIndex - startRowIndex + 1) * 36; //选中区域的高度
            top = startRowIndex * 36; //距离表格整体顶部边框距离
        } else {
            height = (startRowIndex - endRowIndex + 1) * 36; //选中区域的高度
            if (isUp.value) {
                height = height + (selectionExtendOldEnd.rowIndex - selectionExtendOldStart.rowIndex) * 36;
            }
            top = endRowIndex * 36; //距离表格整体顶部边框距离
        }

        return { width, height, left, top, right };
    }
};
// 在某个时刻不要忘记取消监听
onBeforeUnmount(() => {
  removeListener();
  if (getTablexGrid() && getTablexGrid().$el && getTablexGrid().$el.querySelector('.vxe-table--render-wrapper')) {
    removeExtendListener();
  }
});
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值