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]
}