表格Table實現(xiàn)前端全選所有功能方案示例(包含非當(dāng)前頁)
前言
最近兩家公司都遇到了全選全頁+批量操作的功能場景,即點擊全選所有的時候需要勾選所有數(shù)據(jù)包括非當(dāng)前頁的。
方案
如果純前端分頁可以參考 antdv.table,一般主流的組件庫都給封裝好了,全選所有時設(shè)置pageSize為無窮大并調(diào)用列表接口得到全量數(shù)據(jù)賦值給selectedRowKeys即可。但是這套方案最大的問題在于點擊全選所有時需要等待后端接口返回,這樣的交互延遲是無法忍受的!且全選所有+批量操作兩次請求的服務(wù)端資源浪費也是巨大的。
因此基于后端分頁的前提,提出了另一套合理解決方案:
通過isAll判斷是否為全選所有,如果是的話配合excludeIds、否則配合includeIds的值完成返顯。最后業(yè)務(wù)中調(diào)用批量操作接口的時候還需要傳篩選項。
實現(xiàn)
使用框架 vue3 + antdv
代碼如下,框架為 vue3
+ antdv
:
// CTable <template> <a-table v-bind="$attrs" :columns="columns" > <template #headerCell="{ column }" v-if="!$slots.headerCell"> <template v-if="column.dataIndex === '_checkbox_'"> <CTableHeaderCheckbox ref="cTableHeaderCheckboxRef" :rowKey="rowKey" :dataSource="dataSource" v-model:isAll="isAll" v-model:includeIds="includeIds" v-model:excludeIds="excludeIds" :judgeToggleIsAll="judgeToggleIsAll" /> </template> </template> <template #bodyCell="{ record, column }" v-if="!$slots.bodyCell"> <template v-if="column.dataIndex === '_checkbox_'"> <CTableBodyCheckbox :record="record" :rowKey="rowKey" :isAll="isAll" :includeIds="includeIds" :excludeIds="excludeIds" :judgeToggleIsAll="judgeToggleIsAll" /> </template> </template> <template v-for="(_, name) in $slots" :key="name" #[name]="slotProps"> <slot :name="name" v-bind="slotProps" v-if="name === 'headerCell' && slotProps.column.dataIndex === '_checkbox_'"> <CTableHeaderCheckbox ref="cTableHeaderCheckboxRef" :rowKey="rowKey" :dataSource="dataSource" v-model:isAll="isAll" v-model:includeIds="includeIds" v-model:excludeIds="excludeIds" :judgeToggleIsAll="judgeToggleIsAll" /> </slot> <slot :name="name" v-bind="slotProps" v-if="name === 'bodyCell' && slotProps.column.dataIndex === '_checkbox_'"> <CTableBodyCheckbox :record="slotProps.record" :rowKey="rowKey" :isAll="isAll" :includeIds="includeIds" :excludeIds="excludeIds" :judgeToggleIsAll="judgeToggleIsAll" /> </slot> <slot :name="name" v-bind="slotProps" v-else></slot> </template> </a-table> </template> <script lang="ts" setup> import { Table, TableColumnProps } from 'ant-design-vue'; import CTableHeaderCheckbox from './CTableHeaderCheckbox.vue'; import CTableBodyCheckbox from './CTableBodyCheckbox.vue'; const props = withDefaults( defineProps<{ columns: TableColumnProps[], allSelection?: { onCheckboxChange:(params) => void, } | null, }>(), { columns: () => [], allSelection: null, }, ); const $attrs = useAttrs() as any; const $slots = useSlots(); const cTableHeaderCheckboxRef = ref(); const columns = computed(() => { if (props.allSelection) { return [ { title: '多選', dataIndex: '_checkbox_', fixed: 'left', width: 48, customHeaderCell: () => ({ class: 'ant-table-checkbox-column' }), }, ...props.columns, ]; } return props.columns; }); // 是否全選所有 const isAll = ref(false); // 未全選所有時勾選數(shù)據(jù) const includeIds = ref<string[]>([]); // 全選所有時反選數(shù)據(jù) const excludeIds = ref<string[]>([]); const rowKey = computed(() => $attrs.rowKey || $attrs['row-key']); const dataSource = computed(() => $attrs.dataSource || $attrs['data-source']); // 表單數(shù)據(jù)可能存在disabled不可選擇狀態(tài),此時需要后端返回enabledTotal幫助判斷 const total = computed(() => $attrs.pagination?.enabledTotal || $attrs.pagination?.total || $attrs.enabledTotal || $attrs.total); // 已勾選總數(shù),幫助業(yè)務(wù)展示 const checkedTotal = computed(() => (isAll.value ? total.value - excludeIds.value.length : includeIds.value.length)); // 當(dāng)選擇數(shù)據(jù)發(fā)生改變時,需要判斷是否切換全選狀態(tài) const judgeToggleIsAll = () => { if (isAll.value && excludeIds.value.length && excludeIds.value.length === total.value) { isAll.value = false; includeIds.value = []; excludeIds.value = []; } if (!isAll.value && includeIds.value.length && includeIds.value.length === total.value) { isAll.value = true; includeIds.value = []; excludeIds.value = []; } }; // 當(dāng)源數(shù)據(jù)發(fā)生改變時,手動重置選擇框狀態(tài) const onResetCheckbox = () => { cTableHeaderCheckboxRef.value.handleMenu({ key: Table.SELECTION_NONE }); }; // 有任何選擇變化時,同步回傳給父組件 watch( [isAll, includeIds, excludeIds], () => { props.allSelection?.onCheckboxChange?.({ isAll: isAll.value, includeIds: includeIds.value, excludeIds: excludeIds.value, checkedTotal: checkedTotal.value, }); }, { deep: true }, ); defineExpose({ onResetCheckbox, }); </script>
判斷slots是否存在headerCell和bodyCell
vue
模版里需要額外判斷slots是否存在headerCell和bodyCell,如果存在的話需要透傳動態(tài)插槽,否則通過具名插槽傳入。
// CTableHeaderCheckbox <template> <a-checkbox :checked="isCurrentChecked" :indeterminate="isCurrentIndeterminate" :disabled="isCurrentDisabled" @change="onCheckboxChange" /> <a-dropdown :disabled="isCurrentDisabled" > <CIcon class="ml-2 cursor-pointer" icon="triangle-down-o" :size="12" color="#C9CCD0" /> <template #overlay> <a-menu @click="handleMenu"> <a-menu-item :key="Table.SELECTION_ALL">全選所有</a-menu-item> <a-menu-item :key="Table.SELECTION_INVERT">反選當(dāng)頁</a-menu-item> <a-menu-item :key="Table.SELECTION_NONE">清空所有</a-menu-item> </a-menu> </template> </a-dropdown> </template> <script lang="ts" setup> import { Table } from 'ant-design-vue'; const props = withDefaults( defineProps<{ rowKey: string, dataSource: any[], isAll: boolean, includeIds: string[], excludeIds: string[], judgeToggleIsAll:() => void, }>(), {}, ); const emit = defineEmits(['update:isAll', 'update:includeIds', 'update:excludeIds']); const isAll = computed({ get: () => props.isAll, set: (val) => { emit('update:isAll', val); }, }); const includeIds = computed({ get: () => props.includeIds, set: (val) => { emit('update:includeIds', val); }, }); const excludeIds = computed({ get: () => props.excludeIds, set: (val) => { emit('update:excludeIds', val); }, }); const isCurrentChecked = computed(() => { const ids = props.dataSource?.map((item) => item[props.rowKey]); if (!ids.length) return false; return isAll.value ? !ids.some((id) => excludeIds.value.includes(id)) : ids.every((id) => includeIds.value.includes(id)); }); const isCurrentIndeterminate = computed(() => { const ids = props.dataSource?.map((item) => item[props.rowKey]); if (!ids.length) return false; if (isAll.value) { return !ids.every((id) => excludeIds.value.includes(id)) && !isCurrentChecked.value; } else { return ids.some((id) => includeIds.value.includes(id)) && !isCurrentChecked.value; } }); const isCurrentDisabled = computed(() => !props.dataSource?.map((item) => item[props.rowKey]).length); const handleMenu = ({ key }) => { const ids = props.dataSource?.map((item) => item[props.rowKey]); if (key === Table.SELECTION_INVERT) { // 數(shù)學(xué)意義的補集 if (isAll.value) { excludeIds.value = [ ...excludeIds.value.filter((id) => !ids.includes(id)), ...ids.filter((id) => !excludeIds.value.includes(id)), ]; } else { includeIds.value = [ ...includeIds.value.filter((id) => !ids.includes(id)), ...ids.filter((id) => !includeIds.value.includes(id)), ]; } props.judgeToggleIsAll(); } else { isAll.value = key === Table.SELECTION_ALL; includeIds.value = []; excludeIds.value = []; } }; const onCheckboxChange = (e) => { const ids = props.dataSource?.map((item) => item[props.rowKey]); const { checked } = e.target; if (isAll.value) { excludeIds.value = checked ? excludeIds.value.filter((id) => !ids.includes(id)) : Array.from(new Set([...excludeIds.value, ...ids])); } else { includeIds.value = checked ? Array.from(new Set([...includeIds.value, ...ids])) : includeIds.value.filter((id) => !ids.includes(id)); } props.judgeToggleIsAll(); }; defineExpose({ handleMenu, }); </script>
CTableBodyCheckbox
// CTableBodyCheckbox <template> <a-checkbox :checked="isAll ? !excludeIds.includes(record[rowKey]) : includeIds.includes(record[rowKey])" :disabled="record.disabled" @change="onCheckboxChange(record[rowKey])" /> </template> <script lang="ts" setup> const props = withDefaults( defineProps<{ record: any, rowKey: string, isAll: boolean, includeIds: string[], excludeIds: string[], judgeToggleIsAll:() => void, }>(), {}, ); const onCheckboxChange = (keyId) => { const ids = props.isAll ? props.excludeIds : props.includeIds; const index = ids.indexOf(keyId); if (~index) { ids.splice(index, 1); } else { ids.push(keyId); } props.judgeToggleIsAll(); }; </script>
結(jié)論
如此一來,展示和交互邏輯就全部收攏在前端了,對于交互體驗和服務(wù)端負載都是極大的改善。
以上就是表格Table實現(xiàn)前端全選所有功能方案示例的詳細內(nèi)容,更多關(guān)于Table表格全選功能的資料請關(guān)注腳本之家其它相關(guān)文章!
以上就是表格Table實現(xiàn)前端全選所有功能方案示例(包括非當(dāng)前頁)的詳細內(nèi)容,更多關(guān)于Table表格全選功能的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue.js實現(xiàn)格式化時間并每秒更新顯示功能示例
這篇文章主要介紹了vue.js實現(xiàn)格式化時間并每秒更新顯示功能,結(jié)合實例形式分析了vue.js時間格式化顯示與基于定時器進行實時更新的相關(guān)操作技巧,需要的朋友可以參考下2018-07-07Vue?+?SpringBoot?實現(xiàn)文件的斷點上傳、秒傳存儲到Minio的操作方法
這篇文章主要介紹了Vue?+?SpringBoot?實現(xiàn)文件的斷點上傳、秒傳存儲到Minio的操作方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-06-06Vue+tracking.js 實現(xiàn)前端人臉檢測功能
這篇文章主要介紹了Vue+tracking.js 實現(xiàn)前端人臉檢測功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04在 Vue 3 中設(shè)置 `@` 指向根目錄的幾種常見方法匯總
在 Vue 3 項目開發(fā)中,為了方便管理和引用文件路徑,設(shè)置 @ 指向根目錄是一項常見的需求,下面給大家分享在Vue3中設(shè)置 `@` 指向根目錄的方法匯總,感興趣的朋友一起看看吧2024-06-06