需求描述
- 最近产品说,某个
el-table
要实现按住shift键
快速勾选功能 - 大概就是仿
windows系统
的文件shift
按住选中功能 - 反正就是尽可能多的让用户勾选
- 方便用户快速勾选操作
github完整代码:https://github.com/shuirongshuifu/vue3-echarts5-example
Windows系统的功能效果图
- 比如可以向前多选
- 或者向后多选
- 大家可以自己尝试一下
自己实现的el-table勾选效果图
实现思路
- 页面加载好了以后,绑定监听事件,监听用户键盘按下和抬起事件,看看是不是Shift键
- 页面销毁时候,再卸载一下
- 搞三个变量记录是否按下Shift键、勾选el-table是第几行,和再次勾选el-table是第几行
- 假设第一次勾选的是第四行,第二次勾选的是第七行,只需要把四行和七行中间的五六行控制为勾选即可
- 同理,第一次勾选第七行,第二次勾选第四行也是一样
- 最后shift键抬起的时候,控制把三个变量重置即可
- less word,more code
代码
先来个el-table
<template>
<el-table ref="multipleTableRef" :data="tableData" style="width: 100%" @select="selectFn" @select-all="selectAllFn">
<el-table-column type="selection" width="55" />
<el-table-column label="Date" width="120" />
<el-table-column property="name" label="Name" width="120" />
<el-table-column property="address" label="Address" show-overflow-tooltip />
</el-table>
</template>
// 数据
import { ref, onMounted, onBeforeUnmount, reactive } from 'vue'
const tableData = ref([
{
id: 1,
date: '2016-05-01',
name: '111',
address: 'No. 189, Grove St, Los Angeles',
},
...
])
再绑定监听事件
onMounted(() => {
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
});
const onKeyDown = (e) => {
if (e.key === 'Shift') {
clickInfo.isShiftPressed = true;
}
};
const onKeyUp = (e) => {
if (e.key === 'Shift') {
// 鼠标抬起,重置初始状态
clickInfo.isShiftPressed = false;
clickInfo.startRowIndex = -1
clickInfo.endRowIndex = -1
}
};
定义相关变量信息
clickInfo
是收集到的信息
const clickInfo = reactive({
// 开始勾选的索引,初始没勾选为-1
startRowIndex: -1,
// 结束勾选的索引, 初始没勾选为-1
endRowIndex: -1,
// 是否按下shift键,初始没有摁下
isShiftPressed: false
})
当然还需要定义表格实例和勾选存储数组,如下:
const multipleTableRef = ref()
const multipleSelection = ref([])
注意,这里要使用select
和select-all
去控制,不使用selection-change
事件,因为要更灵活第去控制了,如下:
// 全选
const selectAllFn = (selection) => {
multipleSelection.value = selection
}
// 单选
const selectFn = (selection, row) => {
multipleSelection.value = selection
// Shift相关控制逻辑...
}
Shift勾选控制关键代码
- 全选不用控制
- 控制的逻辑主要在单选这一块
- 请对着注释阅读:
// 单选
const selectFn = (selection, row) => {
multipleSelection.value = selection
// 获取当前点击的是第几行
let i = tableData.value.findIndex((item) => item.id == row.id)
// Shift按下逻辑
if (clickInfo.isShiftPressed) {
// 初始没勾选,就赋值开始勾选索引
if (clickInfo.startRowIndex === -1) {
clickInfo.startRowIndex = i
}
// 初始勾选了,说明是第二次勾选
else {
// 赋值索引
clickInfo.endRowIndex = i
// 执行把中间段的表格勾选上逻辑
selectTable(clickInfo.startRowIndex, clickInfo.endRowIndex)
}
}
}
// 执行勾选逻辑
const selectTable = (startRowIndex, endRowIndex) => {
// 第一次勾选后,紧接着再次勾选,有可能往前勾选,也有可能往后勾选,所以要做一个大小区分
const startIndex = Math.min(startRowIndex, endRowIndex);
const endIndex = Math.max(startRowIndex, endRowIndex);
// 遍历去把中间段的勾选上
tableData.value.forEach((rowData, rowIndex) => {
// 若是中间项包含在已勾选的数组中去,就忽略之(这里我们用id为标识做区分)
if (multipleSelection.value.some((msItem) => msItem.id == rowData.id)) { }
// 若是不在勾选的数组中,在去看看要不勾选
else {
// 因为起始勾选和再次勾选的数据,已经保存到勾选数组中去了,所以不用管
if (rowIndex > startIndex && rowIndex < endIndex) {
// 只需把中间段的状态置为勾选,并丢到勾选数组中去就行了
multipleTableRef.value.toggleRowSelection(rowData, rowIndex > startIndex && rowIndex < endIndex)
multipleSelection.value.push(rowData)
}
}
})
}
- 至此,需求就算解决了...
- 但是我们想,若是过两天,另外一个
el-table
也需要这个需求功能呢? - 再复制粘贴一份吗?
- 似乎太麻烦,所以如何优化呢?
- 如何能够做到复用呢?
hook优化
- 懂的都懂,这里使用Vue3中的hook会更加合适
- 更方便复用代码逻辑
什么是hook
复杂的概念简单化...
- 说到代码复用这一块,我们会想到什么?
- 哦,有组件的复用,比如封装一个公共的卡片组件、表单组件
- 哦,有工具函数的复用,比如有一个获取当前的年月日时分秒的函数
- 哦,Vue2还有
Mixin
这个可以概念 - 同样的,
hook也是一种复用的方式
,就是单独拎出来,哪里需要哪里引入,哪里使用即可
hook代码
我们思考一下,这个需求的什么东西可以单独拎出来呢?
- 那这里绑定、销毁键盘按下抬起事件可以拎出来
- 收集的
clickInfo
信息也可以单独拎出来 - 甚至于勾选时候控制表格,给表格的
multipleSelection
塞值,也可以单独拎出来
于是乎,我们就可以这样做了
- 新建一个hook文件夹,用于存放越来越多的hook
- 取个名字,一般用use开头
useShiftQuickSelect.ts
文件 - 拎出来操作,再暴露出去,给外边用
- 代码:
hook/useShiftQuickSelect.ts
import { onMounted, onBeforeUnmount, reactive } from 'vue'
export function useShiftQuickSelect() {
onMounted(() => {
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
});
const onKeyDown = (e) => {
if (e.key === 'Shift') {
clickInfo.isShiftPressed = true;
}
};
const onKeyUp = (e) => {
if (e.key === 'Shift') {
// Shift抬起重置
clickInfo.isShiftPressed = false;
clickInfo.startRowIndex = -1
clickInfo.endRowIndex = -1
}
};
const clickInfo = reactive({
startRowIndex: -1,
endRowIndex: -1,
isShiftPressed: false
})
/**
* tableData表格数据、multipleSelection勾选数组,multipleTableRef表格实例
* key用于进行对比的标识字段,一般都是每一行的唯一身份证即id
* */
const ctr = (tableData, multipleSelection, multipleTableRef, key) => {
// 获取当前点击的是第几行
let i = tableData.findIndex((item) => item.id == key)
// Shift按下逻辑
if (clickInfo.isShiftPressed) {
// 初始没勾选,就赋值开始勾选索引
if (clickInfo.startRowIndex === -1) {
clickInfo.startRowIndex = i
}
// 初始已经勾选,就说明是shift快速勾选
else {
// 索引赋值
clickInfo.endRowIndex = i
// 把开始索引和结束索引进行大小对比
const { startRowIndex, endRowIndex } = clickInfo
const startIndex = Math.min(startRowIndex, endRowIndex);
const endIndex = Math.max(startRowIndex, endRowIndex);
// 遍历操作
tableData.forEach((rowData, rowIndex) => {
// 若是这一项包含在已勾选的数组中去(已勾选),就忽略之;没勾选就控制其勾选
if (!multipleSelection.some((msItem) => msItem.id == rowData.id)) {
// 中间段勾选
if (rowIndex > startIndex && rowIndex < endIndex) {
// 改表格状态并存起来
multipleTableRef.toggleRowSelection(rowData, rowIndex > startIndex && rowIndex < endIndex)
multipleSelection.push(rowData)
}
}
})
}
}
}
return { ctr }
}
- 这里我暴露一个ctr函数,给外层用,外层只要传递进来表格绑定的数据tableData
- 传进来勾选数组multipleSelection
- 传进来表格实例multipleTableRef,用于控制表格勾选
- 和区分某一行是否被勾选的字段(比如用id字段来区分判断)
- 这样的话,就简单多了
- 具体多简单,让我们看看使用的地方,代码:
使用hook的代码
<template>
<el-table ref="multipleTableRef" :data="tableData" style="width: 100%" @select="selectFn" @select-all="selectAllFn">
<el-table-column type="selection" width="55" />
<el-table-column label="Date" width="120">
<template #default="scope">{{ scope.row.date }}</template>
</el-table-column>
<el-table-column property="name" label="Name" width="120" />
<el-table-column property="address" label="Address" show-overflow-tooltip />
</el-table>
<button @click="look">查看已勾选的</button>
</template>
<script setup>
import { ref } from 'vue'
import { useShiftQuickSelect } from "@/hook/useShiftQuickSelect.js";
const tableData = ref([
{
id: 1,
date: '2016-05-01',
name: '111',
address: 'No. 189, Grove St, Los Angeles',
}
])
const multipleTableRef = ref()
const multipleSelection = ref([])
const look = () => {
console.log('multipleSelection', multipleSelection.value);
}
// 全选
const selectAllFn = (selection) => {
multipleSelection.value = selection
}
// 单选
const { ctr } = useShiftQuickSelect()
const selectFn = (selection, row) => {
multipleSelection.value = selection
// hook操作控制函数
ctr(tableData.value, multipleSelection.value, multipleTableRef.value, row.id)
}
</script>
- 看到了叭,只有几行就行了。设置与可以说,仅通过
ctr函数
传递一下参数,问题就解决了。 - 大大提升了效率
- hook is yyds...
A good memory is better than a bad pen. Write it down...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。