vue3 + element-plus : el-table多选表格,按 shift 可进行连选 – 封装成公共方法

基于大佬的思路,封装成全局方法,以供各个页面使用

更多细节请看原文,原文链接:实现 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>