Files
webpreview/src/components/Challenge.vue
yuetsh a7aa4f63ac
Some checks failed
Deploy / deploy (build, debian, 22) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822) (push) Has been cancelled
add leaderboard
2026-03-09 20:00:11 +08:00

108 lines
3.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="container" v-if="taskTab === TASK_TYPE.Challenge">
<template v-if="currentChallenge">
<n-flex align="center" style="margin-bottom: 12px">
<n-button text @click="back">
<Icon :width="20" icon="pepicons-pencil:arrow-left"></Icon>
</n-button>
<span style="font-weight: bold">返回挑战列表</span>
</n-flex>
<div class="markdown-body" v-html="content" />
</template>
<template v-else>
<n-empty v-if="!challenges.length">暂无挑战敬请期待</n-empty>
<n-grid v-else :cols="3" x-gap="12" y-gap="12">
<n-gi v-for="item in challenges" :key="item.display">
<n-card hoverable class="challenge-card" @click="select(item)">
<template #header>
{{ item.title }}
</template>
<template #header-extra>
<n-tag type="warning" size="small">{{ item.score }}</n-tag>
<n-tag
v-if="myScoreMap.get(item.display)"
type="success"
size="small"
style="margin-left: 4px"
>
得分 {{ myScoreMap.get(item.display)!.toFixed(1) }}
</n-tag>
</template>
</n-card>
</n-gi>
</n-grid>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue"
import { Icon } from "@iconify/vue"
import { marked } from "marked"
import { useRouter } from "vue-router"
import { Challenge, Submission } from "../api"
import { taskTab, taskId, challengeDisplay } from "../store/task"
import { TASK_TYPE } from "../utils/const"
import type { ChallengeSlim } from "../utils/type"
const router = useRouter()
const challenges = ref<ChallengeSlim[]>([])
const currentChallenge = ref<ChallengeSlim | null>(null)
const content = ref("")
const myScoreMap = ref<Map<number, number>>(new Map())
async function loadMyScores() {
try {
const scores = await Submission.myScores()
myScoreMap.value = new Map(scores.map((s) => [s.task_display, s.score]))
} catch {
// 未登录时忽略
}
}
async function loadList() {
challenges.value = await Challenge.listDisplay()
// 从 URL 恢复选中状态
if (challengeDisplay.value) {
const item = challenges.value.find(
(c) => c.display === challengeDisplay.value,
)
if (item) await select(item, false)
}
}
async function select(item: ChallengeSlim, updateUrl = true) {
currentChallenge.value = item
challengeDisplay.value = item.display
if (updateUrl) {
router.push({ name: "home-challenge", params: { display: item.display } })
}
const data = await Challenge.get(item.display)
taskId.value = data.task_ptr
const merged = `# #${data.display} ${data.title}\n${data.content}`
content.value = await marked.parse(merged, { async: true })
}
function back() {
currentChallenge.value = null
challengeDisplay.value = 0
taskId.value = 0
content.value = ""
router.push({ name: "home-challenge-list" })
}
onMounted(async () => {
await loadList()
await loadMyScores()
})
</script>
<style scoped>
.container {
padding: 16px;
overflow: auto;
}
.challenge-card {
cursor: pointer;
}
</style>