vue中实现el-table点选和鼠标框选功能

news2024/12/28 6:57:30

 

实现思路: 项目有两个需求,既能在el-table实现点选又能实现鼠标框选

一. 点选实现思路: 使用el-table的cellClick方法,

        1.直接给点击的cell添加类名,cell.classList.add("blue-cell");然后把获取的数据存入数组,

           设置样式:

::v-deep .el-table td.blue-cell {
  border: 1px solid blue !important;
}

     方法2.如果不添加类名,可以在cellStyle方法里通过存储的数组添加边框,如果是普通滚动可以使用行索引,如果是虚拟滚动,这里需要使用id更为准确

cellStyle({ row, column, rowIndex, columnIndex }) {
      // 对xqArr选择选区的单元格加蓝边框
      let matchObj = this.xqArr.find(
        item =>
          item.column === column.index &&
          // item.row === row.index &&
          item.rowId === row.id &&
          item.sampleTime === row.sampleTime
      );
      if (matchObj) {
        return { border: "1px solid #5E99FD" };
      }
}

二.鼠标框选实现思路: 利用鼠标按下和抬起事件,计算框的范围,框住的cell可以通过类名添加边框或者依然通过数组形式. 需要注意的是因为el-table在页面的右下方,并且数据量大,可以滚动,所以需要在计算距离的时候需要减去容器偏移的距离和滚动的高度.

              <el-table
                border
                :lazy="true"
                v-loading="loading"
                @cell-click="cellClick"
                :cell-class-name="tableCellClassName"
                @row-contextmenu="rightClick"
                @row-click="clickTableRow"
                @mousedown.native="down($event)"
                @mousemove.native="move($event)"
                @mouseup.native="up($event)"
                :data="historyDataTables"
                :highlight-current-row="true"
                :stripe="true"
                :header-cell-style="{
                  background: '#cff7ff',
                  fontWeight: 'bold',
                  color: '#080809'
                }"
                :row-height="30"
                :total="totalCount"
                :cell-style="cellStyle"
                :max-height="maxHeight1"
                @selection-change="handleSelectionChange"
                ref="multipleTable"
                :row-key="row => row.id"
                id="table"
              >

