实现 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采用高扭…...
向量与矩阵 导数和偏导数 特征值与特征向量 概率分布 期望方差 相关系数
文章目录向量与矩阵标量、向量、矩阵、张量向量范数和矩阵的范数导数和偏导数特征值和特征向量概率分布伯努利分布正态分布(高斯分布)指数分布期望、⽅差、协⽅差、相关系数期望方差协⽅差相关系数向量与矩阵 标量、向量、矩阵、张量 标量(…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
