写在前面#
对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的Hooks.
本篇文章为useTableData.js
/useTableData.ts
基于个人项目环境进行封装的Hooks,仅以本文介绍封装Hooks思想心得,故相关代码可能不适用他人
项目环境#
Vue3.x + Ant Design Vue3.x + Vite3.x
封装分解:声明变量#
import { ref, shallowRef, isRef } from 'vue';
const loading = ref(false); // 表格数据UI交互Loading
const tableData = shallowRef([]); // 表格数据ShallowRef全响应式
const totalElements = ref(0); // 总数据量totalElements
无论是使用过Ant Design Vue 还是Element UI 亦或是其他UI FrameWork 框架,表格里需要的必定有tableData、totalElements。
封装分解:请求接口#
以下代码仅解释封装思想
const getTableData = async() => {
***
const { data ||
data: { content, totalElements: total }
} = await API(QueryParams) // 请求接口,
***
tableData.value = data ||
data.content ||
data.content.map(item => {***}) //解构data进行赋值
totalElements.value = total
}
接口请求表格数据,最基础的”增删改查”之一,针对后端提供接口和相关代码产出规范约定俗成后,封装Hooks同时考虑,tableData是否能够直接由data解构的content赋值,或需要进行二次处理
封装分解:筛选查询 及重置查询#
const search = async () => {
queryParams.value.pageIndex = 1; // 根据筛选想查询数据,重置页码为1,调用接口
await getTableData();
};
const resetSearch = async () => {
if (isFunction(options.resetParams)) {
options.resetParams(); // 重置请求参数
await getTableData();
}
};
封装分解:获取分页器配置#
以下封装基于项目环境Ant Design Vue 3.x
const getPaginationOptions = () => {
return {
total: totalElements.value,
current: queryParams.value.pageIndex,
pageSize: queryParams.value.pageSize,
showQuickJumper: true,
showSizeChanger: true,
showTotal: _total => `共 ${_total} 条数据`,
};
};
封装分解:表格change事件(分页、排序、筛选变化触发)#
以下封装基于项目环境Ant Design Vue 3.x
const onTableChange = async (page, _, sorter) => { // page: 页码, sorter: 排序字段
queryParams.value.pageIndex = page.current || 1;
queryParams.value.pageSize = page.pageSize || 10;
if (sorter && Object.keys(sorter).length > 0) {
if (sorter.order) {
queryParams.value.orderBy = sorter.field; // asc \ desc
queryParams.value.direction = sorter.order.slice(0, -3);
} else {
queryParams.value.orderBy = '';
queryParams.value.direction = '';
}
}
await getTableData();
};
useTableData.js完整代码#
import { ref, shallowRef, isRef } from 'vue';
import { isFunction } from 'lodash-es';
export function useTableData(apiInterface, queryParams, options = {}) {
if (!isRef(queryParams)) throw new Error('queryParams 参数必修为 Ref 类型');
const loading = ref(false);
const tableData = shallowRef([]);
const totalElements = ref(0);
const getTableData = async () => {
try {
loading.value = true;
const {
data: { content, totalElements: total },
} = await apiInterface(queryParams.value);
if (isFunction(options.handleContent)) {
tableData.value = content.map(options.handleContent);
} else {
tableData.value = content;
}
if (isFunction(options.callback)) {
options.callback(content);
}
totalElements.value = total;
} finally {
loading.value = false;
}
};
const onTableChange = async (page, _, sorter) => {
queryParams.value.pageIndex = page.current || 1;
queryParams.value.pageSize = page.pageSize || 10;
if (sorter && Object.keys(sorter).length > 0) {
if (sorter.order) {
queryParams.value.orderBy = sorter.field;
queryParams.value.direction = sorter.order.slice(0, -3);
} else {
queryParams.value.orderBy = '';
queryParams.value.direction = '';
}
}
await getTableData();
};
const search = async () => {
queryParams.value.pageIndex = 1;
await getTableData();
};
const resetSearch = async () => {
if (isFunction(options.resetParams)) {
options.resetParams();
await getTableData();
}
};
const getPaginationOptions = () => {
return {
total: totalElements.value,
current: queryParams.value.pageIndex,
pageSize: queryParams.value.pageSize,
showQuickJumper: true,
showSizeChanger: true,
showTotal: _total => `共 ${_total} 条数据`,
};
};
return {
loading,
totalElements,
tableData,
search,
resetSearch,
getPaginationOptions,
getTableData,
onTableChange,
};
}
后记 | useTableData.ts完整代码#
基于来自掘金的读者反馈,现更新ts版本的代码,以供使用,原文已说明相关封装思路~
import { ref, shallowRef, Ref } from 'vue';
import { isFunction } from 'lodash-es';
import { TableProps } from 'ant-design-vue';
interface Options {
handleContent?(content: Record<string, unknown>[]): void;
callback?(content: Record<string, unknown>[]): void;
resetParams?(): void;
contentKey?: string;
totalElementsKey?: string;
pageSizeOptions?: string[];
}
export function useTableData(apiInterface: Function, queryParams: Ref<Record<string, unknown>>, options?: Options) {
const loading = ref<boolean>(false);
const tableData = shallowRef<Record<string, unknown>[]>([]);
const totalElements = ref<number>(0);
const getTableData = async () => {
try {
loading.value = true;
const { data } = await apiInterface(queryParams.value);
const content = data[options?.contentKey ? options.contentKey : 'content'];
if (isFunction(options?.handleContent)) {
tableData.value = content.map(options?.handleContent);
} else {
tableData.value = content;
}
isFunction(options?.callback) && options?.callback(content);
totalElements.value = options?.totalElementsKey ? data[options.totalElementsKey] : data.totalElements;
} finally {
loading.value = false;
}
};
const onTableChange: TableProps['onChange'] = async (page, _, sorter) => {
queryParams.value.pageIndex = page.current || 1;
queryParams.value.pageSize = page.pageSize || 10;
if (sorter && Object.keys(sorter).length > 0) {
// @ts-ignore
if (sorter.order) {
// @ts-ignore
queryParams.value.orderBy = sorter.field as string;
// @ts-ignore
queryParams.value.direction = sorter.order.slice(0, -3) as string;
} else {
queryParams.value.orderBy = '';
queryParams.value.direction = '';
}
}
await getTableData();
};
const search = async () => {
queryParams.value.pageIndex = 1;
await getTableData();
};
const resetSearch = async () => {
if (isFunction(options?.resetParams)) {
options?.resetParams();
await getTableData();
}
};
const getPaginationOptions = () => {
return {
total: totalElements.value,
current: queryParams.value.pageIndex,
pageSize: queryParams.value.pageSize,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: options?.pageSizeOptions ? options.pageSizeOptions : ['10', '20', '30', '40'],
showTotal: _total => `共 ${_total} 条数据`,
};
};
return {
loading,
totalElements,
tableData,
getTableData,
onTableChange,
search,
resetSearch,
getPaginationOptions,
};
}