<script>
  export default {
    data() {
      return {
      // 列表集合
      historyDataTables: [],
      select: false,
      isMouseDown: true, // 是否需要(允许)处理鼠标的移动事件

      // 定义移动元素div
      rect: null,
      // 记录鼠标按下时的坐标
      downX: 0,
      downY: 0,
      // 记录鼠标抬起时候的坐标
      mouseX2: this.downX,
      mouseY2: this.downY,
      // 表格dom元素
      TableDom: null,
      tableHeaderHeight: 0,
      selectedData: [], // 鼠标框选选中的数据

      selectedCellTop: 0, // 选中单元格距离el-table顶部的距离
      selectedCellLeft: 0, // 选中单元格距离el-table左侧的距离

      tableRectTop: 0, // el-table距离window顶部的距离
      tableRectLeft: 0, // el-table距离window左侧的距离
      tableScrollTop: 0, // el-table滚动的距离
      }
  },
  mounted() {
    this.TableDom = this.$refs.multipleTable.$el; // 获取table元素

    // 获取table的位置,监听窗口变化,table的距离变化
    this.getTableMarginLeft();
    window.addEventListener("resize", this.getTableMarginLeft);

    this.clientWidth =
      document.documentElement.clientWidth || document.body.clientWidth;
    this.clientHeight =
      document.documentElement.clientHeight || document.body.cientHeight;

    this.otherHeight =
      Math.ceil($(".is-always-shadow").outerHeight()) +
      Math.ceil($(".is-top").outerHeight());

    this.maxHeight1 = this.clientHeight - this.otherHeight - 150 + "px";

    var that = this;
    window.onresize = () => {
      return (() => {
        window.clientHeight =
          document.documentElement.clientHeight || document.body.clientHeight;
        that.clientHeight = window.clientHeight;
      })();
    };
  },

  beforeDestroy() {
    window.removeEventListener("resize", this.getTableMarginLeft);
  },
  methods: {
    // 获取table距离页面左侧和上方的距离
    getTableMarginLeft() {
      const tableRect = this.TableDom.getBoundingClientRect(); // 获取el-table元素的位置信息
      this.tableRectTop = Math.ceil(tableRect.top);
      this.tableRectLeft = Math.ceil(tableRect.left);
    },

    down(event) {
      // 当允许鼠标按下时,才允许处理鼠标的移动事件,这里结合项目其他问题所以设置了判断条件
      if (this.isMouseDown) {
        this.select = true;
        this.rect = document.createElement("div");
        // 框选div 样式
        this.rect.style.cssText =
          "position:absolute;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:1px solid #0099FF;background-color:#C3D5ED;z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;";
        this.rect.id = "selectDiv";
        this.getTableMarginLeft();

        const container = document.querySelector(".el-table__body-wrapper"); // 获取table容器元素
        this.TableDom.appendChild(this.rect); // 添加到table元素下

        // 取得鼠标按下时的坐标位置
        this.downX =
          event.x || event.clientX + container.scrollLeft - this.tableRectLeft; // 鼠标按下时的x轴坐标 event.x 兼容火狐浏览器, event.clientX 兼容谷歌浏览器
        this.downY =
          event.y || event.clientY + container.scrollTop - this.tableRectTop; // 鼠标按下时的y轴坐标

        this.rect.style.left = this.downX + "px"; // 设置你要画的矩形框的起点位置
        this.rect.style.top = this.downY + "px"; // 设置你要画的矩形框的起点位置
        //设置你要画的矩形框的起点位置
        this.rect.style.left = this.downX; // 因为在火狐浏览器下,上面的代码不起作用,所以在这里再写一遍,为什么火狐浏览器不起作用,因为火狐浏览器下,我们的div是绝对定位的,所以我们要加上px,为什么这里没加px,因为我们下面要加上px,所以这里不用加
        this.rect.style.top = this.downY;
      } else {
        return;
      }
    },
    move(event) {
      /*
      这个部分,根据你鼠标按下的位置,和你拉框时鼠标松开的位置关系,可以把区域分为四个部分,根据四个部分的不同,
      我们可以分别来画框,否则的话,就只能向一个方向画框,也就是点的右下方画框.
      */
      if (this.select && this.isMouseDown) {
        // 取得鼠标移动时的坐标位置
        this.mouseX2 = event.clientX; // 鼠标移动时的x轴坐标
        this.mouseY2 = event.clientY; // 鼠标移动时的y轴坐标
        // 框选元素的显示与隐藏
        if (this.rect.style.display == "none") {
          this.rect.style.display = "";
        }
        // 框选元素的位置处理
        this.rect.style.left =
          Math.min(this.mouseX2, this.downX) - this.tableRectLeft + "px";

        this.rect.style.top =
          Math.min(this.mouseY2, this.downY) - this.tableRectTop + "px"; // 取得鼠标拉框时的起点坐标

        this.rect.style.width = Math.abs(this.mouseX2 - this.downX) + "px"; // 取得鼠标拉框时的宽度

        this.rect.style.height = Math.abs(this.mouseY2 - this.downY) + "px"; // 取得鼠标拉框时的高度
        // A part
        if (this.mouseX2 < this.downX && this.mouseY2 < this.downY) {
          this.rect.style.left = this.mouseX2 + this.tableRectLeft;
          this.rect.style.top = this.mouseY2 + this.tableRectTop;
        }

        // B part
        if (this.mouseX2 > this.downX && this.mouseY2 < this.downY) {
          this.rect.style.left = this.downX + this.tableRectLeft;
          this.rect.style.top = this.mouseY2 + this.tableRectTop;
        }

        // C part
        if (this.mouseX2 < this.downX && this.mouseY2 > this.downY) {
          this.rect.style.left = this.mouseX2 + this.tableRectLeft;
          this.rect.style.top = this.downY + this.tableRectTop;
        }

        // D part
        if (this.mouseX2 > this.downX && this.mouseY2 > this.downY) {
          this.rect.style.left = this.downX + this.tableRectLeft;
          this.rect.style.top = this.downY + this.tableRectTop;
        }
      } else {
        return;
      }

      this.stopEvent(event);
    },
    // 阻止默认事件
    stopEvent(event) {
      if (event.stopPropagation) {
        // 标准浏览器
        event.stopPropagation(); // 阻止事件冒泡
        event.preventDefault(); // 阻止默认事件
      } else {
        // IE浏览器
        event.cancelBubble = true;
        event.returnValue = false;
      }
    },
    // 鼠标抬起事件
    up() {
      if (this.select && this.isMouseDown) {
        const container = document.querySelector(".el-table__body-wrapper"); // 获取table容器元素
        const scrollTop = container.scrollTop; // 获取el-table的scrollTop和scrollLeft
        const scrollLeft = container.scrollLeft;

        const headerWrapper = this.TableDom.querySelector(
          ".el-table__header-wrapper"
        );
        const tableHeaderHeight = Math.ceil(
          headerWrapper.getBoundingClientRect().height
        );

        const columns = this.$refs.multipleTable.columns; // 表格的标题

        const rectLeft = this.rect.offsetLeft + scrollLeft - this.tableRectLeft;
        const rectTop =
          this.rect.offsetTop +
          scrollTop -
          this.tableRectTop -
          tableHeaderHeight;

        const tableBody = document.querySelector(".el-table__body");

        tableBody.children[1].childNodes.forEach(element => {
          for (let index = 0; index < element.childNodes.length; index++) {
            // 获取当前单元格
            const cell = element.childNodes[index];

            if (
              // 判断选中的单元格是否在鼠标拉框的范围内
              rectLeft <
                cell.offsetLeft - this.tableRectLeft + cell.offsetWidth &&
              rectLeft + this.rect.offsetWidth >
                cell.offsetLeft - this.tableRectLeft &&
              rectTop <
                cell.offsetTop - this.tableRectTop + cell.offsetHeight &&
              rectTop + this.rect.offsetHeight >
                cell.offsetTop - this.tableRectTop &&
              index >= 3 // 选中的单元格所在列的索引大于等于3
            ) {
              if (cell.className.indexOf("add") == -1) {
                // cell.style.border = "1px solid red";
                const cellText = cell.innerText;
                const rowData = this.historyDataTables[element.rowIndex]; // 获取当前单元格所在的行数据
                // 获取表格的列名的属性名property
                let columnProperty = undefined;
                // 遍历第一行数据
                 // console.log(index, '--index--'); // 框选数据所在列的索引

                for (const item of columns) {
                  if (item.index === index) {
                    columnProperty = item.property;
                    break;
                  }
                }
                // const rowIndex = element.rowIndex; // 将当前单元格所在的行数据加入到该列数据中
                const columnIndex = index;
                const time = rowData.sampleTime;

                // 选择要添加到选中行数组中的属性
                const selected = {
                  rowId: rowData.id,
                  // row: rowIndex,
                  column: columnIndex,
                  sampleTime: time,
                  factor: columnProperty,
                  tag: rowData[columnProperty + "_tag"] || "",
                  tagStatus: rowData[columnProperty + "_tag_status"] || "",
                  mark: rowData[columnProperty + "_mark"] || ""
                };

                // 将选中数据加入到状态中已有的数据中,如果已有相同的数据,则不加入
                if (
                  !this.selectedData.some(data => this.isEqual(data, selected))
                ) {
                  this.selectedData.push(selected);
                }
                // 将选中数据加入到 xqArr 中
                this.selectedData.forEach(item => {
                  // 如果 xqArr 中已有相同数据,则不加入
                  if (!this.xqArr.some(data => this.isEqual(data, item))) {
                    this.xqArr.push(item);
                  }
                });
                this.selectedData = [];
              }
            }
          }
        });

        //鼠标抬起,就不允许在处理鼠标移动事件
        this.select = false;
        //隐藏图层
        if (this.rect) {
          this.TableDom.removeChild(this.rect);
        }
      } else {
        return;
      }
    },
    // 定义方法 isEqual 来比较两个选中数据对象是否相同
    isEqual(data1, data2) {
      return (
        data1.rowId === data2.rowId &&
        data1.column === data2.column &&
        data1.sampleTime === data2.sampleTime &&
        data1.factor === data2.factor
      );
    }
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/694937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安全帽冲击试验机

一、特点&#xff1a; KDJS-20AM安全帽冲击试验机&#xff08;以下简称试验机&#xff09;适用于安全防护器材安全帽冲击吸收性能试验与安全帽耐穿刺性能试验&#xff0c;冲击和穿刺试验结果既可直接导出到电子表格&#xff0c;也可上传至管理系统。 二、主要技术参数&#x…

蓝牙资讯|未来几年物联网迅猛发展,蓝牙发挥重要作用

IDC预测&#xff0c;2023年全球物联网(IoT)支出将达到8057亿美元&#xff0c;比2022年增长10.6%。物联网生态系统的投资预计将在2026年超过1万亿美元&#xff0c;在2023-2027年的预测期内&#xff0c;复合年增长率(CAGR)为10.4%。 到2023年&#xff0c;物联网服务将成为最大的…

mysql新建用户,连接认证时报错的解决办法

问题描述 mysql新建用户后&#xff0c;修改密码&#xff0c;进行连接认证时报错 Access denied for user testuserlocalhost (using password: YES)原因分析&#xff1a; 未授权 解决方案&#xff1a; GRANT ALL ON *.* TO testuserlocalhost ;注意*.*指对所有数据库下的所…

AOSP安卓源码编译

写在前面 继上次安卓源码下载篇以后已经过了很久了 这里需要补充一些内容&#xff0c;在现在安卓源码是建议只下载自己需要的某个版本&#xff0c;这样更快 #初始化仓库,-b 指示分支&#xff0c;这里使用 android10 repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/A…

Python入门教程+项目实战-14.3节-函数的可变参数

目录 14.3.1 理解可变参数 14.3.2 操作符*与** 14.3.3 对实参进行解包 14.3.4 知识要点 14.3.5 系统学习python 14.3.1 理解可变参数 函数的可变参数&#xff0c;可以从其字面意思来进行理解。“可变”是指参数的数目是变化的&#xff0c;不确定的。在Python中定义函数时…

feign与springcloud版本不匹配报错,查看springcloud组件与springcloud的匹配版本的方法

问题描述 springboot版本号是2.0.0.M3&#xff0c;springcloud的版本号是Finchley.M2 2023-06-27 00:40:02.158 ERROR 26304 --- [ restartedMain] o.s.boot.SpringApplication : Application startup failedjava.lang.IllegalStateException: Error processi…

基于c#和xml存储数据的员工管理系统

完整资料进入【数字空间】查看——baidu搜索"writebug" 项目介绍 本系统是人事管理系统&#xff0c;它主要实现管理员如何对普通用户进行授权&#xff0c;如何插入、删除一个员工的信息&#xff0c;用户如何浏览员工的所有信息&#xff0c;以及用户如何查询自己想要…

服务器数据库中了locked勒索病毒怎么办及正确的解密恢复流程是什么

在当今数字化时代&#xff0c;服务器数据库是企业重要的信息资产之一。然而&#xff0c;网络安全威胁也日益增加&#xff0c;其中之一便是勒索病毒。最近很多企业被.locked后缀勒索病毒攻击&#xff0c;导致系统的重要数据被加密&#xff0c;此次locked勒索病毒非常猖狂&#x…

运动耳机哪个好、超级适合运动的耳机推荐

无论你喜欢跑步、健身、骑行还是瑜伽等等&#xff0c;一款好的运动耳机都能够让你在运动过程中享受音乐的陪伴&#xff0c;让运动不再枯燥。运动耳机成为了运动伴侣中不可或缺的装备。今天&#xff0c;我将为大家介绍几款非常适合运动的耳机&#xff0c;并对这几款耳机进行全方…

在WSL2中安装IntelliJ IDEA开发工具

一、wsl支持图形 windows安装xming https://sourceforge.net/projects/xming/ 添加白名单 查看服务器ip ifconfig 编辑配置文件(结合自己的安装目录) ‪D:\ProgramFiles\Xming\X0.hosts 启动Xlaunh wsl 配置并验证 #b编辑配置文件 vi ~/.bashrc #末尾增加配置 export DI…

Clion开发STM32之W5500系列(三)

前言 编写w5500的驱动测试驱动 驱动编写 相关宏定义 #define sys_force_static_inline __attribute__((always_inline)) static inline寄存器驱动头文件 #ifndef STM32_VET6_W5500_REG_H #define STM32_VET6_W5500_REG_H#include "sys_core.h"#define MR (…

Java发送邮件-工具类-基于springboot

那么&#xff0c;废话少说&#xff0c;直接上代码。 1. 目录结构 重点是那几个带mail的&#xff0c;其他文件不用管。 2. pom <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schema…

【Java-14】3万字深入理解HashMap集合(高级)

1.HashMap集合简介 ​ HashMap基于哈希表的Map接口实现&#xff0c;是以key-value存储形式存在&#xff0c;即主要用来存放键值对。HashMap 的实现不是同步的&#xff0c;这意味着它不是线程安全的。它的key、value都可以为null。此外&#xff0c;HashMap中的映射不是有序的。…

chatgpt赋能python:Python跑步轨迹生成:如何利用Python生成跑步轨迹

Python跑步轨迹生成&#xff1a;如何利用Python生成跑步轨迹 如果你经常跑步&#xff0c;你可能会想知道你每次跑步的轨迹&#xff0c;而不仅仅是跑步的距离和时间。这时&#xff0c;Python可以帮助你生成跑步轨迹&#xff0c;并且还可以将轨迹可视化。在本文中&#xff0c;我…

软考高级系统架构设计师(五) 系统性能评价

目录 概要 性能指标 性能调整 ​阿姆达尔 性能评价 概要 性能指标 参考&#xff1a;【软考-系统架构设计师】知识要点-8 - 知乎 响应时间&#xff1a; 0.1秒&#xff1a;用户感觉不到任何延迟&#xff1b;1.0秒&#xff1a;用户愿意接受的系统立即响应的时间极限&#x…

Liunx 安装MySQL 8 社区版

1.下载MySQL版本 在官网可直接找见对应版本 wget https://dev.mysql.com/get/mysql80-community-release-el8-5.noarch.rpmrpm -ivh mysql80-community-release-el8-5.noarch.rpm2. 使用命令 ls /etc/yum.repos.d/ | grep mysql 查看是否存在以下两个repo文件 3.更新yum资源 …

云原生改造- istio (二)

目录 1 VirtualService 文件 2 DestinationRule 文件 3 演示结果 前提 基于内容的灰度发布&#xff0c;保证在chrome下可以访问V2版本&#xff0c;其他浏览器可以访问v1. 1 VirtualService 文件 apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata:n…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)二:登录界面及对应功能实现

一、本章内容 本章介绍系统登录界面、登录流程、登录接口等相关内容的开发,实现包括账号密码登录、短信验证登录等不同的登录方式,使用svg-capter生成图形验证码,使用expressjwt实现登录token的生成及验证。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预…

CNAS软件测评实验室内审流程与技巧

软件检测实验室通过内部审核活动&#xff0c;可以验证内部体系的运行是否符合管理体系的要求&#xff0c;在内审时&#xff0c;需要审核宜检查管理体系是否满足 ISO/IEC17025 或 ISO/IEC17020、或其他相关准则文件的要求&#xff0c;即符合性检查。 还要检查组织的质量手册及相…

npm install 报错 gyp 解决方案

问题&#xff1a; 接手别人的项目&#xff0c;在安装项目依赖npm install时&#xff0c;一直提示 gyp相关的错误。 问题原因&#xff1a; 项目中依赖项"node-sass": "^4.14.1",与当前node.js版本不符合。 解决问题&#xff1a; 通过百度踩坑&#xff0c;…