From 4790aced1571e25f05d52be091f8a4b10943e6b8 Mon Sep 17 00:00:00 2001 From: yangjianbo Date: Tue, 6 Jan 2026 12:42:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=89=8D=E7=AB=AF):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20ESLint=20=E4=BB=A3=E7=A0=81=E8=A7=84=E8=8C=83=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 AccountTableFilters.vue 中的 vue/no-mutating-props 错误,使用 emit 模式替代直接修改 props - 修复 TypeScript 类型错误,支持 Select 组件的 null 值类型 - 为所有空 catch 块添加错误日志,提升代码可维护性和调试能力 - 涉及文件:AccountTableFilters.vue, UserAllowedGroupsModal.vue, UserApiKeysModal.vue, UserBalanceModal.vue, AccountsView.vue, UsageView.vue, DashboardView.vue, ProfileView.vue 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../admin/account/AccountTableFilters.vue | 8 +++++--- .../admin/user/UserAllowedGroupsModal.vue | 4 ++-- .../src/components/admin/user/UserApiKeysModal.vue | 2 +- .../src/components/admin/user/UserBalanceModal.vue | 2 +- frontend/src/views/admin/AccountsView.vue | 14 +++++++------- frontend/src/views/admin/UsageView.vue | 4 ++-- frontend/src/views/user/DashboardView.vue | 6 +++--- frontend/src/views/user/ProfileView.vue | 2 +- 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/admin/account/AccountTableFilters.vue b/frontend/src/components/admin/account/AccountTableFilters.vue index 3721acc6..457afe8c 100644 --- a/frontend/src/components/admin/account/AccountTableFilters.vue +++ b/frontend/src/components/admin/account/AccountTableFilters.vue @@ -9,15 +9,17 @@ />
- +
diff --git a/frontend/src/components/admin/user/UserAllowedGroupsModal.vue b/frontend/src/components/admin/user/UserAllowedGroupsModal.vue index 669772e3..e538e4e5 100644 --- a/frontend/src/components/admin/user/UserAllowedGroupsModal.vue +++ b/frontend/src/components/admin/user/UserAllowedGroupsModal.vue @@ -48,12 +48,12 @@ const emit = defineEmits(['close', 'success']); const { t } = useI18n(); const a const groups = ref([]); const selectedIds = ref([]); const loading = ref(false); const submitting = ref(false) watch(() => props.show, (v) => { if(v && props.user) { selectedIds.value = props.user.allowed_groups || []; load() } }) -const load = async () => { loading.value = true; try { const res = await adminAPI.groups.list(1, 1000); groups.value = res.items.filter(g => g.subscription_type === 'standard' && g.status === 'active') } catch {} finally { loading.value = false } } +const load = async () => { loading.value = true; try { const res = await adminAPI.groups.list(1, 1000); groups.value = res.items.filter(g => g.subscription_type === 'standard' && g.status === 'active') } catch (error) { console.error('Failed to load groups:', error) } finally { loading.value = false } } const handleSave = async () => { if (!props.user) return; submitting.value = true try { await adminAPI.users.update(props.user.id, { allowed_groups: selectedIds.value.length > 0 ? selectedIds.value : null }) appStore.showSuccess(t('admin.users.allowedGroupsUpdated')); emit('success'); emit('close') - } catch {} finally { submitting.value = false } + } catch (error) { console.error('Failed to update allowed groups:', error) } finally { submitting.value = false } } \ No newline at end of file diff --git a/frontend/src/components/admin/user/UserApiKeysModal.vue b/frontend/src/components/admin/user/UserApiKeysModal.vue index 27c006bc..ef098ba1 100644 --- a/frontend/src/components/admin/user/UserApiKeysModal.vue +++ b/frontend/src/components/admin/user/UserApiKeysModal.vue @@ -42,6 +42,6 @@ const apiKeys = ref([]); const loading = ref(false) watch(() => props.show, (v) => { if (v && props.user) load() }) const load = async () => { if (!props.user) return; loading.value = true - try { const res = await adminAPI.users.getUserApiKeys(props.user.id); apiKeys.value = res.items || [] } catch {} finally { loading.value = false } + try { const res = await adminAPI.users.getUserApiKeys(props.user.id); apiKeys.value = res.items || [] } catch (error) { console.error('Failed to load API keys:', error) } finally { loading.value = false } } \ No newline at end of file diff --git a/frontend/src/components/admin/user/UserBalanceModal.vue b/frontend/src/components/admin/user/UserBalanceModal.vue index 19e9ccab..41050629 100644 --- a/frontend/src/components/admin/user/UserBalanceModal.vue +++ b/frontend/src/components/admin/user/UserBalanceModal.vue @@ -41,6 +41,6 @@ const handleBalanceSubmit = async () => { try { await adminAPI.users.updateBalance(props.user.id, form.amount, props.operation, form.notes) appStore.showSuccess(t('common.success')); emit('success'); emit('close') - } catch {} finally { submitting.value = false } + } catch (error) { console.error('Failed to update balance:', error) } finally { submitting.value = false } } \ No newline at end of file diff --git a/frontend/src/views/admin/AccountsView.vue b/frontend/src/views/admin/AccountsView.vue index 5a3138c9..c886de01 100644 --- a/frontend/src/views/admin/AccountsView.vue +++ b/frontend/src/views/admin/AccountsView.vue @@ -191,7 +191,7 @@ const handleEdit = (a: Account) => { edAcc.value = a; showEdit.value = true } const openMenu = (a: Account, e: MouseEvent) => { menu.acc = a; menu.pos = { top: e.clientY, left: e.clientX - 200 }; menu.show = true } const toggleSel = (id: number) => { const i = selIds.value.indexOf(id); if(i === -1) selIds.value.push(id); else selIds.value.splice(i, 1) } const selectPage = () => { selIds.value = [...new Set([...selIds.value, ...accounts.value.map(a => a.id)])] } -const handleBulkDelete = async () => { if(!confirm(t('common.confirm'))) return; try { await Promise.all(selIds.value.map(id => adminAPI.accounts.delete(id))); selIds.value = []; reload() } catch {} } +const handleBulkDelete = async () => { if(!confirm(t('common.confirm'))) return; try { await Promise.all(selIds.value.map(id => adminAPI.accounts.delete(id))); selIds.value = []; reload() } catch (error) { console.error('Failed to bulk delete accounts:', error) } } const handleBulkUpdated = () => { showBulkEdit.value = false; selIds.value = []; reload() } const closeTestModal = () => { showTest.value = false; testingAcc.value = null } const closeStatsModal = () => { showStats.value = false; statsAcc.value = null } @@ -199,14 +199,14 @@ const closeReAuthModal = () => { showReAuth.value = false; reAuthAcc.value = nul const handleTest = (a: Account) => { testingAcc.value = a; showTest.value = true } const handleViewStats = (a: Account) => { statsAcc.value = a; showStats.value = true } const handleReAuth = (a: Account) => { reAuthAcc.value = a; showReAuth.value = true } -const handleRefresh = async (a: Account) => { try { await adminAPI.accounts.refreshCredentials(a.id); load() } catch {} } -const handleResetStatus = async (a: Account) => { try { await adminAPI.accounts.clearError(a.id); appStore.showSuccess(t('common.success')); load() } catch {} } -const handleClearRateLimit = async (a: Account) => { try { await adminAPI.accounts.clearError(a.id); appStore.showSuccess(t('common.success')); load() } catch {} } +const handleRefresh = async (a: Account) => { try { await adminAPI.accounts.refreshCredentials(a.id); load() } catch (error) { console.error('Failed to refresh credentials:', error) } } +const handleResetStatus = async (a: Account) => { try { await adminAPI.accounts.clearError(a.id); appStore.showSuccess(t('common.success')); load() } catch (error) { console.error('Failed to reset status:', error) } } +const handleClearRateLimit = async (a: Account) => { try { await adminAPI.accounts.clearError(a.id); appStore.showSuccess(t('common.success')); load() } catch (error) { console.error('Failed to clear rate limit:', error) } } const handleDelete = (a: Account) => { deletingAcc.value = a; showDeleteDialog.value = true } -const confirmDelete = async () => { if(!deletingAcc.value) return; try { await adminAPI.accounts.delete(deletingAcc.value.id); showDeleteDialog.value = false; deletingAcc.value = null; reload() } catch {} } +const confirmDelete = async () => { if(!deletingAcc.value) return; try { await adminAPI.accounts.delete(deletingAcc.value.id); showDeleteDialog.value = false; deletingAcc.value = null; reload() } catch (error) { console.error('Failed to delete account:', error) } } const handleToggleSchedulable = async (a: Account) => { togglingSchedulable.value = a.id; try { await adminAPI.accounts.update(a.id, { schedulable: !a.schedulable }); load() } finally { togglingSchedulable.value = null } } const handleShowTempUnsched = (a: Account) => { tempUnschedAcc.value = a; showTempUnsched.value = true } -const handleTempUnschedReset = async () => { if(!tempUnschedAcc.value) return; try { await adminAPI.accounts.clearError(tempUnschedAcc.value.id); showTempUnsched.value = false; tempUnschedAcc.value = null; load() } catch {} } +const handleTempUnschedReset = async () => { if(!tempUnschedAcc.value) return; try { await adminAPI.accounts.clearError(tempUnschedAcc.value.id); showTempUnsched.value = false; tempUnschedAcc.value = null; load() } catch (error) { console.error('Failed to reset temp unscheduled:', error) } } -onMounted(async () => { load(); try { const [p, g] = await Promise.all([adminAPI.proxies.getAll(), adminAPI.groups.getAll()]); proxies.value = p; groups.value = g } catch {} }) +onMounted(async () => { load(); try { const [p, g] = await Promise.all([adminAPI.proxies.getAll(), adminAPI.groups.getAll()]); proxies.value = p; groups.value = g } catch (error) { console.error('Failed to load proxies/groups:', error) } }) diff --git a/frontend/src/views/admin/UsageView.vue b/frontend/src/views/admin/UsageView.vue index edc1e194..03324e6e 100644 --- a/frontend/src/views/admin/UsageView.vue +++ b/frontend/src/views/admin/UsageView.vue @@ -35,9 +35,9 @@ const loadLogs = async () => { try { const res = await adminAPI.usage.list({ page: pagination.page, page_size: pagination.page_size, ...filters.value }, { signal: c.signal }) if(!c.signal.aborted) { usageLogs.value = res.items; pagination.total = res.total } - } catch {} finally { if(abortController === c) loading.value = false } + } catch (error: any) { if(error?.name !== 'AbortError') console.error('Failed to load usage logs:', error) } finally { if(abortController === c) loading.value = false } } -const loadStats = async () => { try { const s = await adminAPI.usage.getStats(filters.value); usageStats.value = s } catch {} } +const loadStats = async () => { try { const s = await adminAPI.usage.getStats(filters.value); usageStats.value = s } catch (error) { console.error('Failed to load usage stats:', error) } } const applyFilters = () => { pagination.page = 1; loadLogs(); loadStats() } const resetFilters = () => { startDate.value = formatLD(weekAgo); endDate.value = formatLD(now); filters.value = { start_date: startDate.value, end_date: endDate.value }; applyFilters() } const handlePageChange = (p: number) => { pagination.page = p; loadLogs() } diff --git a/frontend/src/views/user/DashboardView.vue b/frontend/src/views/user/DashboardView.vue index 39d2f877..55bbc66b 100644 --- a/frontend/src/views/user/DashboardView.vue +++ b/frontend/src/views/user/DashboardView.vue @@ -28,9 +28,9 @@ const trendData = ref([]); const modelStats = ref const formatLD = (d: Date) => d.toISOString().split('T')[0] const startDate = ref(formatLD(new Date(Date.now() - 6 * 86400000))); const endDate = ref(formatLD(new Date())); const granularity = ref('day') -const loadStats = async () => { loading.value = true; try { await authStore.refreshUser(); stats.value = await usageAPI.getDashboardStats() } catch {} finally { loading.value = false } } -const loadCharts = async () => { loadingCharts.value = true; try { const res = await Promise.all([usageAPI.getDashboardTrend({ start_date: startDate.value, end_date: endDate.value, granularity: granularity.value as any }), usageAPI.getDashboardModels({ start_date: startDate.value, end_date: endDate.value })]); trendData.value = res[0].trend || []; modelStats.value = res[1].models || [] } catch {} finally { loadingCharts.value = false } } -const loadRecent = async () => { loadingUsage.value = true; try { const res = await usageAPI.getByDateRange(startDate.value, endDate.value); recentUsage.value = res.items.slice(0, 5) } catch {} finally { loadingUsage.value = false } } +const loadStats = async () => { loading.value = true; try { await authStore.refreshUser(); stats.value = await usageAPI.getDashboardStats() } catch (error) { console.error('Failed to load dashboard stats:', error) } finally { loading.value = false } } +const loadCharts = async () => { loadingCharts.value = true; try { const res = await Promise.all([usageAPI.getDashboardTrend({ start_date: startDate.value, end_date: endDate.value, granularity: granularity.value as any }), usageAPI.getDashboardModels({ start_date: startDate.value, end_date: endDate.value })]); trendData.value = res[0].trend || []; modelStats.value = res[1].models || [] } catch (error) { console.error('Failed to load charts:', error) } finally { loadingCharts.value = false } } +const loadRecent = async () => { loadingUsage.value = true; try { const res = await usageAPI.getByDateRange(startDate.value, endDate.value); recentUsage.value = res.items.slice(0, 5) } catch (error) { console.error('Failed to load recent usage:', error) } finally { loadingUsage.value = false } } onMounted(() => { loadStats(); loadCharts(); loadRecent() }) diff --git a/frontend/src/views/user/ProfileView.vue b/frontend/src/views/user/ProfileView.vue index eaf98b77..4b6511ea 100644 --- a/frontend/src/views/user/ProfileView.vue +++ b/frontend/src/views/user/ProfileView.vue @@ -35,6 +35,6 @@ const WalletIcon = { render: () => h('svg', { fill: 'none', viewBox: '0 0 24 24' const BoltIcon = { render: () => h('svg', { fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor', 'stroke-width': '1.5' }, [h('path', { d: 'm3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z' })]) } const CalendarIcon = { render: () => h('svg', { fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor', 'stroke-width': '1.5' }, [h('path', { d: 'M6.75 3v2.25M17.25 3v2.25' })]) } -onMounted(async () => { try { const s = await authAPI.getPublicSettings(); contactInfo.value = s.contact_info || '' } catch {} }) +onMounted(async () => { try { const s = await authAPI.getPublicSettings(); contactInfo.value = s.contact_info || '' } catch (error) { console.error('Failed to load contact info:', error) } }) const formatCurrency = (v: number) => `$${v.toFixed(2)}` \ No newline at end of file