fix(fe): 修复中优先级表格功能问题

修复的问题:

1. **搜索和筛选防抖不同步**(AccountsView.vue)
   - 问题:筛选器使用 reload(立即),搜索使用 debouncedReload(300ms延迟)
   - 修复:统一使用 debouncedReload,避免多余的API调用

2. **useTableLoader 竞态条件**(useTableLoader.ts)
   - 问题:finally 块检查 signal.aborted 而不是 controller 实例
   - 修复:检查 abortController === currentController

3. **改进错误处理**(UsersView.vue)
   - 添加详细错误消息:error.response?.data?.detail || error.message
   - 用户可以看到具体的错误原因而不是通用消息

4. **分页边界检查**(useTableLoader.ts, UsersView.vue)
   - 添加页码有效性检查:Math.max(1, Math.min(page, pagination.pages || 1))
   - 防止分页越界导致显示空表

影响范围:
- frontend/src/composables/useTableLoader.ts
- frontend/src/views/admin/AccountsView.vue
- frontend/src/views/admin/UsersView.vue

测试:✓ 前端构建测试通过
This commit is contained in:
IanShaw027
2026-01-09 17:58:21 +08:00
parent ee9b9b3971
commit 514f5802b5
3 changed files with 15 additions and 9 deletions

View File

@@ -43,7 +43,8 @@ export function useTableLoader<T, P extends Record<string, any>>(options: TableL
if (abortController) { if (abortController) {
abortController.abort() abortController.abort()
} }
abortController = new AbortController() const currentController = new AbortController()
abortController = currentController
loading.value = true loading.value = true
try { try {
@@ -51,7 +52,7 @@ export function useTableLoader<T, P extends Record<string, any>>(options: TableL
pagination.page, pagination.page,
pagination.page_size, pagination.page_size,
toRaw(params) as P, toRaw(params) as P,
{ signal: abortController.signal } { signal: currentController.signal }
) )
items.value = response.items || [] items.value = response.items || []
@@ -63,7 +64,7 @@ export function useTableLoader<T, P extends Record<string, any>>(options: TableL
throw error throw error
} }
} finally { } finally {
if (abortController && !abortController.signal.aborted) { if (abortController === currentController) {
loading.value = false loading.value = false
} }
} }
@@ -77,7 +78,9 @@ export function useTableLoader<T, P extends Record<string, any>>(options: TableL
const debouncedReload = useDebounceFn(reload, debounceMs) const debouncedReload = useDebounceFn(reload, debounceMs)
const handlePageChange = (page: number) => { const handlePageChange = (page: number) => {
pagination.page = page // 确保页码在有效范围内
const validPage = Math.max(1, Math.min(page, pagination.pages || 1))
pagination.page = validPage
load() load()
} }

View File

@@ -7,7 +7,7 @@
v-model:searchQuery="params.search" v-model:searchQuery="params.search"
:filters="params" :filters="params"
@update:filters="(newFilters) => Object.assign(params, newFilters)" @update:filters="(newFilters) => Object.assign(params, newFilters)"
@change="reload" @change="debouncedReload"
@update:searchQuery="debouncedReload" @update:searchQuery="debouncedReload"
/> />
<AccountTableActions <AccountTableActions

View File

@@ -893,12 +893,13 @@ const loadUsers = async () => {
} }
} }
} }
} catch (error) { } catch (error: any) {
const errorInfo = error as { name?: string; code?: string } const errorInfo = error as { name?: string; code?: string }
if (errorInfo?.name === 'AbortError' || errorInfo?.name === 'CanceledError' || errorInfo?.code === 'ERR_CANCELED') { if (errorInfo?.name === 'AbortError' || errorInfo?.name === 'CanceledError' || errorInfo?.code === 'ERR_CANCELED') {
return return
} }
appStore.showError(t('admin.users.failedToLoad')) const message = error.response?.data?.detail || error.message || t('admin.users.failedToLoad')
appStore.showError(message)
console.error('Error loading users:', error) console.error('Error loading users:', error)
} finally { } finally {
if (abortController === currentAbortController) { if (abortController === currentAbortController) {
@@ -917,7 +918,9 @@ const handleSearch = () => {
} }
const handlePageChange = (page: number) => { const handlePageChange = (page: number) => {
pagination.page = page // 确保页码在有效范围内
const validPage = Math.max(1, Math.min(page, pagination.pages || 1))
pagination.page = validPage
loadUsers() loadUsers()
} }