vue2+a-table——实现框选选中功能——js技能提升

实现的功能:单个点击勾选数据,用户觉得太麻烦,所以希望可以出一个框选的功能,来实现框住的行都自动勾选。

效果图如上所示:

下面直接上代码:

解决步骤1:给table添加指定id------id="containerId"

解决步骤2:给指定列添加一个固定的className,并绑定id

比如上图中,我并没有每一列都支持框选选中,只有【编号】一列是有效的,所以只要框选的内容包含【编号】一列,则就可以实现功能

js 复制代码
<template #code="{ record }">
  <div class="item" :data-id="record.id">{{ record.code }}</div>
</template>

解决步骤3:在页面mounted函数中添加以下代码:

js 复制代码
this.areaSelector = new AreaSelector({
  element: document.getElementById('containerId'),
  selectableTargetSelector: '.item',
  datasetKeyForSelection: 'id',
  onSelectChange: (arr) => {
    let currentSelectArr = arr.filter(
      (item) => this.selectedKeys.indexOf(item) === -1
    );
    this.selectedKeys = this.selectedKeys.concat(currentSelectArr);//表格选中的数据id集合
    this.$refs.combinedOrderTable.selectedKeys = this.selectedKeys;//给table表格回显选中
  },
});

解决步骤4:写一个类函数------AreaSelector

在项目中新建一个名为dragSelect.js的文件,内容如下:

