import {
|
useInfiniteQuery,
|
QueryKey,
|
useMutation,
|
useQueryClient,
|
InfiniteData,
|
RefetchOptions,
|
RefetchQueryFilters,
|
} from '@tanstack/vue-query';
|
import { UnwrapNestedRefs, Ref, ref, reactive, computed } from 'vue';
|
import Taro from '@tarojs/taro';
|
|
export type BaseData<T = any> = {
|
data?: T[];
|
objectData?: any;
|
pageModel?: {
|
rows?: number;
|
page?: number;
|
totalCount?: number;
|
totalPage?: number;
|
};
|
};
|
|
export interface OrderInput {
|
property?: string;
|
order?: number;
|
}
|
|
export type ExtraParams = {
|
[key: string]: any;
|
orderByProperty?: string;
|
};
|
|
export type ServiceContext = {
|
pageParam?: any;
|
signal?: AbortSignal;
|
};
|
|
export type InfiniteGroupOptions = {
|
groupIndex?: number;
|
itemIndex?: number;
|
};
|
|
type Service<TData, TExtraParams extends ExtraParams> = (
|
context: ServiceContext,
|
extraParamsState: UnwrapNestedRefs<TExtraParams>
|
) => Promise<TData>;
|
|
type UseInfiniteLoadingOptions<T, TExtraParams extends ExtraParams> = {
|
/**
|
* @deprecated
|
*/
|
defaultExtraParams?: TExtraParams;
|
enabled?: Ref<boolean> | boolean;
|
queryKey?: QueryKey;
|
onSuccess?: (data: InfiniteData<BaseData<T>>) => void;
|
onRefetch?: () => any;
|
useLocalData?: boolean;
|
refeshDidShow?: boolean;
|
select?: (data: InfiniteData<BaseData<T>>) => InfiniteData<BaseData<T>>;
|
};
|
// QueryFunction<BaseData<T>, (string | DeepUnwrapRef<UnwrapNestedRefs<TExtraParams>>)[], any>
|
export function useInfiniteLoading<T, TExtraParams extends ExtraParams>(
|
service: Service<BaseData<T>, TExtraParams>,
|
options: UseInfiniteLoadingOptions<T, TExtraParams> = {}
|
) {
|
const {
|
defaultExtraParams = {} as TExtraParams,
|
enabled = ref(true),
|
queryKey,
|
onSuccess,
|
onRefetch: _onRefetch,
|
useLocalData = false,
|
refeshDidShow = true,
|
select,
|
} = options;
|
|
const extraParamState = reactive({
|
...defaultExtraParams,
|
});
|
|
const localData = ref({
|
pageParams: [],
|
pages: [],
|
});
|
|
const {
|
data,
|
error,
|
fetchNextPage,
|
hasNextPage,
|
isFetching,
|
isFetchingNextPage,
|
isLoading,
|
isError,
|
refetch,
|
isInitialLoading,
|
fetchPreviousPage,
|
hasPreviousPage,
|
} = useInfiniteQuery({
|
queryKey: queryKey,
|
queryFn: ({ pageParam = 1, signal }) => {
|
return service({ pageParam, signal }, extraParamState);
|
},
|
// initialPageParam: 1,
|
getNextPageParam: (lastPage, pages) => {
|
if (!lastPage) return 1;
|
if (
|
(lastPage.pageModel.page - 1) * lastPage.pageModel.rows + lastPage.data.length <
|
lastPage.pageModel.totalCount &&
|
lastPage.pageModel.totalCount > 0
|
) {
|
return lastPage.pageModel.page + 1;
|
}
|
},
|
getPreviousPageParam: (firstPage, pages) => {
|
if (!firstPage) return 1;
|
if (
|
(firstPage.pageModel.page - 1) * firstPage.pageModel.rows + firstPage.data.length <
|
firstPage.pageModel.totalCount &&
|
firstPage.pageModel.totalCount > 0
|
) {
|
return firstPage.pageModel.page + 1;
|
}
|
},
|
enabled: enabled,
|
onSuccess(data) {
|
// console.log('data2: ', data);
|
localData.value.pageParams = data.pageParams;
|
//@ts-ignore
|
localData.value.pages = data.pages;
|
onSuccess?.(data);
|
},
|
select(data) {
|
return select ? select(data) : data;
|
},
|
});
|
|
const _data = computed<InfiniteData<BaseData<T>>>(() =>
|
useLocalData ? (localData.value as any) : data.value
|
);
|
|
const queryClient = useQueryClient();
|
|
const { mutateAsync } = useMutation({
|
mutationFn: ({ pageParam }: ServiceContext) => fetchNextPage(pageParam),
|
|
// onSettled: async () => {
|
// return await queryClient.invalidateQueries({ queryKey: queryKey });
|
// },
|
});
|
|
const { mutate: setListItem } = useMutation({
|
mutationFn: (data: { dataKey?: string; data: Partial<T> }) => {
|
return Promise.resolve(data);
|
},
|
onSuccess: ({ dataKey = 'id', data }) => {
|
console.log('dataKey: ', data);
|
const pagesArray: InfiniteData<BaseData<T>> = useLocalData
|
? localData.value
|
: queryClient.getQueryData(queryKey);
|
console.log('pagesArray: ', pagesArray);
|
const newPagesArray =
|
pagesArray?.pages.map((page) => {
|
return {
|
...page,
|
data: page.data.map((item) => {
|
if (item[dataKey] === data[dataKey]) {
|
return {
|
...item,
|
...data,
|
};
|
} else {
|
return item;
|
}
|
}),
|
};
|
}) ?? [];
|
if (useLocalData) {
|
console.log('newPagesArray: ', newPagesArray);
|
localData.value.pageParams = pagesArray.pageParams;
|
localData.value.pages = newPagesArray;
|
} else {
|
queryClient.setQueryData(queryKey, () => ({
|
pages: newPagesArray,
|
pageParams: pagesArray.pageParams,
|
}));
|
}
|
},
|
});
|
|
function onRefetch(options?: RefetchOptions & RefetchQueryFilters<unknown>) {
|
_onRefetch?.();
|
return refetch(options);
|
}
|
|
const infiniteLoadingProps = computed(() => ({
|
fetchNextPage,
|
listData: _data.value,
|
flattenListData: flattenListData.value,
|
// error: error.value,
|
hasMore: hasNextPage.value,
|
isFetching: isFetching.value,
|
isFetchingNextPage: isFetchingNextPage.value,
|
isLoading: isLoading.value,
|
isError: !!isError.value,
|
refetch: onRefetch,
|
}));
|
|
const flattenListData = computed(() => {
|
let list: BaseData<T>['data'] = [];
|
if (data && _data.value) {
|
_data.value?.pages.forEach((group) => {
|
group.data.forEach((item) => {
|
list.push(item);
|
});
|
});
|
}
|
return list;
|
});
|
|
if (refeshDidShow) {
|
useRefeshDidShow({ queryKey: queryKey });
|
}
|
|
const infiniteLoadingRef = ref<{ backToTop(): void; scrollToBottom(dis?: number): void }>();
|
|
function setByIndex(options: InfiniteGroupOptions = {}) {
|
refetch({ refetchPage: (page, index) => index === options.groupIndex, type: 'inactive' });
|
}
|
|
function updatePageByIndex(options: InfiniteGroupOptions = {}) {
|
refetch({ refetchPage: (page, index) => index === options.groupIndex, type: 'inactive' });
|
}
|
|
function remove() {
|
queryClient.invalidateQueries({ queryKey: queryKey });
|
}
|
|
function invalidateQueries() {
|
return queryClient.invalidateQueries({ queryKey: queryKey });
|
}
|
|
const listActions = {
|
setByIndex,
|
updatePageByIndex,
|
remove,
|
};
|
|
return {
|
infiniteLoadingProps,
|
flattenListData,
|
extraParamState,
|
mutateAsync,
|
setListItem,
|
cancel: () => queryClient.cancelQueries(queryKey),
|
infiniteLoadingRef,
|
listActions,
|
invalidateQueries,
|
fetchPreviousPage,
|
hasPreviousPage,
|
};
|
}
|
|
export type ListActionsType = {
|
setByIndex: (options?: InfiniteGroupOptions) => void;
|
updatePageByIndex: (options?: InfiniteGroupOptions) => void;
|
remove: () => void;
|
};
|
|
type UseRefeshDidShowOptions = {
|
queryKey: QueryKey;
|
};
|
|
export function useRefeshDidShow({ queryKey }: UseRefeshDidShowOptions) {
|
const queryClient = useQueryClient();
|
const showUpdate = ref(false);
|
|
Taro.useDidShow(() => {
|
if (showUpdate.value) {
|
queryClient.invalidateQueries({
|
queryKey: queryKey,
|
});
|
}
|
});
|
|
Taro.useDidHide(() => {
|
showUpdate.value = true;
|
});
|
|
Taro.useUnload(() => {
|
showUpdate.value = false;
|
});
|
}
|