fix(后台列表): 加载失败有提示、慢的时候表格转圈、旧请求晚回来不再盖掉新结果
Deploy / deploy (push) Canceled after 0s

后台八个列表(题目、比赛、用户、公告、题单、AI 报告、从题库加题、标签下的题)原来是
`const res = await getXxx(); rows.value = res.results`:接口 500 或断网时什么都不提示,
表格停在旧数据上;关键词防抖和翻页一交错,先发的晚回来会把新结果盖掉。

shared/composables/listLoader.ts 的 track() 把请求包一层:带请求序号、出错按后端文案提示
(登录过期、没权限这几种全局拦截器已经提示过,不弹第二遍)、表格挂 loading。

实跑:六个列表各 10 行正常;拦掉题目列表的接口再翻页,弹「网络异常,请检查网络后重试」
(原来没有任何反应)。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-10-16 15:07:10 +08:00
1 parent 4dfd14f95b
commit d4aadc7722
10 files changed
+122 -27

No files matched your search

@@ -3,6 +3,7 @@ import { getLibraryProblemList } from "admin/api"
import Pagination from "shared/components/Pagination.vue"
import type { AdminProblemRow } from "utils/types"
import AddButton from "./AddButton.vue"
import { useListLoader } from "shared/composables/listLoader"
interface Props {
show: boolean
@@ -42,9 +43,11 @@ const columns: DataTableColumn<AdminProblemRow>[] = [
},
]
const { loading, track } = useListLoader()
async function getList() {
const offset = (query.page - 1) * query.limit
const res = await getLibraryProblemList(offset, query.limit, query.keyword)
const res = await track(getLibraryProblemList(offset, query.limit, query.keyword))
if (!res) return
total.value = res.total
problems.value = res.results
}
@@ -74,7 +77,7 @@ watchDebounced(
@close="$emit('update:show', false)"
>
<n-input class="search" v-model:value="query.keyword" clearable placeholder="搜索标题或编号" />
<n-data-table striped :columns="columns" :data="problems" />
<n-data-table :loading="loading" striped :columns="columns" :data="problems" />
<Pagination :total="total" v-model:limit="query.limit" v-model:page="query.page" />
</n-modal>
</template>
@@ -4,6 +4,7 @@ import Pagination from "shared/components/Pagination.vue"
import type { AdminProblemRow } from "utils/types"
import { batchTagProblems, getProblemList } from "admin/api"
import { useBreakpoints } from "shared/composables/breakpoints"
import { useListLoader } from "shared/composables/listLoader"
interface Props {
show: boolean
@@ -63,10 +64,14 @@ const mobileColumns: DataTableColumn<AdminProblemRow>[] = [
const columns = computed(() => (isMobile.value ? mobileColumns : allColumns))
const { loading, track } = useListLoader()
async function listProblems() {
if (page.value < 1) page.value = 1
const offset = (page.value - 1) * limit.value
const res = await getProblemList(offset, limit.value, keyword.value, "", undefined, props.tagId)
const res = await track(
getProblemList(offset, limit.value, keyword.value, "", undefined, props.tagId),
)
if (!res) return
problems.value = res.results
total.value = res.total
}
@@ -134,7 +139,7 @@ watchDebounced(
clearable
/>
</n-flex>
<n-data-table striped :columns="columns" :data="problems" />
<n-data-table :loading="loading" striped :columns="columns" :data="problems" />
<Pagination :total="total" v-model:limit="limit" v-model:page="page" />
</n-flex>
</n-modal>
+15 -9
View File
@@ -16,6 +16,7 @@ import CreateProblemMenu from "./components/CreateProblemMenu.vue"
import PageHeader from "admin/components/PageHeader.vue"
import ProblemTypeTag from "oj/problem/components/ProblemTypeTag.vue"
import { useBreakpoints } from "shared/composables/breakpoints"
import { useListLoader } from "shared/composables/listLoader"
/**
* 后台题目列表(设计稿「后台题目列表」PlA / PlB)。一行一道题:题号、题目(点了就是编辑)、
@@ -265,19 +266,23 @@ const columns = computed<DataTableColumn<AdminProblemRow>[]>(() => {
: [{ type: "selection" }, ...cols]
})
const { loading, track } = useListLoader()
async function listProblems() {
if (query.page < 1) query.page = 1
const offset = (query.page - 1) * query.limit
const res = await getProblemList(
offset,
query.limit,
query.keyword,
query.author,
props.contestID,
undefined,
query.kind,
props.contestID ? "" : query.visible,
const res = await track(
getProblemList(
offset,
query.limit,
query.keyword,
query.author,
props.contestID,
undefined,
query.kind,
props.contestID ? "" : query.visible,
),
)
if (!res) return
total.value = res.total
problems.value = res.results
counts.value = res.counts
@@ -404,6 +409,7 @@ watch(() => [query.page, query.limit, query.author, query.kind, query.visible],
<n-button size="small" quaternary @click="selectedRowKeys = []">取消选择</n-button>
</div>
<n-data-table
:loading="loading"
striped
class="problem-table"
:columns="columns"