需求描述
- 最近產品説,某個
el-table要實現按住shift鍵快速勾選功能 - 大概就是仿
windows系統的文件shift按住選中功能 - 反正就是儘可能多的讓用户勾選
- 方便用户快速勾選操作
github完整代碼:https://github.com/shuirongshuifu/vue3-echarts5-example
Windows系統的功能效果圖
- 比如可以向前多選
- 或者向後多選
- 大家可以自己嘗試一下
自己實現的el-table勾選效果圖
實現思路
- 頁面加載好了以後,綁定監聽事件,監聽用户鍵盤按下和抬起事件,看看是不是Shift鍵
- 頁面銷燬時候,再卸載一下
- 搞三個變量記錄是否按下Shift鍵、勾選el-table是第幾行,和再次勾選el-table是第幾行
- 假設第一次勾選的是第四行,第二次勾選的是第七行,只需要把四行和七行中間的五六行控制為勾選即可
- 同理,第一次勾選第七行,第二次勾選第四行也是一樣
- 最後shift鍵抬起的時候,控制把三個變量重置即可
- less word,more code
代碼
先來個el-table
<template>
<el-table ref="multipleTableRef" :data="tableData" style="width: 100%" @select="selectFn" @select-all="selectAllFn">
<el-table-column type="selection" width="55" />
<el-table-column label="Date" width="120" />
<el-table-column property="name" label="Name" width="120" />
<el-table-column property="address" label="Address" show-overflow-tooltip />
</el-table>
</template>
// 數據
import { ref, onMounted, onBeforeUnmount, reactive } from 'vue'
const tableData = ref([
{
id: 1,
date: '2016-05-01',
name: '111',
address: 'No. 189, Grove St, Los Angeles',
},
...
])
再綁定監聽事件
onMounted(() => {
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
});
const onKeyDown = (e) => {
if (e.key === 'Shift') {
clickInfo.isShiftPressed = true;
}
};
const onKeyUp = (e) => {
if (e.key === 'Shift') {
// 鼠標抬起,重置初始狀態
clickInfo.isShiftPressed = false;
clickInfo.startRowIndex = -1
clickInfo.endRowIndex = -1
}
};
定義相關變量信息
clickInfo是收集到的信息
const clickInfo = reactive({
// 開始勾選的索引,初始沒勾選為-1
startRowIndex: -1,
// 結束勾選的索引, 初始沒勾選為-1
endRowIndex: -1,
// 是否按下shift鍵,初始沒有摁下
isShiftPressed: false
})
當然還需要定義表格實例和勾選存儲數組,如下:
const multipleTableRef = ref()
const multipleSelection = ref([])
注意,這裏要使用select和select-all去控制,不使用selection-change事件,因為要更靈活第去控制了,如下:
// 全選
const selectAllFn = (selection) => {
multipleSelection.value = selection
}
// 單選
const selectFn = (selection, row) => {
multipleSelection.value = selection
// Shift相關控制邏輯...
}
Shift勾選控制關鍵代碼
- 全選不用控制
- 控制的邏輯主要在單選這一塊
- 請對着註釋閲讀:
// 單選
const selectFn = (selection, row) => {
multipleSelection.value = selection
// 獲取當前點擊的是第幾行
let i = tableData.value.findIndex((item) => item.id == row.id)
// Shift按下邏輯
if (clickInfo.isShiftPressed) {
// 初始沒勾選,就賦值開始勾選索引
if (clickInfo.startRowIndex === -1) {
clickInfo.startRowIndex = i
}
// 初始勾選了,説明是第二次勾選
else {
// 賦值索引
clickInfo.endRowIndex = i
// 執行把中間段的表格勾選上邏輯
selectTable(clickInfo.startRowIndex, clickInfo.endRowIndex)
}
}
}
// 執行勾選邏輯
const selectTable = (startRowIndex, endRowIndex) => {
// 第一次勾選後,緊接着再次勾選,有可能往前勾選,也有可能往後勾選,所以要做一個大小區分
const startIndex = Math.min(startRowIndex, endRowIndex);
const endIndex = Math.max(startRowIndex, endRowIndex);
// 遍歷去把中間段的勾選上
tableData.value.forEach((rowData, rowIndex) => {
// 若是中間項包含在已勾選的數組中去,就忽略之(這裏我們用id為標識做區分)
if (multipleSelection.value.some((msItem) => msItem.id == rowData.id)) { }
// 若是不在勾選的數組中,在去看看要不勾選
else {
// 因為起始勾選和再次勾選的數據,已經保存到勾選數組中去了,所以不用管
if (rowIndex > startIndex && rowIndex < endIndex) {
// 只需把中間段的狀態置為勾選,並丟到勾選數組中去就行了
multipleTableRef.value.toggleRowSelection(rowData, rowIndex > startIndex && rowIndex < endIndex)
multipleSelection.value.push(rowData)
}
}
})
}
- 至此,需求就算解決了...
- 但是我們想,若是過兩天,另外一個
el-table也需要這個需求功能呢? - 再複製粘貼一份嗎?
- 似乎太麻煩,所以如何優化呢?
- 如何能夠做到複用呢?
hook優化
- 懂的都懂,這裏使用Vue3中的hook會更加合適
- 更方便複用代碼邏輯
什麼是hook
複雜的概念簡單化...
- 説到代碼複用這一塊,我們會想到什麼?
- 哦,有組件的複用,比如封裝一個公共的卡片組件、表單組件
- 哦,有工具函數的複用,比如有一個獲取當前的年月日時分秒的函數
- 哦,Vue2還有
Mixin這個可以概念 - 同樣的,
hook也是一種複用的方式,就是單獨拎出來,哪裏需要哪裏引入,哪裏使用即可
hook代碼
我們思考一下,這個需求的什麼東西可以單獨拎出來呢?
- 那這裏綁定、銷燬鍵盤按下抬起事件可以拎出來
- 收集的
clickInfo信息也可以單獨拎出來 - 甚至於勾選時候控制表格,給表格的
multipleSelection塞值,也可以單獨拎出來
於是乎,我們就可以這樣做了
- 新建一個hook文件夾,用於存放越來越多的hook
- 取個名字,一般用use開頭
useShiftQuickSelect.ts文件 - 拎出來操作,再暴露出去,給外邊用
- 代碼:
hook/useShiftQuickSelect.ts
import { onMounted, onBeforeUnmount, reactive } from 'vue'
export function useShiftQuickSelect() {
onMounted(() => {
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
});
const onKeyDown = (e) => {
if (e.key === 'Shift') {
clickInfo.isShiftPressed = true;
}
};
const onKeyUp = (e) => {
if (e.key === 'Shift') {
// Shift抬起重置
clickInfo.isShiftPressed = false;
clickInfo.startRowIndex = -1
clickInfo.endRowIndex = -1
}
};
const clickInfo = reactive({
startRowIndex: -1,
endRowIndex: -1,
isShiftPressed: false
})
/**
* tableData表格數據、multipleSelection勾選數組,multipleTableRef表格實例
* key用於進行對比的標識字段,一般都是每一行的唯一身份證即id
* */
const ctr = (tableData, multipleSelection, multipleTableRef, key) => {
// 獲取當前點擊的是第幾行
let i = tableData.findIndex((item) => item.id == key)
// Shift按下邏輯
if (clickInfo.isShiftPressed) {
// 初始沒勾選,就賦值開始勾選索引
if (clickInfo.startRowIndex === -1) {
clickInfo.startRowIndex = i
}
// 初始已經勾選,就説明是shift快速勾選
else {
// 索引賦值
clickInfo.endRowIndex = i
// 把開始索引和結束索引進行大小對比
const { startRowIndex, endRowIndex } = clickInfo
const startIndex = Math.min(startRowIndex, endRowIndex);
const endIndex = Math.max(startRowIndex, endRowIndex);
// 遍歷操作
tableData.forEach((rowData, rowIndex) => {
// 若是這一項包含在已勾選的數組中去(已勾選),就忽略之;沒勾選就控制其勾選
if (!multipleSelection.some((msItem) => msItem.id == rowData.id)) {
// 中間段勾選
if (rowIndex > startIndex && rowIndex < endIndex) {
// 改表格狀態並存起來
multipleTableRef.toggleRowSelection(rowData, rowIndex > startIndex && rowIndex < endIndex)
multipleSelection.push(rowData)
}
}
})
}
}
}
return { ctr }
}
- 這裏我暴露一個ctr函數,給外層用,外層只要傳遞進來表格綁定的數據tableData
- 傳進來勾選數組multipleSelection
- 傳進來表格實例multipleTableRef,用於控制表格勾選
- 和區分某一行是否被勾選的字段(比如用id字段來區分判斷)
- 這樣的話,就簡單多了
- 具體多簡單,讓我們看看使用的地方,代碼:
使用hook的代碼
<template>
<el-table ref="multipleTableRef" :data="tableData" style="width: 100%" @select="selectFn" @select-all="selectAllFn">
<el-table-column type="selection" width="55" />
<el-table-column label="Date" width="120">
<template #default="scope">{{ scope.row.date }}</template>
</el-table-column>
<el-table-column property="name" label="Name" width="120" />
<el-table-column property="address" label="Address" show-overflow-tooltip />
</el-table>
<button @click="look">查看已勾選的</button>
</template>
<script setup>
import { ref } from 'vue'
import { useShiftQuickSelect } from "@/hook/useShiftQuickSelect.js";
const tableData = ref([
{
id: 1,
date: '2016-05-01',
name: '111',
address: 'No. 189, Grove St, Los Angeles',
}
])
const multipleTableRef = ref()
const multipleSelection = ref([])
const look = () => {
console.log('multipleSelection', multipleSelection.value);
}
// 全選
const selectAllFn = (selection) => {
multipleSelection.value = selection
}
// 單選
const { ctr } = useShiftQuickSelect()
const selectFn = (selection, row) => {
multipleSelection.value = selection
// hook操作控制函數
ctr(tableData.value, multipleSelection.value, multipleTableRef.value, row.id)
}
</script>
- 看到了叭,只有幾行就行了。設置與可以説,僅通過
ctr函數傳遞一下參數,問題就解決了。 - 大大提升了效率
- hook is yyds...
A good memory is better than a bad pen. Write it down...