@@ -338,25 +338,6 @@ export function joinProblemSet(problemSetId: number) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getProblemSetSubmissions(
|
||||
problemSetId: number,
|
||||
params: {
|
||||
problem_id?: string
|
||||
result?: string
|
||||
language?: string
|
||||
offset?: number
|
||||
limit?: number
|
||||
} = {},
|
||||
) {
|
||||
return http.get(`problemset/${problemSetId}/submissions`, {
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export function getProblemSetStatistics(problemSetId: number) {
|
||||
return http.get(`problemset/${problemSetId}/statistics`)
|
||||
}
|
||||
|
||||
export function updateProblemSetProgress(
|
||||
problemSetId: number,
|
||||
problemId: number,
|
||||
|
||||
@@ -24,7 +24,8 @@ const problemStore = useProblemStore()
|
||||
const { problem } = storeToRefs(problemStore)
|
||||
const route = useRoute()
|
||||
const contestID = <string>route.params.contestID ?? ""
|
||||
const problemSetID = computed(() => route.params.problemSetID as string || "")
|
||||
const problemSetId = <string>route.params.problemSetId ?? ""
|
||||
console.log(problemSetId, "problemSetId")
|
||||
const [commentPanel] = useToggle()
|
||||
|
||||
const { isDesktop } = useBreakpoints()
|
||||
@@ -100,8 +101,8 @@ async function submit() {
|
||||
if (contestID) {
|
||||
data.contest_id = parseInt(contestID)
|
||||
}
|
||||
if (problemSetID.value) {
|
||||
data.problemset_id = parseInt(problemSetID.value)
|
||||
if (problemSetId) {
|
||||
data.problemset_id = parseInt(problemSetId)
|
||||
}
|
||||
|
||||
// 2. 提交代码到后端
|
||||
@@ -123,19 +124,20 @@ watch(
|
||||
problem.value!.my_status = 0
|
||||
|
||||
// 2. 更新题单进度
|
||||
if (problemSetID.value) {
|
||||
if (problemSetId) {
|
||||
console.log(problemSetId, "problemSetId")
|
||||
try {
|
||||
await updateProblemSetProgress(Number(problemSetID.value), problem.value!.id)
|
||||
await updateProblemSetProgress(Number(problemSetId), problem.value!.id)
|
||||
} catch (error) {
|
||||
console.error("更新题单进度失败:", error)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 放烟花(随机效果)
|
||||
// 3. 放烟花
|
||||
celebrate()
|
||||
|
||||
// 4. 显示评价框(非比赛模式)
|
||||
if (!contestID) {
|
||||
// 4. 显示评价框
|
||||
if (!contestID && !problemSetId) {
|
||||
showCommentPanelDelayed()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { getProblemSetStatistics } from "oj/api"
|
||||
import { ProblemSetStatistics } from "utils/types"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
import { NTag } from "naive-ui"
|
||||
import { h } from "vue"
|
||||
|
||||
interface Props {
|
||||
problemSetId: number
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { isDesktop } = useBreakpoints()
|
||||
const message = useMessage()
|
||||
|
||||
const statistics = ref<ProblemSetStatistics | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
// 结果状态映射
|
||||
const resultStatusMap: Record<string, { text: string; type: string }> = {
|
||||
"-2": { text: "编译错误", type: "error" },
|
||||
"-1": { text: "答案错误", type: "error" },
|
||||
"0": { text: "通过", type: "success" },
|
||||
"1": { text: "时间超限", type: "warning" },
|
||||
"2": { text: "时间超限", type: "warning" },
|
||||
"3": { text: "内存超限", type: "warning" },
|
||||
"4": { text: "运行时错误", type: "error" },
|
||||
"5": { text: "系统错误", type: "error" },
|
||||
"6": { text: "等待中", type: "info" },
|
||||
"7": { text: "评测中", type: "info" },
|
||||
"8": { text: "部分通过", type: "warning" },
|
||||
}
|
||||
|
||||
async function loadStatistics() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getProblemSetStatistics(props.problemSetId)
|
||||
statistics.value = res.data
|
||||
} catch (err: any) {
|
||||
message.error("加载统计信息失败:" + (err.data || "未知错误"))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadStatistics()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-flex vertical size="large" v-if="statistics">
|
||||
<!-- 总体统计 -->
|
||||
<n-card title="总体统计">
|
||||
<n-grid :cols="isDesktop ? 4 : 2" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item>
|
||||
<n-statistic label="总提交数" :value="statistics.total_submissions">
|
||||
<template #prefix>
|
||||
<Icon icon="streamline-emojis:file" />
|
||||
</template>
|
||||
</n-statistic>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-statistic label="通过数" :value="statistics.accepted_submissions">
|
||||
<template #prefix>
|
||||
<Icon icon="streamline-emojis:check-mark" />
|
||||
</template>
|
||||
</n-statistic>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-statistic label="通过率" :value="`${statistics.acceptance_rate}%`">
|
||||
<template #prefix>
|
||||
<Icon icon="streamline-emojis:chart-increasing" />
|
||||
</template>
|
||||
</n-statistic>
|
||||
</n-grid-item>
|
||||
<n-grid-item>
|
||||
<n-statistic label="完成进度" :value="`${statistics.progress.progress_percentage.toFixed(1)}%`">
|
||||
<template #prefix>
|
||||
<Icon icon="streamline-emojis:target" />
|
||||
</template>
|
||||
</n-statistic>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
|
||||
<!-- 进度信息 -->
|
||||
<n-card title="题单进度">
|
||||
<n-flex vertical size="medium">
|
||||
<n-flex justify="space-between" align="center">
|
||||
<n-text>完成题目数</n-text>
|
||||
<n-text strong>
|
||||
{{ statistics.progress.completed_problems_count }} / {{ statistics.progress.total_problems_count }}
|
||||
</n-text>
|
||||
</n-flex>
|
||||
<n-progress
|
||||
:percentage="statistics.progress.progress_percentage"
|
||||
:height="8"
|
||||
:border-radius="4"
|
||||
/>
|
||||
<n-flex justify="space-between" align="center">
|
||||
<n-text>总得分</n-text>
|
||||
<n-text strong>{{ statistics.progress.total_score }} 分</n-text>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
|
||||
<!-- 题目统计 -->
|
||||
<n-card title="题目统计">
|
||||
<n-data-table
|
||||
:columns="[
|
||||
{
|
||||
title: '题目',
|
||||
key: 'problem_title',
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
title: '总提交',
|
||||
key: 'total_submissions',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '通过',
|
||||
key: 'accepted_submissions',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
key: 'is_completed',
|
||||
width: 100,
|
||||
render: (row) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.is_completed ? 'success' : 'default',
|
||||
},
|
||||
() => row.is_completed ? '已完成' : '未完成'
|
||||
)
|
||||
},
|
||||
},
|
||||
]"
|
||||
:data="Object.values(statistics.problem_stats)"
|
||||
:pagination="false"
|
||||
/>
|
||||
</n-card>
|
||||
|
||||
<!-- 语言统计 -->
|
||||
<n-card title="语言统计" v-if="Object.keys(statistics.language_stats).length > 0">
|
||||
<n-flex vertical size="small">
|
||||
<n-flex
|
||||
v-for="(count, language) in statistics.language_stats"
|
||||
:key="language"
|
||||
justify="space-between"
|
||||
align="center"
|
||||
>
|
||||
<n-text>{{ language }}</n-text>
|
||||
<n-text strong>{{ count }} 次</n-text>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
|
||||
<!-- 结果统计 -->
|
||||
<n-card title="结果统计" v-if="Object.keys(statistics.result_stats).length > 0">
|
||||
<n-flex vertical size="small">
|
||||
<n-flex
|
||||
v-for="(count, result) in statistics.result_stats"
|
||||
:key="result"
|
||||
justify="space-between"
|
||||
align="center"
|
||||
>
|
||||
<n-tag
|
||||
:type="(resultStatusMap[String(result)]?.type as any) || 'default'"
|
||||
size="small"
|
||||
>
|
||||
{{ resultStatusMap[String(result)]?.text || '未知' }}
|
||||
</n-tag>
|
||||
<n-text strong>{{ count }} 次</n-text>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</n-flex>
|
||||
|
||||
<n-empty v-else-if="!loading" description="暂无统计信息" />
|
||||
<n-spin v-else size="large" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -1,252 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { getProblemSetSubmissions } from "oj/api"
|
||||
import { parseTime } from "utils/functions"
|
||||
import { ProblemSetSubmission } from "utils/types"
|
||||
import { LANGUAGE_SHOW_VALUE } from "utils/constants"
|
||||
import { renderTableTitle } from "utils/renders"
|
||||
import SubmissionResultTag from "shared/components/SubmissionResultTag.vue"
|
||||
import Pagination from "shared/components/Pagination.vue"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
import { usePagination } from "shared/composables/pagination"
|
||||
import SubmissionDetail from "oj/submission/detail.vue"
|
||||
import { NButton } from "naive-ui"
|
||||
|
||||
interface Props {
|
||||
problemSetId: number
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { isDesktop } = useBreakpoints()
|
||||
const message = useMessage()
|
||||
const router = useRouter()
|
||||
|
||||
// 弹框状态管理
|
||||
const [codePanelVisible, toggleCodePanel] = useToggle(false)
|
||||
const submissionID = ref("")
|
||||
const problemID = ref("")
|
||||
|
||||
// 显示代码弹框
|
||||
function showCodePanel(id: string, problem: string) {
|
||||
submissionID.value = id
|
||||
problemID.value = problem
|
||||
toggleCodePanel(true)
|
||||
}
|
||||
|
||||
// 使用分页 composable
|
||||
const { query, clearQuery } = usePagination({
|
||||
problem_id: "",
|
||||
result: "",
|
||||
language: "",
|
||||
})
|
||||
|
||||
const submissions = ref<ProblemSetSubmission[]>([])
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
const columns = computed(() => {
|
||||
const baseColumns = [
|
||||
{
|
||||
title: renderTableTitle("提交时间", "noto:seven-oclock"),
|
||||
key: "submit_time",
|
||||
minWidth: 200,
|
||||
render: (row: ProblemSetSubmission) => parseTime(row.submit_time, "YYYY-MM-DD HH:mm:ss"),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("提交编号", "fluent-emoji-flat:input-numbers"),
|
||||
key: "id",
|
||||
minWidth: 200,
|
||||
render: (row: ProblemSetSubmission) => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: "info",
|
||||
onClick: () => {
|
||||
showCodePanel(row.submission, row.problem.toString())
|
||||
},
|
||||
},
|
||||
() => row.submission.slice(0, 12),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("状态", "streamline-emojis:panda-face"),
|
||||
key: "result",
|
||||
minWidth: 140,
|
||||
render: (row: ProblemSetSubmission) => h(SubmissionResultTag, { result: row.result as any }),
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("题目", "streamline-emojis:blossom"),
|
||||
key: "problem_title",
|
||||
minWidth: 300,
|
||||
render: (row: ProblemSetSubmission) => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: "primary",
|
||||
onClick: () => {
|
||||
router.push(`/problem/${row.problem_id}`)
|
||||
},
|
||||
},
|
||||
() => `${row.problem_id} ${row.problem_title}`,
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("语言", "streamline-emojis:globe-showing-europe-africa"),
|
||||
key: "language",
|
||||
minWidth: 120,
|
||||
render: (row: ProblemSetSubmission) => LANGUAGE_SHOW_VALUE[row.language as keyof typeof LANGUAGE_SHOW_VALUE] || row.language,
|
||||
},
|
||||
]
|
||||
|
||||
if (isDesktop.value) {
|
||||
baseColumns.push(
|
||||
{
|
||||
title: renderTableTitle("代码长度", "streamline-emojis:file"),
|
||||
key: "code_length",
|
||||
minWidth: 100,
|
||||
render: (row: ProblemSetSubmission) => `${row.code_length} 字符`,
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("执行时间", "streamline-emojis:stopwatch"),
|
||||
key: "execution_time",
|
||||
minWidth: 100,
|
||||
render: (row: ProblemSetSubmission) => `${row.execution_time}ms`,
|
||||
},
|
||||
{
|
||||
title: renderTableTitle("内存使用", "streamline-emojis:brain"),
|
||||
key: "memory_usage",
|
||||
minWidth: 100,
|
||||
render: (row: ProblemSetSubmission) => `${row.memory_usage}KB`,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return baseColumns
|
||||
})
|
||||
|
||||
async function loadSubmissions() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getProblemSetSubmissions(props.problemSetId, {
|
||||
...query,
|
||||
offset: query.limit * (query.page - 1),
|
||||
})
|
||||
submissions.value = res.data.results
|
||||
total.value = res.data.total
|
||||
} catch (err: any) {
|
||||
message.error("加载提交记录失败:" + (err.data || "未知错误"))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function search() {
|
||||
query.page = 1
|
||||
loadSubmissions()
|
||||
}
|
||||
|
||||
function clear() {
|
||||
clearQuery()
|
||||
search()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSubmissions()
|
||||
})
|
||||
|
||||
watch(query, loadSubmissions)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-flex vertical size="large">
|
||||
<!-- 搜索表单 -->
|
||||
<n-form :show-feedback="false" inline label-placement="left">
|
||||
<n-form-item label="题目ID">
|
||||
<n-input
|
||||
v-model:value="query.problem_id"
|
||||
placeholder="输入题目ID"
|
||||
clearable
|
||||
@keyup.enter="search"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="结果">
|
||||
<n-select
|
||||
v-model:value="query.result"
|
||||
placeholder="选择结果"
|
||||
clearable
|
||||
:options="[
|
||||
{ label: '通过', value: '0' },
|
||||
{ label: '答案错误', value: '-1' },
|
||||
{ label: '编译错误', value: '-2' },
|
||||
{ label: '时间超限', value: '1' },
|
||||
{ label: '内存超限', value: '3' },
|
||||
{ label: '运行时错误', value: '4' },
|
||||
{ label: '系统错误', value: '5' },
|
||||
{ label: '等待中', value: '6' },
|
||||
{ label: '评测中', value: '7' },
|
||||
{ label: '部分通过', value: '8' },
|
||||
]"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="语言">
|
||||
<n-select
|
||||
v-model:value="query.language"
|
||||
placeholder="选择语言"
|
||||
clearable
|
||||
:options="[
|
||||
{ label: 'C', value: 'C' },
|
||||
{ label: 'C++', value: 'C++' },
|
||||
{ label: 'Java', value: 'Java' },
|
||||
{ label: 'Python', value: 'Python' },
|
||||
{ label: 'JavaScript', value: 'JavaScript' },
|
||||
]"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button @click="search" type="primary">搜索</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button @click="clear" quaternary>重置</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
<!-- 提交记录表格 -->
|
||||
<n-data-table
|
||||
v-if="submissions.length > 0"
|
||||
:bordered="false"
|
||||
:columns="columns"
|
||||
:data="submissions"
|
||||
:loading="loading"
|
||||
/>
|
||||
<n-empty v-else-if="!loading" description="暂无提交记录" />
|
||||
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:limit="query.limit"
|
||||
v-model:page="query.page"
|
||||
/>
|
||||
|
||||
<!-- 代码详情弹框 -->
|
||||
<n-modal
|
||||
v-model:show="codePanelVisible"
|
||||
preset="card"
|
||||
:style="{ maxWidth: isDesktop && '70vw', maxHeight: '80vh' }"
|
||||
:content-style="{ overflow: 'auto' }"
|
||||
title="代码详情"
|
||||
>
|
||||
<SubmissionDetail
|
||||
:problemID="problemID"
|
||||
:submissionID="submissionID"
|
||||
hideList
|
||||
/>
|
||||
</n-modal>
|
||||
</n-flex>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -5,11 +5,9 @@ import {
|
||||
getProblemSetProblems,
|
||||
joinProblemSet,
|
||||
} from "../api"
|
||||
import { parseTime, getTagColor, getACRate } from "utils/functions"
|
||||
import { getTagColor, getACRate } from "utils/functions"
|
||||
import { ProblemSet, ProblemSetProblem } from "utils/types"
|
||||
import ProblemStatus from "../problem/components/ProblemStatus.vue"
|
||||
import ProblemSetSubmissions from "./components/ProblemSetSubmissions.vue"
|
||||
import ProblemSetStatistics from "./components/ProblemSetStatistics.vue"
|
||||
import { DIFFICULTY } from "utils/constants"
|
||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
||||
|
||||
@@ -39,23 +37,6 @@ async function refreshProblemSetDetail() {
|
||||
}
|
||||
}
|
||||
|
||||
// 标签页状态
|
||||
const activeTab = ref("problems")
|
||||
const tabOptions = computed(() => {
|
||||
const options = [
|
||||
{ label: "题目列表", value: "problems" }
|
||||
]
|
||||
|
||||
if (isJoined.value) {
|
||||
options.push(
|
||||
{ label: "提交记录", value: "submissions" },
|
||||
{ label: "统计信息", value: "statistics" }
|
||||
)
|
||||
}
|
||||
|
||||
return options
|
||||
})
|
||||
|
||||
function getDifficultyTag(difficulty: string) {
|
||||
const difficultyMap: Record<
|
||||
string,
|
||||
@@ -138,10 +119,14 @@ watch(
|
||||
() => route.path,
|
||||
(newPath, oldPath) => {
|
||||
// 如果从题单题目页面返回到题单详情页面,刷新题单详情
|
||||
if (oldPath?.includes('/problem/') && newPath === `/problemset/${problemSetId.value}` && isJoined.value) {
|
||||
if (
|
||||
oldPath?.includes("/problem/") &&
|
||||
newPath === `/problemset/${problemSetId.value}` &&
|
||||
isJoined.value
|
||||
) {
|
||||
refreshProblemSetDetail()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -198,76 +183,66 @@ watch(
|
||||
</n-flex>
|
||||
</n-card>
|
||||
|
||||
<!-- 标签页 -->
|
||||
<n-tabs v-model:value="activeTab" type="segment">
|
||||
<n-tab-pane name="problems" tab="题目列表">
|
||||
<n-grid :cols="isDesktop ? 4 : 1" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item
|
||||
v-for="(problemSetProblem, index) in problems"
|
||||
:key="problemSetProblem.id"
|
||||
>
|
||||
<n-card
|
||||
hoverable
|
||||
@click="goToProblem(problemSetProblem.problem._id)"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<n-flex>
|
||||
<!-- 题目列表 -->
|
||||
<n-grid :cols="isDesktop ? 4 : 1" :x-gap="16" :y-gap="16">
|
||||
<n-grid-item
|
||||
v-for="(problemSetProblem, index) in problems"
|
||||
:key="problemSetProblem.id"
|
||||
>
|
||||
<n-card
|
||||
hoverable
|
||||
@click="goToProblem(problemSetProblem.problem._id)"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<n-flex>
|
||||
<n-flex align="center">
|
||||
<n-h2 style="margin: 0 20px 0 0">#{{ index + 1 }}. </n-h2>
|
||||
|
||||
<n-flex vertical style="flex: 1">
|
||||
<n-flex align="center">
|
||||
<n-h2 style="margin: 0 20px 0 0">#{{ index + 1 }}. </n-h2>
|
||||
|
||||
<n-flex vertical style="flex: 1">
|
||||
<n-flex align="center">
|
||||
<n-tag
|
||||
:type="getTagColor(problemSetProblem.problem.difficulty)"
|
||||
size="small"
|
||||
>
|
||||
{{ DIFFICULTY[problemSetProblem.problem.difficulty] }}
|
||||
</n-tag>
|
||||
<n-h4 style="margin: 0">
|
||||
{{ problemSetProblem.problem.title }}
|
||||
</n-h4>
|
||||
</n-flex>
|
||||
|
||||
<n-flex align="center" size="small">
|
||||
<n-text type="info">
|
||||
分数:{{ problemSetProblem.score }}
|
||||
</n-text>
|
||||
<n-text v-if="!problemSetProblem.is_required">(选做)</n-text>
|
||||
<n-text depth="3" style="font-size: 12px">
|
||||
通过率
|
||||
{{
|
||||
getACRate(
|
||||
problemSetProblem.problem.accepted_number,
|
||||
problemSetProblem.problem.submission_number,
|
||||
)
|
||||
}}
|
||||
</n-text>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
<n-tag
|
||||
:type="getTagColor(problemSetProblem.problem.difficulty)"
|
||||
size="small"
|
||||
>
|
||||
{{ DIFFICULTY[problemSetProblem.problem.difficulty] }}
|
||||
</n-tag>
|
||||
<n-h4 style="margin: 0">
|
||||
{{ problemSetProblem.problem.title }}
|
||||
</n-h4>
|
||||
</n-flex>
|
||||
|
||||
<n-flex align="center">
|
||||
<ProblemStatus
|
||||
:status="getProblemStatus(problemSetProblem.problem.my_status)"
|
||||
/>
|
||||
<n-icon>
|
||||
<Icon icon="streamline-emojis:right-arrow" />
|
||||
</n-icon>
|
||||
<n-flex align="center" size="small">
|
||||
<n-text type="info">
|
||||
分数:{{ problemSetProblem.score }}
|
||||
</n-text>
|
||||
<n-text v-if="!problemSetProblem.is_required"
|
||||
>(选做)</n-text
|
||||
>
|
||||
<n-text depth="3" style="font-size: 12px">
|
||||
通过率
|
||||
{{
|
||||
getACRate(
|
||||
problemSetProblem.problem.accepted_number,
|
||||
problemSetProblem.problem.submission_number,
|
||||
)
|
||||
}}
|
||||
</n-text>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-tab-pane>
|
||||
</n-flex>
|
||||
|
||||
<n-tab-pane v-if="isJoined" name="submissions" tab="提交记录">
|
||||
<ProblemSetSubmissions :problemSetId="problemSetId" />
|
||||
</n-tab-pane>
|
||||
|
||||
<n-tab-pane v-if="isJoined" name="statistics" tab="统计信息">
|
||||
<ProblemSetStatistics :problemSetId="problemSetId" />
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
<n-flex align="center">
|
||||
<ProblemStatus
|
||||
:status="getProblemStatus(problemSetProblem.problem.my_status)"
|
||||
/>
|
||||
<n-icon>
|
||||
<Icon icon="streamline-emojis:right-arrow" />
|
||||
</n-icon>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-card>
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -604,41 +604,4 @@ export interface DetailsData {
|
||||
|
||||
export type Grade = "S" | "A" | "B" | "C"
|
||||
|
||||
// 题单提交记录相关类型
|
||||
export interface ProblemSetSubmission {
|
||||
id: number
|
||||
problem: number
|
||||
problem_id: number
|
||||
problem_title: string
|
||||
submission: string
|
||||
result: number
|
||||
result_text: string
|
||||
score: number
|
||||
language: string
|
||||
code_length: number
|
||||
execution_time: number
|
||||
memory_usage: number
|
||||
submit_time: string
|
||||
}
|
||||
|
||||
export interface ProblemSetStatistics {
|
||||
total_submissions: number
|
||||
accepted_submissions: number
|
||||
acceptance_rate: number
|
||||
problem_stats: {
|
||||
[problemId: string]: {
|
||||
problem_title: string
|
||||
total_submissions: number
|
||||
accepted_submissions: number
|
||||
is_completed: boolean
|
||||
}
|
||||
}
|
||||
language_stats: { [language: string]: number }
|
||||
result_stats: { [result: number]: number }
|
||||
progress: {
|
||||
completed_problems_count: number
|
||||
total_problems_count: number
|
||||
progress_percentage: number
|
||||
total_score: number
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user