diff --git a/src/api.ts b/src/api.ts index 3ec2307..6bc4ba2 100644 --- a/src/api.ts +++ b/src/api.ts @@ -421,6 +421,13 @@ export const Showcase = { return res.data }, + async refreshAwardItem(itemId: number): Promise { + const res = await http.put( + `/submission/showcase/manage/items/${itemId}/refresh`, + ) + return res.data + }, + async getDetail(submissionId: string): Promise { const res = await http.get(`/submission/showcase/${submissionId}/`) return res.data diff --git a/src/pages/ShowcaseManage.vue b/src/pages/ShowcaseManage.vue index 830e7a0..c92e098 100644 --- a/src/pages/ShowcaseManage.vue +++ b/src/pages/ShowcaseManage.vue @@ -242,6 +242,7 @@ import { computed, h, onMounted, reactive, ref } from "vue" import { NButton, + NButtonGroup, NInputNumber, NTag, useMessage, @@ -268,6 +269,7 @@ const itemsLoading = ref(false) const savingAward = ref(false) const deletingAward = ref(false) const updatingItemIds = ref(new Set()) +const refreshingItemIds = ref(new Set()) const addWorkModalVisible = ref(false) const lookupSubmissionId = ref("") const lookupLoading = ref(false) @@ -347,22 +349,49 @@ const itemColumns: DataTableColumn[] = [ { default: () => (row.has_prompt_chain ? "有" : "无") }, ), }, + { + title: "状态", + key: "is_stale", + width: 96, + render: (row) => + row.is_stale + ? h(NTag, { size: "small", type: "warning" }, { default: () => "有新提交" }) + : null, + }, { title: "", key: "actions", - width: 54, + width: 100, render: (row) => - h( - NButton, - { - size: "small", - tertiary: true, - type: "error", - title: "移除", - onClick: () => removeAwardItem(row), - }, - { icon: () => h(Icon, { icon: "lucide:trash-2", width: 15 }) }, - ), + h(NButtonGroup, { size: "small" }, { + default: () => [ + row.is_stale + ? h( + NButton, + { + size: "small", + secondary: true, + type: "warning", + title: "切换到最新提交", + loading: refreshingItemIds.value.has(row.id), + onClick: () => refreshAwardItem(row), + }, + { icon: () => h(Icon, { icon: "lucide:refresh-cw", width: 14 }) }, + ) + : null, + h( + NButton, + { + size: "small", + tertiary: true, + type: "error", + title: "移除", + onClick: () => removeAwardItem(row), + }, + { icon: () => h(Icon, { icon: "lucide:trash-2", width: 15 }) }, + ), + ], + }), }, ] @@ -470,6 +499,27 @@ function setUpdatingItem(id: number, loading: boolean) { updatingItemIds.value = next } +function setRefreshingItem(id: number, loading: boolean) { + const next = new Set(refreshingItemIds.value) + if (loading) next.add(id) + else next.delete(id) + refreshingItemIds.value = next +} + +async function refreshAwardItem(row: AwardItemManageOut) { + setRefreshingItem(row.id, true) + try { + const updated = await Showcase.refreshAwardItem(row.id) + const idx = awardItems.value.findIndex((item) => item.id === row.id) + if (idx !== -1) awardItems.value[idx] = updated + message.success("已切换到最新提交") + } catch (err: any) { + message.error(err.response?.data?.detail ?? "切换失败") + } finally { + setRefreshingItem(row.id, false) + } +} + async function updateItemOrder(row: AwardItemManageOut, sortOrder: number) { if (row.sort_order === sortOrder) return row.sort_order = sortOrder diff --git a/src/utils/type.ts b/src/utils/type.ts index 5d52821..87e2bb3 100644 --- a/src/utils/type.ts +++ b/src/utils/type.ts @@ -293,6 +293,7 @@ export interface AwardItemManageOut { sort_order: number awarded_at: string has_prompt_chain: boolean + is_stale: boolean } export interface ShowcaseDetail {