This commit is contained in:
2023-01-31 16:29:58 +08:00
parent d5d6491d6d
commit 7b0163885a
3 changed files with 113 additions and 82 deletions

View File

@@ -1,82 +1,35 @@
<script setup lang="ts">
import { Contest, Problem } from "utils/types"
import { CONTEST_STATUS, ContestStatus, ContestType } from "utils/constants"
import { parseTime } from "utils/functions"
import {
getContest,
getContestAccess,
getContestProblem,
checkContestPassword,
} from "../api"
import { isDesktop } from "~/shared/composables/breakpoints"
import ContestTypeVue from "./components/ContestType.vue"
import { useUserStore } from "~/shared/store/user"
import { useContestStore } from "../store/contest"
const props = defineProps<{
contestID: string
}>()
const message = useMessage()
const contestStore = useContestStore()
const route = useRoute()
const router = useRouter()
const userStore = useUserStore()
const contest = ref<Contest>()
const problems = ref<Problem[]>([])
const password = ref("")
const [access, toggleAccsess] = useToggle()
async function init() {
const res = await getContest(props.contestID)
contest.value = res.data
if (contest.value?.contest_type === ContestType.private) {
const res = await getContestAccess(props.contestID)
toggleAccsess(res.data.access)
onMounted(() => contestStore.init(props.contestID))
const contestMenuVisible = computed(() => {
if (contestStore.isContestAdmin) return true
if (contestStore.contest?.contest_type === ContestType.public) {
// TODO:这里没有完成
}
getProblems()
}
onMounted(init)
async function getProblems() {
try {
problems.value = await getContestProblem(props.contestID)
} catch (err) {
toggleAccsess(false)
}
}
async function checkPassword() {
try {
const res = await checkContestPassword(props.contestID, password.value)
toggleAccsess(res.data)
if (res.data) {
getProblems()
}
} catch (err) {
toggleAccsess(false)
message.error("密码错误")
}
}
const isContestAdmin = computed(
() =>
userStore.isSuperAdmin ||
(userStore.isAuthed && contest.value?.created_by.id === userStore.user.id)
)
return contestStore.access
})
const passwordFormVisible = computed(
() =>
contest.value?.contest_type === ContestType.private &&
!access.value &&
!isContestAdmin.value
contestStore.contest?.contest_type === ContestType.private &&
!contestStore.access &&
!contestStore.isContestAdmin
)
const contestMenuVisible = computed(() => {
if (isContestAdmin) return true
if (contest.value?.contest_type === ContestType.public) {
}
return access.value
})
function goto(name: string) {
router.push({ name: "contest " + name })
}
@@ -88,31 +41,35 @@ function getCurrentType(name: string): "primary" | "default" {
</script>
<template>
<div v-if="contest">
<div v-if="contestStore.contest">
<n-space align="center">
<n-tag :type="CONTEST_STATUS[contest.status]['type']">
{{ CONTEST_STATUS[contest.status]["name"] }}
<n-tag :type="CONTEST_STATUS[contestStore.contest.status]['type']">
{{ CONTEST_STATUS[contestStore.contest.status]["name"] }}
</n-tag>
<h2 class="contestTitle">{{ contest.title }}</h2>
<h2 class="contestTitle">{{ contestStore.contest.title }}</h2>
<n-icon
v-if="contest.contest_type === ContestType.private"
v-if="contestStore.contest.contest_type === ContestType.private"
class="lockIcon"
>
<i-ep-lock />
</n-icon>
</n-space>
<div v-html="contest.description"></div>
<div v-html="contestStore.contest.description"></div>
<n-form :inline="isDesktop" label-placement="left">
<n-form-item v-if="passwordFormVisible" label="需要输入密码才能看到题目">
<n-input
name="ContestPassword"
type="password"
v-model:value="password"
@change="checkPassword"
/>
</n-form-item>
<n-form-item v-if="passwordFormVisible">
<n-button @click="checkPassword" :disabled="!password">确认</n-button>
<n-button
@click="contestStore.checkPassword(contestID, password)"
:disabled="!password"
>
确认
</n-button>
</n-form-item>
<n-form-item v-if="contestMenuVisible">
<n-space>
@@ -137,39 +94,43 @@ function getCurrentType(name: string): "primary" | "default" {
</n-space>
</n-form-item>
</n-form>
<n-descriptions class="margin" bordered :column="isDesktop ? 5 : 2">
<n-descriptions class="bottom" bordered :column="isDesktop ? 5 : 2">
<n-descriptions-item
:span="isDesktop ? 1 : 2"
v-if="contest.status !== ContestStatus.finished"
v-if="contestStore.contest.status !== ContestStatus.finished"
>
<template #label>
<span v-if="contest.status === ContestStatus.not_started">
<span
v-if="contestStore.contest.status === ContestStatus.not_started"
>
距离比赛开始还有
</span>
<span v-if="contest.status === ContestStatus.underway">
<span v-if="contestStore.contest.status === ContestStatus.underway">
距离比赛结束还有
</span>
</template>
<n-space align="center">
<n-tag :type="CONTEST_STATUS[contest.status]['type']">
<n-tag :type="CONTEST_STATUS[contestStore.contest.status]['type']">
<n-countdown :duration="500000" />
</n-tag>
</n-space>
</n-descriptions-item>
<n-descriptions-item label="开始时间">
{{ parseTime(contest.start_time, "YYYY年M月D日 hh:mm:ss") }}
{{
parseTime(contestStore.contest.start_time, "YYYY年M月D日 hh:mm:ss")
}}
</n-descriptions-item>
<n-descriptions-item label="结束时间">
{{ parseTime(contest.end_time, "YYYY年M月D日 hh:mm:ss") }}
{{ parseTime(contestStore.contest.end_time, "YYYY年M月D日 hh:mm:ss") }}
</n-descriptions-item>
<n-descriptions-item label="比赛类型">
<ContestTypeVue :contest="contest" />
<ContestTypeVue :contest="contestStore.contest" />
</n-descriptions-item>
<n-descriptions-item label="发起人">
{{ contest.created_by.username }}
{{ contestStore.contest.created_by.username }}
</n-descriptions-item>
</n-descriptions>
<router-view :problems="problems"></router-view>
<router-view></router-view>
</div>
</template>
@@ -182,7 +143,7 @@ function getCurrentType(name: string): "primary" | "default" {
transform: translateY(2px);
}
.margin {
.bottom {
margin-bottom: 24px;
}
</style>

View File

@@ -2,13 +2,14 @@
import { DataTableColumn } from "naive-ui"
import { ProblemFiltered } from "utils/types"
import ProblemStatus from "~/oj/problem/components/ProblemStatus.vue"
import { useContestStore } from "~/oj/store/contest"
const props = defineProps<{
contestID: string
problems: ProblemFiltered[]
}>()
const router = useRouter()
const contestStore = useContestStore()
const problemsColumns: DataTableColumn<ProblemFiltered>[] = [
{
title: "状态",
@@ -37,10 +38,10 @@ function rowProps(row: ProblemFiltered) {
striped
size="small"
class="problems"
:data="problems"
:data="contestStore.problems"
:columns="problemsColumns"
:row-props="rowProps"
v-if="problems?.length"
v-if="contestStore.problems?.length"
/>
</template>

69
src/oj/store/contest.ts Normal file
View File

@@ -0,0 +1,69 @@
import { useUserStore } from "~/shared/store/user"
import { ContestType } from "~/utils/constants"
import { Contest, Problem } from "~/utils/types"
import {
getContest,
getContestAccess,
getContestProblem,
checkContestPassword,
} from "../api"
export const useContestStore = defineStore("contest", () => {
const userStore = useUserStore()
const contest = ref<Contest>()
const [access, toggleAccsess] = useToggle()
const problems = ref<Problem[]>([])
const message = useMessage()
const contestStatus = computed(() => {
return false
})
const isContestAdmin = computed(
() =>
userStore.isSuperAdmin ||
(userStore.isAuthed && contest.value?.created_by.id === userStore.user.id)
)
async function init(contestID: string) {
const res = await getContest(contestID)
contest.value = res.data
if (contest.value?.contest_type === ContestType.private) {
const res = await getContestAccess(contestID)
toggleAccsess(res.data.access)
}
_getProblems(contestID)
}
async function checkPassword(contestID: string, password: string) {
try {
const res = await checkContestPassword(contestID, password)
toggleAccsess(res.data)
if (res.data) {
_getProblems(contestID)
}
} catch (err) {
toggleAccsess(false)
message.error("密码错误")
}
}
async function _getProblems(contestID: string) {
try {
problems.value = await getContestProblem(contestID)
} catch (err) {
problems.value = []
toggleAccsess(false)
}
}
return {
contest,
contestStatus,
isContestAdmin,
access,
problems,
init,
checkPassword,
}
})