vue2+a-table——实现框选选中功能——js技能提升
实现的功能:单个点击勾选数据,用户觉得太麻烦,所以希望可以出一个框选的功能,来实现框住的行都自动勾选。
效果图如上所示:
下面直接上代码:
解决步骤1:给table
添加指定id——id="containerId"
解决步骤2:给指定列添加一个固定的className,并绑定id
比如上图中,我并没有每一列都支持框选选中,只有【编号】一列是有效的,所以只要框选的内容包含【编号】一列,则就可以实现功能
<template #code="{ record }">
<div class="item" :data-id="record.id">{{ record.code }}</div>
</template>
解决步骤3:在页面mounted函数中添加以下代码:
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
的文件,内容如下:
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',
});
}
};
}