UNPKG

@twotwoba/vv-cli

Version:

Easily create Vite + React19/Vue3 web/h5/mini-program/chrome-extension projects.

185 lines (171 loc) 6.1 kB
import { useQuery, useMutation, useInfiniteQuery, type UseQueryOptions, type UseMutationOptions, type QueryKey } from "@tanstack/react-query" import type { FetchError } from "./server-helper" import { fetcher, type FetcherOptions } from "./fecher" // ============================================================================ // Query Factory - 用于创建查询 hooks // ============================================================================ type QueryOptions<TData, TParams> = Omit< UseQueryOptions<TData, FetchError, TData, QueryKey>, "queryKey" | "queryFn" > & { params?: TParams } /** * 创建查询 Hook 工厂函数 * * @template TData 响应数据类型 * @template TParams 请求参数类型 * @param endpoint API 端点 * @param baseOptions 基础配置 * * @example * // 定义 API * export const useUser = createQuery<User>('/api/user') * export const useUserById = createQuery<User, { id: string }>('/api/user') * * // 使用 * const { data, isLoading } = useUser() * const { data } = useUserById({ params: { id: '123' } }) */ export const createQuery = < TData = unknown, TParams extends Record<string, unknown> = Record<string, unknown> >( endpoint: string, baseOptions?: FetcherOptions ) => { return (options?: QueryOptions<TData, TParams>) => { const { params, ...queryOptions } = options ?? {} return useQuery<TData, FetchError>({ queryKey: params ? [endpoint, params] : [endpoint], queryFn: () => fetcher<TData>(endpoint, { ...baseOptions, method: "GET", params: params as Record<string, unknown> }), ...queryOptions }) } } // ============================================================================ // Mutation Factory - 用于创建变更 hooks // ============================================================================ type MutationOptions<TData, TBody> = Omit< UseMutationOptions<TData, FetchError, TBody>, "mutationFn" > /** * 创建变更 Hook 工厂函数 * * @template TData 响应数据类型 * @template TBody 请求体类型 * @param endpoint API 端点 * @param method HTTP 方法 * @param baseOptions 基础配置 * * @example * // 定义 API * export const useCreateUser = createMutation<User, CreateUserDTO>('/api/user', 'POST') * export const useUpdateUser = createMutation<User, UpdateUserDTO>('/api/user', 'PUT') * export const useDeleteUser = createMutation<void, { id: string }>('/api/user', 'DELETE') * * // 使用 * const { mutate, mutateAsync, isPending } = useCreateUser() * mutate({ name: 'John', email: 'john@example.com' }) */ export const createMutation = <TData = unknown, TBody = unknown>( endpoint: string, method: "POST" | "PUT" | "DELETE" | "PATCH" = "POST", baseOptions?: Omit<FetcherOptions, "method"> ) => { return (options?: MutationOptions<TData, TBody>) => { return useMutation<TData, FetchError, TBody>({ mutationFn: (body: TBody) => fetcher<TData>(endpoint, { ...baseOptions, method, body: body as Record<string, unknown> }), ...options }) } } // ============================================================================ // Infinite Query Factory - 用于创建无限加载 hooks // ============================================================================ interface PageParam { current: number pageSize?: number } interface InfiniteQueryOptions<TData, TParams> { params?: TParams pageSize?: number enabled?: boolean getNextPageParam?: (lastPage: TData, allPages: TData[]) => PageParam | undefined } /** * 创建无限查询 Hook 工厂函数 * * @template TData 单页数据类型 * @template TParams 额外请求参数类型 * @param endpoint API 端点 * @param baseOptions 基础配置 * * @example * // 定义 API(假设返回 { list: User[], total: number }) * export const useUserList = createInfiniteQuery<{ list: User[], total: number }>('/api/users') * * // 使用 * const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useUserList({ * params: { status: 'active' }, * pageSize: 20 * }) */ export const createInfiniteQuery = < TData = unknown, TParams extends Record<string, unknown> = Record<string, unknown> >( endpoint: string, baseOptions?: FetcherOptions ) => { return (options?: InfiniteQueryOptions<TData, TParams>) => { const { params, pageSize = 10, getNextPageParam, ...queryOptions } = options ?? {} return useInfiniteQuery<TData, FetchError, TData, QueryKey, PageParam>({ queryKey: params ? [endpoint, "infinite", params] : [endpoint, "infinite"], queryFn: ({ pageParam }) => fetcher<TData>(endpoint, { ...baseOptions, method: "GET", params: { ...params, current: pageParam.current, pageSize: pageParam.pageSize ?? pageSize } as Record<string, unknown> }), initialPageParam: { current: 1, pageSize }, getNextPageParam: getNextPageParam ?? ((lastPage, allPages) => { // 默认实现:假设返回数据有 total 字段 const page = lastPage as { total?: number; list?: unknown[] } const loadedCount = allPages.reduce((acc, p) => { const items = (p as { list?: unknown[] }).list return acc + (items?.length ?? 0) }, 0) if (page.total && loadedCount < page.total) { return { current: allPages.length + 1, pageSize } } return undefined }), ...queryOptions }) } }