Deploy / deploy (push) Canceled after 0s
- 答案错误里「格式和内容都不一样」那一类(样例上错的 44%):原来只说「对照一下,找找 从哪里开始不一样」,现在是「你的程序输出了「E」,正确的是「D」。拿例子 2 的输入「60」, 一行一行走一遍你的程序,看它走进了哪个分支」;多行的说第几行开始不一样 - 不在这节课里的题交对之后,结果页签里给「再练几道相似的」(原来只在题面最底下) - 提交前自动排版改了代码的,结果上面提一句「编辑器里的代码也跟着变了,按 Ctrl+Z 能撤回」 - 交对之后「提交」降成描边按钮,「下一题」是唯一的主按钮 - 「重置代码」先确认(原来点一下就把代码和本地草稿一起清掉了) - AI 提示的档位写「第 1 档 · 反问」,不写「L0 反问」 - 成就弹窗从右下角挪到右上角,别和结果、点评弹窗叠在一起 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
2.3 KiB
Vue
103 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
import AchievementIcon from "shared/components/AchievementIcon.vue"
|
|
import { useAchievementStore } from "shared/store/achievement"
|
|
import { RARITY_COLOR } from "utils/constants"
|
|
|
|
const store = useAchievementStore()
|
|
const { current, queue } = storeToRefs(store)
|
|
const visible = ref(false)
|
|
|
|
let timer: ReturnType<typeof setTimeout> | null = null
|
|
let gapTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
|
// 多个同时解锁时排队依次弹出,不重叠堆积
|
|
function playNext() {
|
|
const item = store.next()
|
|
if (!item) return
|
|
visible.value = true
|
|
timer = setTimeout(async () => {
|
|
visible.value = false
|
|
await store.markRead(item)
|
|
gapTimer = setTimeout(playNext, 400)
|
|
}, 3000)
|
|
}
|
|
|
|
watch(
|
|
() => queue.value.length,
|
|
(len) => {
|
|
if (len > 0 && !visible.value) playNext()
|
|
},
|
|
)
|
|
|
|
onUnmounted(() => {
|
|
if (timer) clearTimeout(timer)
|
|
if (gapTimer) clearTimeout(gapTimer)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<Transition name="slide">
|
|
<div
|
|
v-if="visible && current"
|
|
class="toast"
|
|
:style="{ borderColor: RARITY_COLOR[current.rarity] }"
|
|
>
|
|
<div class="icon">
|
|
<AchievementIcon :icon="current.icon" :size="34" />
|
|
</div>
|
|
<div class="body">
|
|
<div class="label">
|
|
{{ current.kind === "badge" ? "获得奖章" : "成就解锁" }}
|
|
</div>
|
|
<div class="name">{{ current.name }}</div>
|
|
<div class="desc">{{ current.description }}</div>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.toast {
|
|
position: fixed;
|
|
/* 右上角、顶栏下面。原来在右下角,题目页上正好和结果、点评弹窗叠在一起 */
|
|
right: 24px;
|
|
top: 72px;
|
|
z-index: 3000;
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
padding: 14px 18px;
|
|
border-radius: 10px;
|
|
border: 2px solid;
|
|
background: var(--n-color, rgba(24, 24, 28, 0.95));
|
|
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
|
|
min-width: 260px;
|
|
}
|
|
.icon {
|
|
font-size: 34px;
|
|
}
|
|
.label {
|
|
font-size: 11px;
|
|
letter-spacing: 2px;
|
|
opacity: 0.6;
|
|
}
|
|
.name {
|
|
font-weight: 700;
|
|
margin-top: 2px;
|
|
}
|
|
.desc {
|
|
font-size: 12px;
|
|
opacity: 0.7;
|
|
margin-top: 2px;
|
|
}
|
|
.slide-enter-active,
|
|
.slide-leave-active {
|
|
transition: all 0.35s ease;
|
|
}
|
|
.slide-enter-from,
|
|
.slide-leave-to {
|
|
opacity: 0;
|
|
transform: translateX(40px);
|
|
}
|
|
</style>
|