refactor: 修复 API 客户端 composables 滥用和硬编码路径问题

- 修复 lib/api-client.ts 中在类方法中直接调用 composables 的问题
- 添加 ApiErrorHandler 错误处理器,通过依赖注入方式处理 401 错误
- 创建路由工具函数 utils/routes.ts,统一管理路由路径
- 修复 composables/useAuth.ts 中的硬编码路径,使用工具函数
- 修复 components/copy-input.vue 的生命周期问题,在 onMounted 中创建 ClipboardJS 实例
- 修复 plugins/03.s3.ts 中的错误处理,使用 navigateTo 替代直接操作 window.location
- 修复 pages/config.vue 中的硬编码路径
- 添加 composables/useFormValidation.ts、useAsyncOperation.ts、useErrorHandler.ts 工具函数(供后续使用)
- 添加 types/api.ts 类型定义
This commit is contained in:
overtrue
2025-10-31 22:50:59 +08:00
parent 572a2e0150
commit 0598f8065f
12 changed files with 732 additions and 168 deletions
+32 -37
View File
@@ -1,11 +1,11 @@
<script lang="ts" setup>
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Icon } from '#components'
import ClipboardJS from 'clipboard'
import { useMessage } from '@/lib/ui/message'
import { onUnmounted } from 'vue'
import ClipboardJS from 'clipboard'
import { onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
@@ -31,51 +31,46 @@ const props = defineProps({
const model = defineModel<string>()
const message = useMessage()
const clipboard = ref<ClipboardJS | null>(null)
const clipboard = new ClipboardJS(props.copyIcon ? `#${props.id}-btn-icon` : `#${props.id}-btn`)
clipboard.on('success', function (e) {
message.success(t('Copy Success'))
e.clearSelection()
onMounted(() => {
clipboard.value = new ClipboardJS(
props.copyIcon ? `#${props.id}-btn-icon` : `#${props.id}-btn`
)
clipboard.value.on('success', function (e) {
message.success(t('Copy Success'))
e.clearSelection()
})
clipboard.value.on('error', async function () {
// 如果复制失败,则使用 navigator.clipboard.writeText 进行复制
try {
if (navigator.clipboard && model.value) {
await navigator.clipboard.writeText(model.value)
message.success(t('Copy Success'))
} else {
message.error(t('Copy Failed'))
}
} catch {
message.error(t('Copy Failed'))
}
})
})
// 这里成功的时候也响应error,所以这里也加上
clipboard.on('error', function (e) {
// 如果复制失败,则使用navigator.clipboard.writeText进行复制
if (navigator.clipboard) {
navigator.clipboard.writeText(model.value || '')
}
message.success(t('Copy Success'))
})
onUnmounted(() => {
clipboard.destroy()
clipboard.value?.destroy()
clipboard.value = null
})
</script>
<template>
<div class="flex h-full items-center gap-2">
<Input
v-model="model"
:readonly="props.readonly"
:id="props.id"
class="flex-1"
/>
<Button
v-if="!props.copyIcon"
:id="`${props.id}-btn`"
:data-clipboard-target="`#${props.id}`"
variant="default"
>
<Input v-model="model" :readonly="props.readonly" :id="props.id" class="flex-1" />
<Button v-if="!props.copyIcon" :id="`${props.id}-btn`" :data-clipboard-target="`#${props.id}`" variant="default">
{{ t('Copy') }}
</Button>
<Button
v-else
:id="`${props.id}-btn-icon`"
variant="ghost"
size="sm"
class="shrink-0"
:data-clipboard-target="`#${props.id}`"
:title="t('Copy')"
>
<Button v-else :id="`${props.id}-btn-icon`" variant="ghost" size="sm" class="shrink-0" :data-clipboard-target="`#${props.id}`" :title="t('Copy')">
<Icon :size="18" name="ri:file-copy-line" />
<span class="sr-only">{{ t('Copy') }}</span>
</Button>