@@ -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(
|
export function updateProblemSetProgress(
|
||||||
problemSetId: number,
|
problemSetId: number,
|
||||||
problemId: number,
|
problemId: number,
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ const problemStore = useProblemStore()
|
|||||||
const { problem } = storeToRefs(problemStore)
|
const { problem } = storeToRefs(problemStore)
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const contestID = <string>route.params.contestID ?? ""
|
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 [commentPanel] = useToggle()
|
||||||
|
|
||||||
const { isDesktop } = useBreakpoints()
|
const { isDesktop } = useBreakpoints()
|
||||||
@@ -100,8 +101,8 @@ async function submit() {
|
|||||||
if (contestID) {
|
if (contestID) {
|
||||||
data.contest_id = parseInt(contestID)
|
data.contest_id = parseInt(contestID)
|
||||||
}
|
}
|
||||||
if (problemSetID.value) {
|
if (problemSetId) {
|
||||||
data.problemset_id = parseInt(problemSetID.value)
|
data.problemset_id = parseInt(problemSetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 提交代码到后端
|
// 2. 提交代码到后端
|
||||||
@@ -123,19 +124,20 @@ watch(
|
|||||||
problem.value!.my_status = 0
|
problem.value!.my_status = 0
|
||||||
|
|
||||||
// 2. 更新题单进度
|
// 2. 更新题单进度
|
||||||
if (problemSetID.value) {
|
if (problemSetId) {
|
||||||
|
console.log(problemSetId, "problemSetId")
|
||||||
try {
|
try {
|
||||||
await updateProblemSetProgress(Number(problemSetID.value), problem.value!.id)
|
await updateProblemSetProgress(Number(problemSetId), problem.value!.id)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("更新题单进度失败:", error)
|
console.error("更新题单进度失败:", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 放烟花(随机效果)
|
// 3. 放烟花
|
||||||
celebrate()
|
celebrate()
|
||||||
|
|
||||||
// 4. 显示评价框(非比赛模式)
|
// 4. 显示评价框
|
||||||
if (!contestID) {
|
if (!contestID && !problemSetId) {
|
||||||
showCommentPanelDelayed()
|
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,
|
getProblemSetProblems,
|
||||||
joinProblemSet,
|
joinProblemSet,
|
||||||
} from "../api"
|
} from "../api"
|
||||||
import { parseTime, getTagColor, getACRate } from "utils/functions"
|
import { getTagColor, getACRate } from "utils/functions"
|
||||||
import { ProblemSet, ProblemSetProblem } from "utils/types"
|
import { ProblemSet, ProblemSetProblem } from "utils/types"
|
||||||
import ProblemStatus from "../problem/components/ProblemStatus.vue"
|
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 { DIFFICULTY } from "utils/constants"
|
||||||
import { useBreakpoints } from "shared/composables/breakpoints"
|
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) {
|
function getDifficultyTag(difficulty: string) {
|
||||||
const difficultyMap: Record<
|
const difficultyMap: Record<
|
||||||
string,
|
string,
|
||||||
@@ -138,10 +119,14 @@ watch(
|
|||||||
() => route.path,
|
() => route.path,
|
||||||
(newPath, oldPath) => {
|
(newPath, oldPath) => {
|
||||||
// 如果从题单题目页面返回到题单详情页面,刷新题单详情
|
// 如果从题单题目页面返回到题单详情页面,刷新题单详情
|
||||||
if (oldPath?.includes('/problem/') && newPath === `/problemset/${problemSetId.value}` && isJoined.value) {
|
if (
|
||||||
|
oldPath?.includes("/problem/") &&
|
||||||
|
newPath === `/problemset/${problemSetId.value}` &&
|
||||||
|
isJoined.value
|
||||||
|
) {
|
||||||
refreshProblemSetDetail()
|
refreshProblemSetDetail()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -198,9 +183,7 @@ watch(
|
|||||||
</n-flex>
|
</n-flex>
|
||||||
</n-card>
|
</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 :cols="isDesktop ? 4 : 1" :x-gap="16" :y-gap="16">
|
||||||
<n-grid-item
|
<n-grid-item
|
||||||
v-for="(problemSetProblem, index) in problems"
|
v-for="(problemSetProblem, index) in problems"
|
||||||
@@ -232,7 +215,9 @@ watch(
|
|||||||
<n-text type="info">
|
<n-text type="info">
|
||||||
分数:{{ problemSetProblem.score }}
|
分数:{{ problemSetProblem.score }}
|
||||||
</n-text>
|
</n-text>
|
||||||
<n-text v-if="!problemSetProblem.is_required">(选做)</n-text>
|
<n-text v-if="!problemSetProblem.is_required"
|
||||||
|
>(选做)</n-text
|
||||||
|
>
|
||||||
<n-text depth="3" style="font-size: 12px">
|
<n-text depth="3" style="font-size: 12px">
|
||||||
通过率
|
通过率
|
||||||
{{
|
{{
|
||||||
@@ -258,16 +243,6 @@ watch(
|
|||||||
</n-card>
|
</n-card>
|
||||||
</n-grid-item>
|
</n-grid-item>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
</n-tab-pane>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -604,41 +604,4 @@ export interface DetailsData {
|
|||||||
|
|
||||||
export type Grade = "S" | "A" | "B" | "C"
|
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