import type { UnwrapRef } from 'vue' import { isReactive, isRef, ref, watch, unref, reactive } from 'vue' import type { MaybeRef } from '@/typing' export interface PageInfo { current: number; pageSize: number; total: number; [key: string]: any; } export interface ReponseData { data: T[]; success?: boolean; total?: number; [key: string]: any; } export type RequestParams = | { pageSize: number; current: number; [key: string]: any; } | undefined; export interface UseFetchDataAction> { cancel: () => void; reload: () => Promise; resetPageIndex: () => void; reset: () => void; context: Context; setPageInfo: (pageInfo: Partial) => void; } export interface Context> { current: number; pageSize: number; dataSource: T['data']; loading: boolean; total: number; requestParams?: MaybeRef<{ [key: string]: any; }>; [key: string]: any; } export const defaultContext: Context = { loading: false, current: 1, pageSize: 20, total: 0, dataSource: [], }; // 如果请求数据中,没有分页,仅单列表数据,可以使用该方法进行包装,免去重复写请求方法 export const wrap = (req: () => Promise): (() => Promise>) => { return () => req().then(res => { const data = res; return { data, total: data.length, success: data !== null && data !== undefined, }; }); }; const filterNoValidValue = (obj: Record = {}) => { const newObj = {}; Object.keys(obj).forEach(k => { if (obj[k] !== undefined && obj[k] !== '' && obj[k] !== null) { newObj[k] = obj[k]; } }); return newObj; }; export const useFetchData = >( getData: (params?: RequestParams) => Promise, context: MaybeRef<{ current?: number; pageSize?: number; dataSource?: T['data']; loading?: boolean; requestParams?: MaybeRef<{ [key: string]: any; }>; [key: string]: any; }> = reactive(defaultContext), options?: { current?: number; pageSize?: number; onLoad?: (dataSource: T['data']) => void; onRequestError?: (e: Error) => void; pagination?: boolean; }, ): UseFetchDataAction => { const state = reactive({} as Context); const mergeContext = isReactive(context) || isRef(context) ? context : ref(context); watch( mergeContext, () => { Object.assign(state, defaultContext, unref(context)); }, { immediate: true }, ); const fetchList = async () => { // 请求中禁止重复请求 if (state.loading) { return; } state.loading = true; const { pageSize = 20, current = 1 } = state; try { const params: RequestParams = options?.pagination !== false ? { current, pageSize, ...filterNoValidValue(unref(mergeContext).requestParams), } : undefined; const { data, success, total: dataTotal = 0 } = await getData(params); state.loading = false; if (success !== false) { state.dataSource = data as UnwrapRef; state.total = dataTotal; } } catch (e) { state.loading = false; // 如果没有传递这个方法的话,需要把错误抛出去,以免吞掉错误 if (options?.onRequestError === undefined) { throw new Error(e); } else { options.onRequestError(e); } } }; const cancel = () => {}; const reset = () => {}; const reload = (): Promise => { return new Promise(resolve => { resolve(fetchList()); }); }; const setPageInfo = (pageInfo: Partial) => { console.warn('setPageInfo 废弃,请直接使用响应式 context'); pageInfo.current && (state.current = pageInfo.current); pageInfo.pageSize && (state.pageSize = pageInfo.pageSize); }; const resetPageIndex = (): void => { console.warn('resetPageIndex 废弃,请直接使用响应式 context'); // state.current = 1; }; watch( [() => state.current, () => state.pageSize, () => unref(mergeContext).requestParams], () => { fetchList().catch(e => { throw new Error(e); }); }, { immediate: true, deep: true }, ); return { cancel, reset, reload, resetPageIndex, setPageInfo: info => { setPageInfo({ current: state.current, pageSize: state.pageSize, ...info, }); }, context: state, }; };