js 复制代码
export class AreaSelector {
  constructor({
    element,
    selectableTargetSelector,
    datasetKeyForSelection,
    onSelectChange,
  }) {
    this.element = element;
    this.selectableTargetSelector = selectableTargetSelector;
    this.datasetKeyForSelection = datasetKeyForSelection;
    this.onSelectChange = onSelectChange;
    this.selectedIds = [];
    this.#createSelectArea();
    this.#handleMouseDown();
    this.#handleMouseUp();
  }
  #area;
  #startPoint;
  #endPoint;
  #mouseMoveHandler;
  #twoRectsHaveIntersection = (rect1, rect2) => {
    const left1 = rect1.left;
    const left2 = rect2.left;
    const right1 = rect1.left + rect1.width;
    const right2 = rect2.left + rect2.width;
    const top1 = rect1.top;
    const top2 = rect2.top;
    const bottom1 = rect1.top + rect1.height;
    const bottom2 = rect2.top + rect2.height;
    const width1 = rect1.width;
    const width2 = rect2.width;
    const height1 = rect1.height;
    const height2 = rect2.height;
    const noIntersection =
      left2 > right1 ||
      left1 > right2 ||
      bottom1 < top2 ||
      bottom2 < top1 ||
      width1 <= 0 ||
      width2 <= 0 ||
      height1 <= 0 ||
      height2 <= 0;
    return !noIntersection;
  };
  #createSelectArea = () => {
    const area = document.createElement('div');
    this.element.style.position = 'relative';
    area.style.position = 'absolute';
    area.style.zIndex = 15;
    area.style.border = '1px solid #ccc';
    area.style.background = 'rgba(0,119,255,.2)';
    this.element.appendChild(area);
    this.#area = area;
  };
  #selecItems = () => {
    const areaRect = this.#area.getBoundingClientRect();
    const items = document.querySelectorAll(this.selectableTargetSelector);
    let selectionChanged;
    for (const item of items) {
      const itemRect = item.getBoundingClientRect();
      const hasIntersection = this.#twoRectsHaveIntersection(
        areaRect,
        itemRect
      );
      const selected = hasIntersection ? true : false;
      item.dataset.selected = selected;
      const itemId = item.dataset[this.datasetKeyForSelection];
      const index = this.selectedIds.indexOf(itemId);
      if (selected) {
        if (index === -1) {
          this.selectedIds.push(itemId);
          selectionChanged = true;
        }
      } else {
        if (index !== -1) {
          this.selectedIds.splice(index, 1);
          selectionChanged = true;
        }
      }
    }
    if (selectionChanged) {
      this.onSelectChange(this.selectedIds);
    }
  };
  #updateArea = () => {
    const top = Math.min(this.#startPoint.y, this.#endPoint.y);
    const left = Math.min(this.#startPoint.x, this.#endPoint.x);
    const width = Math.abs(this.#startPoint.x - this.#endPoint.x);
    const height = Math.abs(this.#startPoint.y - this.#endPoint.y);
    this.#area.style.top = `${top}px`;
    this.#area.style.left = `${left}px`;
    this.#area.style.width = `${width}px`;
    this.#area.style.height = `${height}px`;
    this.#selecItems();
  };
  #hideArea = () => {
    this.#area.style.display = 'none';
    this.element.style.userSelect = 'all';
  };
  #showArea = () => {
    this.#area.style.display = 'block';
    this.element.style.userSelect = 'none';
  };
  #getRelativePositionInElement = (clientX, clientY) => {
    const { left, top } = this.element.getBoundingClientRect();
    const { scrollLeft, scrollTop, scrollWidth, scrollHeight } = this.element;
    let x = clientX - left + scrollLeft;
    let y = clientY - top + scrollTop;
    if (x < 0) {
      x = 0;
    } else if (x > scrollWidth) {
      x = scrollWidth;
    }
    if (y < 0) {
      y = 0;
    } else if (y > scrollHeight) {
      y = scrollHeight;
    }
    return { x, y };
  };
  #handleMouseDown = () => {
    this.element.addEventListener('mousedown', (e) => {
      if (e.target.nodeName == 'A') {
        window.open(e.target.href, '_blank');
        return;
      }
      const { clientX, clientY } = e;
      this.#startPoint = this.#getRelativePositionInElement(clientX, clientY);
      this.#endPoint = this.#startPoint;
      this.#updateArea();
      this.#showArea();
      this.#handleMouseMove();
    });
  };
  #handleMouseMove = () => {
    this.#mouseMoveHandler = (e) => {
      const { clientX, clientY } = e;
      this.#endPoint = this.#getRelativePositionInElement(clientX, clientY);
      this.#updateArea();
      this.#scrollOnDrag(clientX, clientY);
    };
    window.addEventListener('mousemove', this.#mouseMoveHandler);
  };
  #handleMouseUp = () => {
    window.addEventListener('mouseup', (e) => {
      window.removeEventListener('mousemove', this.#mouseMoveHandler);
      this.#hideArea();
    });
  };
  #scrollOnDrag = (mouseX, mouseY) => {
    const { x, y, width, height } = this.element.getBoundingClientRect();
    let scrollX, scrollY;
    if (mouseX < x) {
      scrollX = mouseX - x;
    } else if (mouseX > x + width) {
      scrollX = mouseX - (x + width);
    }
    if (mouseY < y) {
      scrollY = mouseY - y;
    } else if (mouseY > y + height) {
      scrollY = mouseY - (y + height);
    }
    if (scrollX || scrollY) {
      this.element.scrollBy({
        left: scrollX,
        top: scrollY,
        behavior: 'auto',
      });
    }
  };
}
相关推荐
数据小爬虫@2 小时前
深入解析:使用 Python 爬虫获取苏宁商品详情
开发语言·爬虫·python
健胃消食片片片片2 小时前
Python爬虫技术:高效数据收集与深度挖掘
开发语言·爬虫·python
王老师青少年编程3 小时前
gesp(C++五级)(14)洛谷:B4071:[GESP202412 五级] 武器强化
开发语言·c++·算法·gesp·csp·信奥赛
一只小bit4 小时前
C++之初识模版
开发语言·c++
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
王磊鑫4 小时前
C语言小项目——通讯录
c语言·开发语言
钢铁男儿4 小时前
C# 委托和事件(事件)
开发语言·c#
Ai 编码助手5 小时前
在 Go 语言中如何高效地处理集合
开发语言·后端·golang
喜-喜5 小时前
C# HTTP/HTTPS 请求测试小工具
开发语言·http·c#
ℳ₯㎕ddzོꦿ࿐5 小时前
解决Python 在 Flask 开发模式下定时任务启动两次的问题
开发语言·python·flask