基于大佬的思路,封装成全局方法,以供各个页面使用
更多细节请看原文,原文链接:实现 element-plus 表格多选时按 shift 进行连选的功能_element-plus表格实现按shift键快捷多选-CSDN博客
1. 新建 elTableMultiSelect.js 文件(我放在 utils 中的)
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 = newVal
tbFlag.value = true
}
}, { deep: true, immediate: true })
onMounted(() => {
mountedFlag.value = true
if(!isSetup) {
tbFlag.value = true
tableEl = 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') return
if(shiftFlag.value) return
shiftFlag.value = true
}
function handleKeyUp({ key }) {
if(key !== 'Shift') return
if(!shiftFlag.value) return
shiftFlag.value = false
lastIdx.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 = idx
return 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._index
return
}
// lastIdx为-1时,记录本次下标
if(lastIdx.value === -1) {
lastIdx.value = row._index
return
}
// lastIdx 有值,自动勾选中间rows
let [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)) continue
if(!checkRowSelectable1(tmp)) continue
temp.push(tmp)
}
onprogress.value = true
for(let i = 0, len = temp.length; i < len; i++) {
if(i === len - 1) onprogress.value = false
tableEl.toggleRowSelection(temp[i], true)
}
})
}
// 更新 selectedRows
function updateTbSelection(selection) {
// selectedRows.value = selection
// keep sequence
if(selection!==undefined){
selectedRows.value = selection.slice(0).sort((a, b) => a._index - b._index)
}
}
// 清空 selectedRows
function clearSelection() {
selectedRows.value = []
}
function checkRowSelectable1(row, rowIndex) {
return typeof(checkRowSelectable) === 'function'
? checkRowSelectable(row, rowIndex)
: true
}
return {
selectedRows: readonly(selectedRows),
handleTbSelect,
handleTbSelectionChange,
clearSelection
}
}
2. main.js 文件
······
import { elTableMultiSelect } from "@/utils/elTableMultiSelect"
app.config.globalProperties.elTableMultiSelect = elTableMultiSelect;
······
3. 使用页面
<el-table
ref="mainTableRef"
@selection-change="handleSelectionChange"
@select="handleTbSelect"
······
>
<el-table-column type="selection" width="55" align="center"/>
<el-table-column ....../>
</el-table>
<script setup>
const {proxy} = getCurrentInstance();
const mainTableRef = ref()
// shift多选
const {
handleTbSelect,
handleTbSelectionChange
} = proxy.elTableMultiSelect(mainTableRef) // 传表格ref对象
// 多选框选中数据
function handleSelectionChange(selection) {
handleTbSelectionChange() // selection 就是选中的数据了
// 其他多选操作...
}
</script>