import { defaults, get, has, toArray } from 'lodash-es' import type { Ref } from 'vue' import { reactive, ref } from 'vue' import { usePagination } from './usePagination' type keyPath = Array<string> | string /** * 表格 Hook * @param api 返回Promise的接口函数 * @param params 接口调用传递的参数 * @param options 配置接口数据路径和加载时机 * @returns 返回一个数组 [data, refresh, pagination, table],分别是列表数据数组、刷新数据方法、分页属性、和表格属性 * @example * <el-table :data="tableData" style="width: 100%" v-loading="table.loading" tooltip-effect="light" row-key="qid" @row-click="table.onCurrentChange" @sort-change="table.onSortChange" border></el-table> * @example * <el-pagination v-model:currentPage="pagination.current" v-model:page-size="pagination.pageSize" * :total="pagination.total" small background @size-change="pagination.onSizeChange" :page-sizes="[10, 20, 50, 100, 200, 500]" @current-change="pagination.onPageChange" layout="total, sizes, prev, pager, next, jumper" /> * @example * // 搜索参数(函数可以动态获取参数) * const queryParams = () => { const queryParams = executeCode(requestObject?.request[':params'], requestObject?.request.params) return queryParams?.value } // 加载列表数据 * const listApi = (params: Object) => { * const { api, moduleName } = selectApiByUserRole([QuestionGroupJwApi, QuestionBaseManagementSchoolApi, QuestionBaseManagementTeacherApi, , QuestionGroupExpertApi]); * return getAPI(api)[`api${moduleName}FindAllQuestionBasePost`](params); * }; * // 将逗号分隔的字符串变成数组 * const format = (item: any) => { * item.gidArr = item.gid?.split(',') * return item; * } * const [tableData, refresh, pagination, table] = useTable(listApi, queryParams, { * path: { * data: 'data.result.items', * total: 'data.result.total', * page: 'page', * size: 'pageSize', * }, * immediate: true * }, format); */ export function useTable<T>( api: (params: object) => Promise<T>, params: object | (() => object) = {}, options: { path?: { data?: keyPath; total?: keyPath; page?: string; size?: string } immediate?: boolean } = {}, format?: (item: object) => object, ): [Ref<Array<object>>, (extraData?: object | (() => object)) => object, Pagination, Table] { defaults(options, { path: { data: 'data.list', total: 'data.total', page: 'pageNum', size: 'pageSize', }, immediate: false, }) // 使用()=>fn()而不是fn()区别在于后者只是一个值且立即执行 const [pagination, , , setTotal] = usePagination((extraData?: object) => extraData ? refresh(extraData) : refresh(), ) const data = ref<Array<object>>([]) const table = reactive({ loading: false, curData: { id: '', }, // 当前选中数据 // 按属性进行排序 onSortChange: (column: Column) => { refresh({ field: column.prop, order: column.order }) }, // 改变当前选中的行 onCurrentChange: (val: TableRow = { id: '' }) => { table.curData = val }, }) const refresh = (extraData?: object | (() => object)): object => { table.onCurrentChange() // 接口刷新就重置当前选中 const requestData = { [options?.path?.page as string]: pagination.current, [options?.path?.size as string]: pagination.size, } if (extraData) { if (typeof extraData === 'function') { Object.assign(requestData, extraData()) } else { Object.assign(requestData, extraData) } } if (params) { if (typeof params === 'function') { Object.assign(requestData, params()) } else { Object.assign(requestData, params) } } // 添加查询参数 table.loading = true return api(requestData) .then((res) => { const dataPath = options.path?.data const totalpath = options.path?.total if (!dataPath || !totalpath) { return [] } const rData = get(res, dataPath, []) // 对后端返回的数据进行格式化处理 if (typeof format === 'function') { data.value = rData.map((item: object) => format(item)) } else { data.value = rData } setTotal(get(res, totalpath, 0)) // 友好提示 if (!has(res, dataPath) || !has(res, totalpath)) { console.warn('useTable:响应数据缺少所需字段') } }) .finally(() => { table.loading = false }) } // 立即执行 if (options!.immediate) { refresh() } return [data, refresh, pagination, table] } 扩展阅读 如何封装一个超级好用的 Hook ! usePagination()