fix.
This commit is contained in:
@@ -1,82 +1,35 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Contest, Problem } from "utils/types"
|
|
||||||
import { CONTEST_STATUS, ContestStatus, ContestType } from "utils/constants"
|
import { CONTEST_STATUS, ContestStatus, ContestType } from "utils/constants"
|
||||||
import { parseTime } from "utils/functions"
|
import { parseTime } from "utils/functions"
|
||||||
import {
|
|
||||||
getContest,
|
|
||||||
getContestAccess,
|
|
||||||
getContestProblem,
|
|
||||||
checkContestPassword,
|
|
||||||
} from "../api"
|
|
||||||
import { isDesktop } from "~/shared/composables/breakpoints"
|
import { isDesktop } from "~/shared/composables/breakpoints"
|
||||||
import ContestTypeVue from "./components/ContestType.vue"
|
import ContestTypeVue from "./components/ContestType.vue"
|
||||||
import { useUserStore } from "~/shared/store/user"
|
import { useContestStore } from "../store/contest"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
contestID: string
|
contestID: string
|
||||||
}>()
|
}>()
|
||||||
const message = useMessage()
|
const contestStore = useContestStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userStore = useUserStore()
|
|
||||||
const contest = ref<Contest>()
|
|
||||||
const problems = ref<Problem[]>([])
|
|
||||||
const password = ref("")
|
const password = ref("")
|
||||||
const [access, toggleAccsess] = useToggle()
|
|
||||||
|
|
||||||
async function init() {
|
onMounted(() => contestStore.init(props.contestID))
|
||||||
const res = await getContest(props.contestID)
|
|
||||||
contest.value = res.data
|
const contestMenuVisible = computed(() => {
|
||||||
if (contest.value?.contest_type === ContestType.private) {
|
if (contestStore.isContestAdmin) return true
|
||||||
const res = await getContestAccess(props.contestID)
|
if (contestStore.contest?.contest_type === ContestType.public) {
|
||||||
toggleAccsess(res.data.access)
|
// TODO:这里没有完成
|
||||||
}
|
}
|
||||||
getProblems()
|
return contestStore.access
|
||||||
}
|
})
|
||||||
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
|
|
||||||
const passwordFormVisible = computed(
|
const passwordFormVisible = computed(
|
||||||
() =>
|
() =>
|
||||||
contest.value?.contest_type === ContestType.private &&
|
contestStore.contest?.contest_type === ContestType.private &&
|
||||||
!access.value &&
|
!contestStore.access &&
|
||||||
!isContestAdmin.value
|
!contestStore.isContestAdmin
|
||||||
)
|
)
|
||||||
|
|
||||||
const contestMenuVisible = computed(() => {
|
|
||||||
if (isContestAdmin) return true
|
|
||||||
if (contest.value?.contest_type === ContestType.public) {
|
|
||||||
}
|
|
||||||
return access.value
|
|
||||||
})
|
|
||||||
|
|
||||||
function goto(name: string) {
|
function goto(name: string) {
|
||||||
router.push({ name: "contest " + name })
|
router.push({ name: "contest " + name })
|
||||||
}
|
}
|
||||||
@@ -88,31 +41,35 @@ function getCurrentType(name: string): "primary" | "default" {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="contest">
|
<div v-if="contestStore.contest">
|
||||||
<n-space align="center">
|
<n-space align="center">
|
||||||
<n-tag :type="CONTEST_STATUS[contest.status]['type']">
|
<n-tag :type="CONTEST_STATUS[contestStore.contest.status]['type']">
|
||||||
{{ CONTEST_STATUS[contest.status]["name"] }}
|
{{ CONTEST_STATUS[contestStore.contest.status]["name"] }}
|
||||||
</n-tag>
|
</n-tag>
|
||||||
<h2 class="contestTitle">{{ contest.title }}</h2>
|
<h2 class="contestTitle">{{ contestStore.contest.title }}</h2>
|
||||||
<n-icon
|
<n-icon
|
||||||
v-if="contest.contest_type === ContestType.private"
|
v-if="contestStore.contest.contest_type === ContestType.private"
|
||||||
class="lockIcon"
|
class="lockIcon"
|
||||||
>
|
>
|
||||||
<i-ep-lock />
|
<i-ep-lock />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</n-space>
|
</n-space>
|
||||||
<div v-html="contest.description"></div>
|
<div v-html="contestStore.contest.description"></div>
|
||||||
<n-form :inline="isDesktop" label-placement="left">
|
<n-form :inline="isDesktop" label-placement="left">
|
||||||
<n-form-item v-if="passwordFormVisible" label="需要输入密码才能看到题目">
|
<n-form-item v-if="passwordFormVisible" label="需要输入密码才能看到题目">
|
||||||
<n-input
|
<n-input
|
||||||
name="ContestPassword"
|
name="ContestPassword"
|
||||||
type="password"
|
type="password"
|
||||||
v-model:value="password"
|
v-model:value="password"
|
||||||
@change="checkPassword"
|
|
||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item v-if="passwordFormVisible">
|
<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>
|
||||||
<n-form-item v-if="contestMenuVisible">
|
<n-form-item v-if="contestMenuVisible">
|
||||||
<n-space>
|
<n-space>
|
||||||
@@ -137,39 +94,43 @@ function getCurrentType(name: string): "primary" | "default" {
|
|||||||
</n-space>
|
</n-space>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<n-descriptions class="margin" bordered :column="isDesktop ? 5 : 2">
|
<n-descriptions class="bottom" bordered :column="isDesktop ? 5 : 2">
|
||||||
<n-descriptions-item
|
<n-descriptions-item
|
||||||
:span="isDesktop ? 1 : 2"
|
:span="isDesktop ? 1 : 2"
|
||||||
v-if="contest.status !== ContestStatus.finished"
|
v-if="contestStore.contest.status !== ContestStatus.finished"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span v-if="contest.status === ContestStatus.not_started">
|
<span
|
||||||
|
v-if="contestStore.contest.status === ContestStatus.not_started"
|
||||||
|
>
|
||||||
距离比赛开始还有
|
距离比赛开始还有
|
||||||
</span>
|
</span>
|
||||||
<span v-if="contest.status === ContestStatus.underway">
|
<span v-if="contestStore.contest.status === ContestStatus.underway">
|
||||||
距离比赛结束还有
|
距离比赛结束还有
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<n-space align="center">
|
<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-countdown :duration="500000" />
|
||||||
</n-tag>
|
</n-tag>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="开始时间">
|
<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>
|
||||||
<n-descriptions-item label="结束时间">
|
<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>
|
||||||
<n-descriptions-item label="比赛类型">
|
<n-descriptions-item label="比赛类型">
|
||||||
<ContestTypeVue :contest="contest" />
|
<ContestTypeVue :contest="contestStore.contest" />
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="发起人">
|
<n-descriptions-item label="发起人">
|
||||||
{{ contest.created_by.username }}
|
{{ contestStore.contest.created_by.username }}
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
</n-descriptions>
|
</n-descriptions>
|
||||||
<router-view :problems="problems"></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -182,7 +143,7 @@ function getCurrentType(name: string): "primary" | "default" {
|
|||||||
transform: translateY(2px);
|
transform: translateY(2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin {
|
.bottom {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
import { DataTableColumn } from "naive-ui"
|
import { DataTableColumn } from "naive-ui"
|
||||||
import { ProblemFiltered } from "utils/types"
|
import { ProblemFiltered } from "utils/types"
|
||||||
import ProblemStatus from "~/oj/problem/components/ProblemStatus.vue"
|
import ProblemStatus from "~/oj/problem/components/ProblemStatus.vue"
|
||||||
|
import { useContestStore } from "~/oj/store/contest"
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
contestID: string
|
contestID: string
|
||||||
problems: ProblemFiltered[]
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const contestStore = useContestStore()
|
||||||
const problemsColumns: DataTableColumn<ProblemFiltered>[] = [
|
const problemsColumns: DataTableColumn<ProblemFiltered>[] = [
|
||||||
{
|
{
|
||||||
title: "状态",
|
title: "状态",
|
||||||
@@ -37,10 +38,10 @@ function rowProps(row: ProblemFiltered) {
|
|||||||
striped
|
striped
|
||||||
size="small"
|
size="small"
|
||||||
class="problems"
|
class="problems"
|
||||||
:data="problems"
|
:data="contestStore.problems"
|
||||||
:columns="problemsColumns"
|
:columns="problemsColumns"
|
||||||
:row-props="rowProps"
|
:row-props="rowProps"
|
||||||
v-if="problems?.length"
|
v-if="contestStore.problems?.length"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
69
src/oj/store/contest.ts
Normal file
69
src/oj/store/contest.ts
Normal 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,
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user