import { ref, type Ref } from 'vue' import { useToast } from './useToast' import { parseApiError } from '@/utils/errorParser' /** * 异步操作通用逻辑 * * 统一处理: * - Loading 状态管理 * - try/catch 错误处理 * - Toast 通知(成功/失败) * - 可选的成功/失败回调 * * @example * ```typescript * const { loading, execute } = useAsyncAction() * * // 简单用法 * await execute(() => api.deleteItem(id), { * successMessage: '删除成功', * }) * * // 带回调 * await execute(() => api.createItem(data), { * successMessage: '创建成功', * onSuccess: (result) => { * router.push(`/items/${result.id}`) * }, * }) * * // 自定义错误消息 * await execute(() => api.updateItem(id, data), { * successMessage: '更新成功', * errorMessage: '更新失败,请重试', * }) * ``` */ export interface UseAsyncActionOptions { /** 成功时显示的消息 */ successMessage?: string /** 成功消息的标题 */ successTitle?: string /** 失败时显示的消息(如果不提供,将解析 API 错误) */ errorMessage?: string /** 失败消息的标题 */ errorTitle?: string /** 成功时的回调 */ onSuccess?: (result: T) => void /** 失败时的回调 */ onError?: (error: unknown) => void /** 是否在失败时显示 toast(默认 true) */ showErrorToast?: boolean /** 是否在成功时显示 toast(默认:有 successMessage 时为 true) */ showSuccessToast?: boolean } export interface UseAsyncActionReturn { /** 是否正在执行 */ loading: Ref /** 执行异步操作 */ execute: ( action: () => Promise, options?: UseAsyncActionOptions ) => Promise } export function useAsyncAction(): UseAsyncActionReturn { const loading = ref(false) const { success, error: showError } = useToast() async function execute( action: () => Promise, options?: UseAsyncActionOptions ): Promise { const { successMessage, successTitle, errorMessage, errorTitle = '错误', onSuccess, onError, showErrorToast = true, showSuccessToast, } = options || {} loading.value = true try { const result = await action() // 显示成功消息 const shouldShowSuccess = showSuccessToast ?? !!successMessage if (shouldShowSuccess && successMessage) { success(successMessage, successTitle) } // 调用成功回调 onSuccess?.(result) return result } catch (error) { // 解析错误消息 const message = errorMessage || parseApiError(error, '操作失败') // 显示错误消息 if (showErrorToast) { showError(message, errorTitle) } // 调用错误回调 onError?.(error) return undefined } finally { loading.value = false } } return { loading, execute, } } /** * 创建多个独立的异步操作 * * 当需要在同一个组件中跟踪多个独立的 loading 状态时使用 * * @example * ```typescript * const { actions, isAnyLoading } = useMultipleAsyncActions(['save', 'delete', 'refresh']) * * // 使用各自的 loading 状态 * await actions.save.execute(() => api.save(data), { successMessage: '保存成功' }) * await actions.delete.execute(() => api.delete(id), { successMessage: '删除成功' }) * * // 检查是否有任何操作正在进行 * * ``` */ export function useMultipleAsyncActions( keys: K[] ): { actions: Record isAnyLoading: Ref } { const actions = {} as Record const loadingStates: Ref[] = [] for (const key of keys) { const action = useAsyncAction() actions[key] = action loadingStates.push(action.loading) } const isAnyLoading = ref(false) // 使用 watchEffect 来监听所有 loading 状态 // 这里简化处理,在每次 execute 时会自动更新 // 如果需要响应式,可以使用 computed const checkAnyLoading = () => { isAnyLoading.value = loadingStates.some((state) => state.value) } // 包装每个 action 的 execute 以更新 isAnyLoading for (const key of keys) { const originalExecute = actions[key].execute actions[key].execute = async (action, options) => { checkAnyLoading() try { return await originalExecute(action, options) } finally { checkAnyLoading() } } } return { actions, isAnyLoading, } }