实现 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采用高扭…...

向量与矩阵 导数和偏导数 特征值与特征向量 概率分布 期望方差 相关系数
文章目录向量与矩阵标量、向量、矩阵、张量向量范数和矩阵的范数导数和偏导数特征值和特征向量概率分布伯努利分布正态分布(高斯分布)指数分布期望、⽅差、协⽅差、相关系数期望方差协⽅差相关系数向量与矩阵 标量、向量、矩阵、张量 标量(…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...