实现 element-plus 表格多选时按 shift 进行连选的功能
前言
element-plus表格提供了多选功能,可单击勾选一条数据,可全选。
现在有个很合理的需求,希望实现类似于文件系统中shift连续选择功能,并且在表格排序后,依照排序后的顺序连选。
一、el-table 多选表格基本使用
1、el-table 相关事件、方法
- 插入多选项
<el-table-column type="selection" />
- 表格事件
事件名 | 说明 | 回调参数 |
---|---|---|
select | 当用户手动勾选数据行的 Checkbox 时触发的事件 | selection, row |
select-all | 当用户手动勾选全选 Checkbox 时触发的事件 | selection |
selection-change | 当选择项发生变化时会触发该事件 | selection |
- 表格方法
方法名 | 说明 | 参数 |
---|---|---|
clearSelection | 用于多选表格,清空用户的选择 | - |
getSelectionRows | 返回当前选中的行 | |
toggleRowSelection | 用于多选表格,切换某一行的选中状态, 如果使用了第二个参数,则可直接设置这一行选中与否 | row, selected |
toggleAllSelection | 用于多选表格,切换全选和全不选 | - |
- table-column 属性
selectable
: 仅对 type=selection 的列有效,类型为 Function,Function 的返回值用来决定这一行的 CheckBox 是否可以勾选。(类型:function(row, index)
)
2、el-table 多选表格示例
<template><el-table:data="tableData"@selection-change="handleSelectionChange"><el-table-column type="selection" /><!-- other columns --></el-table>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getTableData } from '@/api/demo.js'const tableData = ref([])
const selectedRows = ref([])onMounted(() => {getData()
})const getData = () => {getTableData().then(res => {tableData.value = res.data ?? []})
}const handleSelectionChange = (selection) => {selectedRows.value = selection
}
</script>
二、实现
由于该功能可能会应用到很多页面中,最好是提供一个统一的公共方法,在页面中引入使用
1、分析
基本过程:
> 记录上一次点击的数据;记录 shift 状态
> 组件挂载后,监听 shift 键的 keydown
, keyup
事件,更新 shift 状态;组件销毁前取消相关监听
> 监听 el-table@select 事件,对比上一次点击与本次点击,计算待连选数据列表后,使用 toggleRowSelection
方法进行连续勾选
2、实现
根据分析,初步创建方法
注意: 为了方便理解思路,下面的示例中仅包含关键代码,完整代码在最后
el-table-multi-select.js:
import { ref, readonly, watch, onMounted, onBeforeUnmount } from 'vue'export function elTableMultiSelect(tableEl) {// 表格数据const tableData = ref([])// 选中数据列表const selectedRows = ref([])// 下标记录const lastIdx = ref(-1)// shift标识const shiftFlag = ref(false)onMounted(() => {// el-table 表格排序不会体现在绑定的数据列表上// 监听 el-table 组件内部状态存储中的表格数据(避免表格排序后连选不连续的bug)watch(tableEl.store?.states?.data, (newVal) => {tableData.value = newVal.map((item,idx) => {item.index = idxreturn item})}, { immediate: true })// Shift监听/取消监听document.addEventListener('keydown', handleKeyDown)document.addEventListener('keyup', handleKeyUp)})// 取消Shift监听onBeforeUnmount(() => {document.removeEventListener('keydown', handleKeyDown)document.removeEventListener('keyup', handleKeyUp)})// Shift事件处理function handleKeyDown({ key }) {if(key !== 'Shift') returnif(shiftFlag.value) returnshiftFlag.value = true}function handleKeyUp({ key }) {if(key !== 'Shift') returnif(!shiftFlag.value) returnshiftFlag.value = falselastIdx.value = -1}// el-table@selectfunction handleTbSelect(selection, row) {updateSelection(selection)// 若未按 shift,更新 lastIdxif(!shiftFlag.value) {if(selection.find(r => r.index === row.index)) lastIdx.value = row.indexreturn}// 若 lastIdx 无有效值,记录本次下标if(lastIdx.value === -1) {lastIdx.value = row.indexreturn}// 若 lastIdx 有值,自动勾选中间rowslet [start, end] = [lastIdx.value, row.index]if(start > end) [start, end] = [end, start]// TODO: toggleRowSelection// ...}// el-table@selection-changefunction handleTbSelectionChange(selection) {updateSelection(selection)}// 更新 selectedRowsfunction updateSelection(selection) {selectedRows.value = selection}return {selectedRows: readonly(selectedRows),handleTbSelect,handleTbSelectionChange}
}
组件中引入使用
demo.vue:
<template><el-table:data="tableData"@select="handleTbSelect"@selection-change="handleTbSelectionChange"ref="demoTable"><el-table-column type="selection" /><!-- other columns --></el-table>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getTableData } from '@/api/demo.js'
import { elTableMultiSelect } from '@/use/el-table-multi-select'const demoTable = ref()
const tableData = ref([])
// const selectedRows = ref([]) // 从公共方法中导出const {selectedRows,handleTbSelect,handleTbSelectionChange
} = elTableMultiSelect(demoTable)onMounted(() => {getData()
})const getData = () => {getTableData().then(res => {tableData.value = res.data ?? []})
}// 从公共方法中导出
// const handleSelectionChange = (selection) => {
// selectedRows.value = selection
// }
3、小结
注意:在下面的完整代码中,本人监听了 el-table 组件内部状态存储信息(tableEl.store.states.data
),未公布在官网。随着版本的更新,有失效的风险。建议锁定 element-plus 版本号或者确定所使用的版本仍有效。
不监听表格绑定的数据本身,是因为el-table排序后,不会体现在绑定的数据上,也就是说,排序后,连选功能还是按照排序前的顺序来进行连选的。
三、代码与演示
在线演示
工具方法完整代码:
import { ref, readonly, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'/*** 工具类: el-table 可多选表格,增加shift连选功能* * @param {string|Element} tableRef 选项式API中,传表格ref字符串;setup中,传表格对象* @param {Function} [checkRowSelectable] 禁选方法(可选),对应el-table-column selectable属性值* @returns {Object} {* selectedRows: 多选结果列表,* handleTbSelect: el-table@select,* handleTbSelectionChange: el-table@selection-change,* clearSelection: 清空多选结果列表* }* @example* // 一、引入* * import { elTableMultiSelect } from '@/use/el-table-multi-select'* * // 二、template* * // el-table 相关属性方法* @select="handleTbSelect"* @selection-change="handleTbSelectionChange"* ref="multiSelectTable"* * // 三、方法调用:* * ------------------------1、选项式API:------------------------* * // data() 相关变量声明:* selectedRows: [],* handleTbSelect: undefined,* handleTbSelectionChange: undefined* * // created() 中解构赋值:* ;({* selectedRows: this.selectedRows,* handleTbSelect: this.handleTbSelect,* handleTbSelectionChange: this.handleTbSelectionChange* } = elTableMultiSelect.call(this, 'multiSelectTable', this.enableSelection)) // 传表格ref字符串* // methods:* enableSelection(row, rowIndex) {* return !row.suspected_detection_seq* }* * ------------------------2、组合式API:------------------------* * const multiSelectTable = ref()* const {* selectedRows, handleTbSelect, handleTbSelectionChange* } = elTableMultiSelect(multiSelectTable, enableSelection) // 传表格ref对象* * function enableSelection(row, rowIndex) {* return !row.suspected_detection_seq* }*/
export function elTableMultiSelect(tableRef, checkRowSelectable) {// 表格数据const tableData = ref([])// 选中数据列表const selectedRows = ref([])// 下标记录const lastIdx = ref(-1)// shift标识const shiftFlag = ref(false)let tableEl // 表格对象const tbFlag = ref(false) // 标识:表格挂载完毕const mountedFlag = ref(false) // 标识:组件挂载完毕const isSetup = typeof(tableRef) !== 'string'isSetup && watch(tableRef, (newVal) => {if(newVal) {tableEl = newValtbFlag.value = true}}, { deep: true, immediate: true })onMounted(() => {mountedFlag.value = trueif(!isSetup) {tbFlag.value = truetableEl = this.$refs[tableRef]}// Shift监听/取消监听document.addEventListener('keydown', handleKeyDown)document.addEventListener('keyup', handleKeyUp)})// 取消Shift监听onBeforeUnmount(() => {document.removeEventListener('keydown', handleKeyDown)document.removeEventListener('keyup', handleKeyUp)})// Shift事件处理function handleKeyDown({ key }) {if(key !== 'Shift') returnif(shiftFlag.value) returnshiftFlag.value = true}function handleKeyUp({ key }) {if(key !== 'Shift') returnif(!shiftFlag.value) returnshiftFlag.value = falselastIdx.value = -1}// 表格挂载 & 组件挂载 后, 添加 tableData 监听事件const tbMountedWatcher = watch([tbFlag, mountedFlag], ([tf, mf]) => {if(tf && mf) {// el-table 表格排序不会体现在绑定的数据列表上// 监听 el-table 组件内部状态存储中的表格数据(避免表格排序后连选不连续的bug)watch(tableEl.store?.states?.data, (newVal) => {// console.log('watch el-table store', newVal.length)tableData.value = newVal.map((item,idx) => {item.index = idxreturn item})}, { immediate: true })tbMountedWatcher() // 取消监听}})// toggleRowSelection 会触发 handleTbSelectionChange// onprogress 控制shift多选时,只触发一次 handleTbSelectionChange// (handleTbSelectionChange 为同步执行方法)const onprogress = ref(false)/*** 当选择项发生变化时会触发该事件* @param {Array} selection selected rows*/function handleTbSelectionChange(selection) {if(!onprogress.value) updateTbSelection(selection)}/*** 当用户手动勾选数据行的 Checkbox 时触发的事件* @param {Array} selection selected rows* @param {Object} row table row data* @returns */function handleTbSelect(selection, row) {updateTbSelection(selection)if(!shiftFlag.value) {if(selection.find(r => r.index === row.index)) lastIdx.value = row.indexreturn}// lastIdx为-1时,记录本次下标if(lastIdx.value === -1) {lastIdx.value = row.indexreturn}// lastIdx 有值,自动勾选中间rowslet [start, end] = [lastIdx.value, row.index]if(start > end) [start, end] = [end, start]nextTick(() => {const temp = []for(let i = start; i <= end; i++) {const tmp = tableData.value[i]if(selectedRows.value.find(r => r.index === tmp.index)) continueif(!checkRowSelectable1(tmp)) continuetemp.push(tmp)}onprogress.value = truefor(let i = 0, len = temp.length; i < len; i++) {if(i === len - 1) onprogress.value = falsetableEl.toggleRowSelection(temp[i], true)}})}// 更新 selectedRowsfunction updateTbSelection(selection) {selectedRows.value = selection}// 清空 selectedRowsfunction clearSelection() {selectedRows.value = []}function checkRowSelectable1(row, rowIndex) {return typeof(checkRowSelectable) === 'function'? checkRowSelectable(row, rowIndex): true}return {selectedRows: readonly(selectedRows),handleTbSelect,handleTbSelectionChange,clearSelection}
}
相关文章:

实现 element-plus 表格多选时按 shift 进行连选的功能
前言 element-plus表格提供了多选功能,可单击勾选一条数据,可全选。 现在有个很合理的需求,希望实现类似于文件系统中shift连续选择功能,并且在表格排序后,依照排序后的顺序连选。 一、el-table 多选表格基本使用 1、…...

华为OD机试真题JAVA实现【考古学家】真题+解题思路+代码(20222023)
🔥系列专栏 华为OD机试(JAVA)真题目录汇总华为OD机试(Python)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出示例一输入输出说明示例二输入输出说明...

Spring3之基于Aspect实现AOP
简介 使用 Aspect 搭配 Spring 可轻松实现 AOP;本章将通过一个完整示例演示如何实现这一功能 实现步骤 修改 beans.xml 配置文件的 schema 部分;可以在 spring-framework-reference.html 文件通过搜索关键字 “/aop” 找到配置 schema,然后…...

buctoj-寒假集训进阶训练赛(二十二)
问题 A: Stones 题目描述 由于自行车状态错误,森普尔开始每天早上从东到西走,每天晚上走回去。走路可能会有点累,所以森普这次总是玩一些游戏。 路上有很多石头,当他遇到一块石头时,如果是他遇到的奇数石头࿰…...

华为OD机试真题JAVA实现【静态扫描最优成本】真题+解题思路+代码(20222023)
🔥系列专栏 华为OD机试(JAVA)真题目录汇总华为OD机试(Python)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出描述示例一输入输出说明示例二输入输出说明...

汽车装配工厂立库物料运送线PLC无线应用
一、应用背景此次项目地在比亚迪的西安工厂,需要实现PLC无线通讯的地方是汽车厂的立体仓库物料运输线。生产物流担负运输、存储、装卸物料等任务。汽车制造业是典型的多工种、多工艺、多物料的大规模生产过程,因此原材料与零部件必需及时准确送至工位&am…...

Python雪花代码
前言 用python画个雪花玩玩,源码在文末公众号哈。 雪花类 class Snow(): #雪花类 def __init__(self): self.r 6 #雪花的半径 self.x ra.randint(-1000,1000) #雪花的横坐标 self.y ra.randint(-500,5…...

Numpy基础与实例——人工智能基础
文章目录一、Numpy概述1. 优势2. numpy历史3. Numpy的核心:多维数组4. 内存中的ndarray对象4.1 元数据(metadata)4.2 实际数据二、numpy基础1. ndarray数组2. arange、zeros、ones、zeros_like3. ndarray对象属性的基本操作3.1 修改数组维度3…...

MQTT的工作原理
介绍MQTT协议的消息模型,消息传输过程,消息发布和订阅。 一、介绍MQTT协议的消息模型 MQTT协议的消息模型被称为“主题”模型。在这种模型中,服务器接收到的消息将通过主题进行分类。客户端可以通过订阅一个或多个主题来接收所需的消息。 1.1 消息主题 1.2 消息内容 1.…...

iOS开发:UINavigationController自定义返回按钮,系统导航支持侧滑返回
当你使用系统导航想拦截用户返回事件时,无法拦截侧滑返回 当你自定义导航或者隐藏导航后,iOS系统导航的侧滑返回就失效了,那么用户体验将大打折扣 网上大部分自定义导航的解决方案是:给页面添加全局的轻扫手势,那么又区别于原生系统,改变了用户的操作习惯 在开发过程中,…...

【Kafka进阶】-- unclean.leader.election.enable参数的内涵
一、背景近期,我们的kafka 消息队列集群(1.x版本)经过了一次事故。某节点意外宕机,导致 log 文件损坏,重启 kafka 失败,最后导致某个 topic 的分区不可用,本文对此做了简单的分析、解决和复现参考,以此为记…...

基于redis实现分布式锁
前言 我们的系统都是分布式部署的,日常开发中,秒杀下单、抢购商品等等业务场景,为了防⽌库存超卖,都需要用到分布式锁。 分布式锁其实就是,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或…...

C#开发的OpenRA动态加载插件DLL里的类实现
C#开发的OpenRA动态加载插件DLL里的类实现 由于这款游戏的设计是为了开源设计, 并且可以让不同个人或团体实现自己的游戏, 那么每个人实现的代码是不一样的,算法也是不一样的。 并且可能也拿不到代码一起编译生成一套运行的代码。 这时候,就要考虑使用动态加载类的功能。 意…...

网站代理是什么?有什么需要注意的?
如今,网站代理已经成为一种不可或缺的经营方式。无论是企业还是个人,都需要通过代理来获得更多的流量和市场份额。 一、网站代理的优势 网站代理的优势在于能够为您提供更加专业、周到的服务。这些优势包括:1.丰富的内容资源,能…...

动态库和静态库的区别
什么是库文件 一般来说,一个程序,通常都会包含目标文件和若干个库文件。经过汇编得到的目标文件再经过和库文件的链接,就能构成可执行文件。库文件像是一个代码仓库或代码组件的集合,为目标文件提供可直接使用的变量、函数、类等…...

C/C++路径去除前缀
在做一些日志输出的工作时,想要获取当前文件名,而不是冗长的文件路径。路径获取往往和各家os底层函数优化。C/C标准中定义了一些预处理宏,可以帮助我们获取文件路径。我们希望能够在编译期而不是在运行期做这个事情,避免额外的性能…...

Vue2之Vue-cli应用及组件基础认识
Vue2之Vue-cli应用及组件基础认识一、Vue-cli1、单页面应用程序2、vue-cli介绍3、安装和使用4、创建项目4.1 输入创建项目4.2 选择第三项,进行自主配置,按回车键即可4.3 选择自己需要的库4.4 选择Vue的版本4.5 选择CSS选择器4.6 选择Babel、ESLint、etc等…...

C 学习笔记 —— 声明、定义、初始化
文章目录声明定义初始化定义和初始化的区别静态变量初始化自动变量初始化声明 说明符表达式列表 int a; char j, k l;定义 一般的情况下,我们把建立空间的声明称之为定义,而把不需要建立存储空间的声明称之为声明。 int tern 1; //定义int main() {…...

机械狗控制算法
一. MIT Cheetah特点 1.驱动器 Cheetah 2采用了定制的本体感受驱动器设计,具有高冲击缓解、力控制和位置控制能力。这种设计使其能够自主跳过障碍物,并以6m/s的高速跳跃,但其运动范围有限,只能进行矢状面运动。 Cheetah 3采用高扭…...

向量与矩阵 导数和偏导数 特征值与特征向量 概率分布 期望方差 相关系数
文章目录向量与矩阵标量、向量、矩阵、张量向量范数和矩阵的范数导数和偏导数特征值和特征向量概率分布伯努利分布正态分布(高斯分布)指数分布期望、⽅差、协⽅差、相关系数期望方差协⽅差相关系数向量与矩阵 标量、向量、矩阵、张量 标量(…...

记录--前端实现登录拼图验证
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 不知各位朋友现在在web端进行登录的时候有没有注意一个变化,以前登录的时候是直接账号密码通过就可以直接登录,再后来图形验证码,数字结果运算验证,…...

【Go语言基础】Go语言中的map集合详细使用(附带源码)
文章目录Go语言中的map集合1-1 定义1-2 map遍历1-3 map集合删除1-4 map是引用类型Go语言中的map集合 Go 语言提供了内置类型 map集合,它将一个值与一个键关联起来,可以使用相应的键检索值。 map是一种集合,可以像遍历数组或切片那样去遍历它…...

C++11 lambda
Lambda 介绍 Lambda 函数也叫匿名函数, 是C 11中新增的特性; 1. Lambda函数的好处 如果你的代码里面存在大量的小函数,而这些函数一般只被调用一次,那么将他们重构成 lambda 表达式。 Lambda函数使代码变得更加紧凑、更加结构化和更富有表现…...

【新】华为OD机试 - 分苹果(Python)
分苹果 题目 AB两个人把苹果分为两堆 A希望按照他的计算规则等分苹果 他的计算规则是按照二级制加法计算 并且不计算进位12+5=9(1100+0101=9), B的计算规则是十进制加法, 包括正常进位,B希望在满足A的情况下获取苹果重量最多 输入苹果的数量和每个苹果重量 输出满足A的情况下…...

Python 模块
Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句。 模块让你能够有逻辑地组织你的 Python 代码段。 把相关的代码分配到一个模块里能让你的代码更好用,更易懂。 模块能定义函数&#…...

gdb调试功能从零到会(Linux详解)
目录 👀 1.安装gdb 👀2.判断是否安装成功 👀3.改成debug方式发布。 👀 4.gdb功能简介 前言 gdb是Linux 下功能全面的调试工具。gdb支持断点、单步执行、打印变量、观察变量、查看寄存器、查看堆栈等调试手段。在Linux环境软件…...

【C语言学习笔记】:数组、指针相关面试题
无特殊说明情况下,下面所有题s目都是linux下的32位C程序。 「1、计算以下sizeof的值。」 char str1[] {a, b, c, d, e}; char str2[] "abcde";char *ptr "abcde";char book[][80]{"计算机应用基础","C语言","C程…...

go语言环境配置 项目启动
一 安装go语言 go语言各个版本之间兼容性比较差。所以可能你需要安装固定的版本 1 安装最新版的go brew install go2 查看go可以安装的版本 brew search go3 安装指定版本的go brew install go1.134 查看安装的go语言的版本 go version5 查看go的安装路径 which go || w…...

Springboot 使用插件 自动生成Mock单元测试 Squaretest
缘起 很多公司对分支单测覆盖率会有一定的要求,比如 单测覆盖率要达到 60% 或者 80%才可以发布。 有时候工期相对紧张,就优先开发功能,测试功能,然后再去补单元测试。 但是编写单元测试又比较浪费时间,有没有能够很大…...

「JVM 执行引擎」栈架构的字节码的解释执行引擎
JVM 执行引擎在执行 Java 代码时有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择; HotSpot 实际的实现中,模版解释器工作时,并不是按照概念模型中进行机械式计…...