diff --git a/src/shared/components/AchievementToast.vue b/src/shared/components/AchievementToast.vue index e6fd7e9..2e7c7e5 100644 --- a/src/shared/components/AchievementToast.vue +++ b/src/shared/components/AchievementToast.vue @@ -22,7 +22,7 @@ function playNext() { visible.value = true timer = setTimeout(async () => { visible.value = false - await store.markRead(item.id) + await store.markRead(item) gapTimer = setTimeout(playNext, 400) }, 3000) } @@ -49,7 +49,9 @@ onUnmounted(() => { >
{{ current.icon }}
-
成就解锁
+
+ {{ current.kind === "badge" ? "获得奖章" : "成就解锁" }} +
{{ current.name }}
{{ current.description }}
diff --git a/src/shared/store/achievement.ts b/src/shared/store/achievement.ts index 58ffb85..75086d5 100644 --- a/src/shared/store/achievement.ts +++ b/src/shared/store/achievement.ts @@ -18,14 +18,20 @@ export const useAchievementStore = defineStore("achievement", () => { const queue = ref([]) const current = ref(null) + // 成就和题单奖章的 id 来自两张不同的表,数值会重叠, + // 只按 id 去重会让奖章 5 把成就 5 挤掉 + function keyOf(item: PendingAchievement) { + return `${item.kind ?? "achievement"}:${item.id}` + } + function enqueue(items: PendingAchievement[]) { if (!items?.length) return // 去重:WebSocket 推来的和 pending 拉来的可能是同一批 const known = new Set([ - ...queue.value.map((i) => i.id), - ...(current.value ? [current.value.id] : []), + ...queue.value.map(keyOf), + ...(current.value ? [keyOf(current.value)] : []), ]) - queue.value.push(...items.filter((i) => !known.has(i.id))) + queue.value.push(...items.filter((i) => !known.has(keyOf(i)))) } async function fetchPending() { @@ -43,9 +49,12 @@ export const useAchievementStore = defineStore("achievement", () => { return current.value } - async function markRead(id: number) { + async function markRead(item: PendingAchievement) { + // 奖章不在 UserAchievement 表里,它的 id 传给标记接口会被当成成就 id, + // 把一个恰好同号、还没弹过的成就静默标记为已弹——那个奖杯就再也不会出现 + if (item.kind === "badge") return try { - await markAchievementsRead([id]) + await markAchievementsRead([item.id]) } catch { // 标记失败下次会重复弹一次,可接受 } diff --git a/src/utils/types.ts b/src/utils/types.ts index 32d87da..c3bf56f 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -759,6 +759,10 @@ export interface PendingAchievement { description: string icon: string rarity: AchievementRarity + // 弹窗队列里混着两种东西:全站成就和题单奖章。它们的 id 来自两张不同的表, + // 数值会重叠,所以去重和标记已读都必须带上 kind 一起判断。 + // pending 接口只返回成就,不带这个字段,缺省按 achievement 处理。 + kind?: "achievement" | "badge" } export interface AchievementSummary {