公共Hooks封装之表格选择操作useTableRowSelection
项目环境
Vue3.x + Ant Design Vue3.x + Vite3.x
封装思考:为什么封装 useTableRowSelection.js
首先, 基于 Hooks(useTableData.js
、useQueryParams.js
)的封装,作为管理后台表格常见操作的批量删除、批量编辑相关,封装useTableRowSelection.js
并使用,可以仅在页面 table 组件中绑定的 :row-selection="rowSelection"
进行处理即可,不用在每个单页面文件中写表格选择的重复代码。
另外,我们可以在Ant Design Vue
或Ant Design
官方的组件文档中,看到 Table 组件的选择示例,在进行翻页后,先前选择的数据就被清除了。好吧,现在官方已经能够提供这个能力了,泪奔~
PS: 至少 2022 年 8 月份肯定没有这个能力,因为我的 Hooks 在那个时候封装的,只是最近稍微空闲些,才又开始写文章~
封装分解:表格多选
由于封装本 Hooks 时,官方未提供对应跨页多选的能力,而现在官方已经提供了相关能力,故相关代码仅介绍封装思想。
多选时,将需要进行跨页选择的表格,对于已选择的数据进行缓存。checkBox 需要考虑选择和全选(每个分页表头的全选)功能。
对于已选择的 selectKeys
、 selectItems
进行处理,并暴露出来,之后提供给需要表格选择的页面进行数据处理,包括但不限于请求业务接口需要进行的传参,或回显。
// 选择
onSelect: (record, selected) => {
if (selected) {
selectKeys.value.push(record[rowId]);
if ($cacheItem) selectItems.value = [...selectItems.value, ...[record]];
} else {
const index = selectKeys.value.findIndex(key => key === record[rowId]);
if (index >= 0) {
selectKeys.value.splice(index, 1);
if ($cacheItem) {
const $cacheSelectItems = selectItems.value;
$cacheSelectItems.splice(index, 1);
selectItems.value = [...$cacheSelectItems];
}
}
}
},
// 全选
onSelectAll: selected => {
if (selected) {
tableData.value.forEach(item => {
const index = selectKeys.value.findIndex(id => item[rowId] === id);
if (index < 0) {
selectKeys.value.push(item[rowId]);
if ($cacheItem) selectItems.value = [...selectItems.value, ...[item]];
}
});
} else {
tableData.value.forEach(item => {
const index = selectKeys.value.findIndex(key => key === item[rowId]);
if (index >= 0) {
selectKeys.value.splice(index, 1);
if ($cacheItem) {
const $cacheSelectItems = selectItems.value;
$cacheSelectItems.splice(index, 1);
selectItems.value = [...$cacheSelectItems];
}
}
});
}
},
封装思考:完整的表格选择 Hooks
一开始出于和useTableData
、useQueryParams
相关表格 Hooks 的使用目的,减少项目内重复代码的考虑。
完整的useTableRowSelection
应该需要同时考虑表格内多选和单选。虽然多选能实现单选的功能但是页面内多选和单选的 UI 呈现方式还是有区别的~,因此 Hooks 接收表格源数据tableData
、筛选类型selectType
、唯一 KeyrowId
以及是否开启缓存(即跨页筛选,毕竟不是所有的表格都需要跨页筛选)cacheItem = false
。
在这里,封装的 Hooks,和现有官方提供的能力有一点点的不同,官方文档内表格的筛选是跨页选择的,考虑实际业务(当然仅个人公司涉及业务场景考虑,可能不是通用的),仅在需要进行选择人员参加某实际业务项目或调整相关可见范围等需求。
封装分解:暴露 clearKeys、isEmptyKeys
对外暴露 clearKeys 方法,用于清除表格筛选,对外暴露 isEmptyKeys 方法,用于判断表格的筛选是否为空
useTableRowSelection.js 完整代码
import { computed, ref, shallowRef, isRef } from "vue";
import { ROW_SELECT_TYPE } from "@/enums";
export function useTableRowSelection(
tableData,
selectType,
rowId,
cacheItem = false
) {
if (!isRef(tableData)) throw new Error("参数 tableData 必须为 Ref 类型");
const selectKeys = ref([]);
const selectItems = shallowRef([]);
const rowSelection = computed(() => {
const $selectType = isRef(selectType) ? selectType.value : selectType;
const $cacheItem = isRef(cacheItem) ? selectType.value : cacheItem;
if ($selectType === ROW_SELECT_TYPE.CHECKBOX) {
return {
onSelect: (record, selected) => {
if (selected) {
selectKeys.value.push(record[rowId]);
if ($cacheItem)
selectItems.value = [...selectItems.value, ...[record]];
} else {
const index = selectKeys.value.findIndex(
(key) => key === record[rowId]
);
if (index >= 0) {
selectKeys.value.splice(index, 1);
if ($cacheItem) {
const $cacheSelectItems = selectItems.value;
$cacheSelectItems.splice(index, 1);
selectItems.value = [...$cacheSelectItems];
}
}
}
},
onSelectAll: (selected) => {
if (selected) {
tableData.value.forEach((item) => {
const index = selectKeys.value.findIndex(
(id) => item[rowId] === id
);
if (index < 0) {
selectKeys.value.push(item[rowId]);
if ($cacheItem)
selectItems.value = [...selectItems.value, ...[item]];
}
});
} else {
tableData.value.forEach((item) => {
const index = selectKeys.value.findIndex(
(key) => key === item[rowId]
);
if (index >= 0) {
selectKeys.value.splice(index, 1);
if ($cacheItem) {
const $cacheSelectItems = selectItems.value;
$cacheSelectItems.splice(index, 1);
selectItems.value = [...$cacheSelectItems];
}
}
});
}
},
getCheckboxProps: (record) => {
return {
disabled: record.disabled,
};
},
selectedRowKeys: selectKeys.value,
type: ROW_SELECT_TYPE.CHECKBOX,
};
} else {
return {
onSelect: (record) => {
selectKeys.value = [record[rowId]];
if ($cacheItem) selectItems.value = [record];
},
selectedRowKeys: selectKeys.value,
getCheckboxProps: (record) => {
return {
disabled: record.disabled,
};
},
type: ROW_SELECT_TYPE.RADIO,
};
}
});
const clearKeys = () => {
selectKeys.value.length = 0;
selectItems.value = [];
};
const isEmptyKeys = () => {
return selectKeys.value.length === 0;
};
return {
selectItems,
selectKeys,
rowSelection,
clearKeys,
isEmptyKeys,
};
}
实际使用:参考示例
- 页面组件
Page.vue
<a-table
size="small"
class="mt-2"
row-key="id"
:data-source="tableData"
:loading="loading"
:columns="columns"
:row-selection="rowSelection"
@change="onTableChange"
/>
- 具体使用
const { rowSelection, selectKeys, clearKeys, isEmptyKeys } =
useTableRowSelection(tableData, ROW_SELECT_TYPE.CHECKBOX, "id");
const confirmSelect = () => {
if (isEmptyKeys()) {
// 确认按钮,业务场景,表格筛选项非空校验
proxy.$message.warning("请选择****");
return;
}
const selectItem = tableData.value.find(
(item) => item.id === selectKeys.value[0]
);
emits("on-select", {
id: selectItem.id,
});
closeModal();
};
const closeModal = () => {
clearKeys();
resetParams(); // 还记得这个吗~`useQueryParams.js`Hooks内提供的方法~
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南