style: use united style

This commit is contained in:
2026-01-03 16:09:22 +08:00
parent 955b09436e
commit c98aa73364
20 changed files with 2099 additions and 1319 deletions
@@ -0,0 +1,84 @@
/**
* 通用异步操作 Composable
* 统一处理 loading、error 状态和消息提示
*
* @example
* const { loading, error, execute } = useAsyncAction()
*
* const handleSubmit = async () => {
* await execute(
* () => api.createTask(formData),
* { successMsg: '创建成功', errorMsg: '创建失败' }
* )
* }
*/
import { ref } from 'vue'
import { message } from 'ant-design-vue'
export function useAsyncAction(options = {}) {
const loading = ref(false)
const error = ref(null)
/**
* 执行异步操作
* @param {Function} asyncFn - 异步函数
* @param {Object} config - 配置选项
* @param {string} config.successMsg - 成功提示消息
* @param {string} config.errorMsg - 错误提示消息
* @param {boolean} config.throwOnError - 是否抛出错误
* @param {boolean} config.silent - 是否静默模式(不显示消息)
* @returns {Promise} 异步函数的返回值
*/
const execute = async (asyncFn, config = {}) => {
const {
successMsg = options.successMsg,
errorMsg = options.errorMsg,
throwOnError = false,
silent = false
} = config
loading.value = true
error.value = null
try {
const result = await asyncFn()
if (!silent && successMsg) {
message.success(successMsg)
}
return result
} catch (err) {
error.value = err
if (!silent) {
const msg = err.message || err.detail || errorMsg || '操作失败'
message.error(msg)
}
if (throwOnError) {
throw err
}
return null
} finally {
loading.value = false
}
}
/**
* 重置状态
*/
const reset = () => {
loading.value = false
error.value = null
}
return {
loading,
error,
execute,
reset
}
}
+128
View File
@@ -0,0 +1,128 @@
/**
* 状态轮询 Composable
* 支持指数退避、最大重试次数、自动清理
*
* @example
* const { polling, startPolling, stopPolling } = usePollStatus({
* interval: 2000,
* maxRetries: 15,
* backoff: true
* })
*
* startPolling(
* async () => {
* const status = await api.getStatus(id)
* return {
* completed: status.status !== 'pending',
* success: status.status === 'success',
* data: status
* }
* },
* {
* onSuccess: (result) => console.log('完成', result),
* onFailure: (error) => console.error('失败', error),
* onTimeout: () => console.warn('超时')
* }
* )
*/
import { ref, onUnmounted } from 'vue'
export function usePollStatus(options = {}) {
const {
interval = 2000, // 初始轮询间隔(毫秒)
maxRetries = 15, // 最大重试次数
backoff = false, // 是否使用指数退避
maxBackoffInterval = 10000 // 最大退避间隔(毫秒)
} = options
const polling = ref(false)
let pollTimer = null
let retryCount = 0
/**
* 开始轮询
* @param {Function} checkFn - 检查函数,应返回 { completed, success, data }
* @param {Object} callbacks - 回调函数
* @param {Function} callbacks.onSuccess - 成功回调
* @param {Function} callbacks.onFailure - 失败回调
* @param {Function} callbacks.onTimeout - 超时回调
*/
const startPolling = async (checkFn, callbacks = {}) => {
const { onSuccess, onFailure, onTimeout } = callbacks
// 重置状态
stopPolling()
polling.value = true
retryCount = 0
const poll = async () => {
try {
const result = await checkFn()
// 检查是否完成
if (result.completed) {
stopPolling()
if (result.success) {
onSuccess?.(result.data || result)
} else {
onFailure?.(result.data || result)
}
return
}
// 检查是否超时
retryCount++
if (retryCount >= maxRetries) {
stopPolling()
onTimeout?.()
return
}
// 计算下次轮询间隔(支持指数退避)
let nextInterval = interval
if (backoff) {
// 指数退避:2s -> 4s -> 8s -> 最大10s
nextInterval = Math.min(
interval * Math.pow(2, retryCount - 1),
maxBackoffInterval
)
}
// 继续轮询
pollTimer = setTimeout(poll, nextInterval)
} catch (error) {
stopPolling()
onFailure?.(error)
}
}
// 立即执行第一次检查
poll()
}
/**
* 停止轮询
*/
const stopPolling = () => {
if (pollTimer) {
clearTimeout(pollTimer)
pollTimer = null
}
polling.value = false
retryCount = 0
}
// 组件卸载时自动清理
onUnmounted(() => {
stopPolling()
})
return {
polling,
startPolling,
stopPolling
}
}