add access.

This commit is contained in:
2023-01-31 11:11:42 +08:00
parent be6e9eaaef
commit 6aa722c64a
8 changed files with 207 additions and 77 deletions

94
package-lock.json generated
View File

@@ -11,7 +11,7 @@
"@element-plus/icons-vue": "^2.0.10", "@element-plus/icons-vue": "^2.0.10",
"@monaco-editor/loader": "^1.3.2", "@monaco-editor/loader": "^1.3.2",
"@vueuse/core": "^9.11.1", "@vueuse/core": "^9.11.1",
"axios": "1.2.3", "axios": "1.2.5",
"chart.js": "^4.2.0", "chart.js": "^4.2.0",
"copy-text-to-clipboard": "^3.0.1", "copy-text-to-clipboard": "^3.0.1",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
@@ -31,8 +31,8 @@
"monaco-editor": "^0.34.1", "monaco-editor": "^0.34.1",
"prettier": "^2.8.3", "prettier": "^2.8.3",
"typescript": "^4.9.4", "typescript": "^4.9.4",
"unplugin-auto-import": "^0.12.1", "unplugin-auto-import": "^0.12.2",
"unplugin-icons": "^0.15.1", "unplugin-icons": "^0.15.2",
"unplugin-vue-components": "^0.22.12", "unplugin-vue-components": "^0.22.12",
"vite": "^4.0.4", "vite": "^4.0.4",
"vite-plugin-vue-markdown": "^0.22.2", "vite-plugin-vue-markdown": "^0.22.2",
@@ -463,9 +463,9 @@
"dev": true "dev": true
}, },
"node_modules/@iconify/utils": { "node_modules/@iconify/utils": {
"version": "2.0.12", "version": "2.1.0",
"resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-2.0.12.tgz", "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-2.1.0.tgz",
"integrity": "sha512-hhUyt1/k5RRhfcW/PRRdBw8e1ACehJT5QEZJRm7HnkCiUx11/0ccLr7K0OMlPSwjnfYcBS2gAUD3EpmL0iJCkQ==", "integrity": "sha512-ouXv1hQfOKq4k3wxQ2OJBYQ2gXBMeFoUCIiORmuUVUNlSq9oOTzQBF3jpFRb0+/P0bnV+RIoHcbZKufZTlJ16g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@antfu/install-pkg": "^0.1.1", "@antfu/install-pkg": "^0.1.1",
@@ -970,9 +970,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.2.3", "version": "1.2.5",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.2.3.tgz", "resolved": "https://registry.npmmirror.com/axios/-/axios-1.2.5.tgz",
"integrity": "sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==", "integrity": "sha512-9pU/8mmjSSOb4CXVsvGIevN+MlO/t9OWtKadTaLuN85Gge3HGorUckgp8A/2FH4V4hJ7JuQ3LIeI7KAV9ITZrQ==",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@@ -1902,9 +1902,9 @@
"dev": true "dev": true
}, },
"node_modules/pathe": { "node_modules/pathe": {
"version": "1.0.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.0.tgz",
"integrity": "sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==", "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==",
"dev": true "dev": true
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
@@ -2276,9 +2276,9 @@
"dev": true "dev": true
}, },
"node_modules/unimport": { "node_modules/unimport": {
"version": "1.3.0", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/unimport/-/unimport-1.3.0.tgz", "resolved": "https://registry.npmmirror.com/unimport/-/unimport-2.0.1.tgz",
"integrity": "sha512-fOkrdxglsHd428yegH0wPH/6IfaSdDeMXtdRGn6en/ccyzc2aaoxiUTMrJyc6Bu+xoa18RJRPMfLUHEzjz8atw==", "integrity": "sha512-hMeDspGrEcocahicTr0AQYUGes24FvJtOxk9QEjeEOGv+n1EdpsDiT6z8t209PWhemPg0T5w/ooTVhup2GdrFA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@rollup/pluginutils": "^5.0.2", "@rollup/pluginutils": "^5.0.2",
@@ -2287,7 +2287,7 @@
"local-pkg": "^0.4.3", "local-pkg": "^0.4.3",
"magic-string": "^0.27.0", "magic-string": "^0.27.0",
"mlly": "^1.1.0", "mlly": "^1.1.0",
"pathe": "^1.0.0", "pathe": "^1.1.0",
"pkg-types": "^1.0.1", "pkg-types": "^1.0.1",
"scule": "^1.0.0", "scule": "^1.0.0",
"strip-literal": "^1.0.0", "strip-literal": "^1.0.0",
@@ -2307,16 +2307,16 @@
} }
}, },
"node_modules/unplugin-auto-import": { "node_modules/unplugin-auto-import": {
"version": "0.12.1", "version": "0.12.2",
"resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.12.1.tgz", "resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.12.2.tgz",
"integrity": "sha512-J/3ZORq5YGKG+8D5vLLOgqaHNK77izlVN07mQ752yRLqBNDbJiwPRSnUwwYqH5N6rDay1SqnJCHaUdbJ9QMI2w==", "integrity": "sha512-hC4w0GZjPjmLtrxV0u10XO350V9eCtQyEyifXr7B9UGD7SvbbIvKuOcHt58Zd4FAqZJXKWoXkpr9mdhBp85Usw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@antfu/utils": "^0.7.2", "@antfu/utils": "^0.7.2",
"@rollup/pluginutils": "^5.0.2", "@rollup/pluginutils": "^5.0.2",
"local-pkg": "^0.4.2", "local-pkg": "^0.4.3",
"magic-string": "^0.27.0", "magic-string": "^0.27.0",
"unimport": "^1.0.2", "unimport": "^2.0.1",
"unplugin": "^1.0.1" "unplugin": "^1.0.1"
}, },
"engines": { "engines": {
@@ -2332,14 +2332,14 @@
} }
}, },
"node_modules/unplugin-icons": { "node_modules/unplugin-icons": {
"version": "0.15.1", "version": "0.15.2",
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.15.1.tgz", "resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.15.2.tgz",
"integrity": "sha512-d4Gc8A4qIJYIXKueltTwoHfR3Cxsdfnmz8lSN5dsITEyai5tdb0uWpbQkn3j9HUlLDSB1ybdQIf5CItxJT3UDw==", "integrity": "sha512-oWTTdLMuqfEYfZcko+KZHDEOIsqT4OeyJB1e4U7luCOo9gto/JLyHkqfbqjmjkjdQqA3DNHS18WOKh5esqQM5g==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@antfu/install-pkg": "^0.1.1", "@antfu/install-pkg": "^0.1.1",
"@antfu/utils": "^0.7.2", "@antfu/utils": "^0.7.2",
"@iconify/utils": "^2.0.9", "@iconify/utils": "^2.1.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"kolorist": "^1.6.0", "kolorist": "^1.6.0",
"local-pkg": "^0.4.2", "local-pkg": "^0.4.2",
@@ -2827,9 +2827,9 @@
"dev": true "dev": true
}, },
"@iconify/utils": { "@iconify/utils": {
"version": "2.0.12", "version": "2.1.0",
"resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-2.0.12.tgz", "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-2.1.0.tgz",
"integrity": "sha512-hhUyt1/k5RRhfcW/PRRdBw8e1ACehJT5QEZJRm7HnkCiUx11/0ccLr7K0OMlPSwjnfYcBS2gAUD3EpmL0iJCkQ==", "integrity": "sha512-ouXv1hQfOKq4k3wxQ2OJBYQ2gXBMeFoUCIiORmuUVUNlSq9oOTzQBF3jpFRb0+/P0bnV+RIoHcbZKufZTlJ16g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@antfu/install-pkg": "^0.1.1", "@antfu/install-pkg": "^0.1.1",
@@ -3268,9 +3268,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
}, },
"axios": { "axios": {
"version": "1.2.3", "version": "1.2.5",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.2.3.tgz", "resolved": "https://registry.npmmirror.com/axios/-/axios-1.2.5.tgz",
"integrity": "sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==", "integrity": "sha512-9pU/8mmjSSOb4CXVsvGIevN+MlO/t9OWtKadTaLuN85Gge3HGorUckgp8A/2FH4V4hJ7JuQ3LIeI7KAV9ITZrQ==",
"requires": { "requires": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@@ -3990,9 +3990,9 @@
"dev": true "dev": true
}, },
"pathe": { "pathe": {
"version": "1.0.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.0.tgz",
"integrity": "sha512-nPdMG0Pd09HuSsr7QOKUXO2Jr9eqaDiZvDwdyIhNG5SHYujkQHYKDfGQkulBxvbDHz8oHLsTgKN86LSwYzSHAg==", "integrity": "sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==",
"dev": true "dev": true
}, },
"picocolors": { "picocolors": {
@@ -4270,9 +4270,9 @@
"dev": true "dev": true
}, },
"unimport": { "unimport": {
"version": "1.3.0", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/unimport/-/unimport-1.3.0.tgz", "resolved": "https://registry.npmmirror.com/unimport/-/unimport-2.0.1.tgz",
"integrity": "sha512-fOkrdxglsHd428yegH0wPH/6IfaSdDeMXtdRGn6en/ccyzc2aaoxiUTMrJyc6Bu+xoa18RJRPMfLUHEzjz8atw==", "integrity": "sha512-hMeDspGrEcocahicTr0AQYUGes24FvJtOxk9QEjeEOGv+n1EdpsDiT6z8t209PWhemPg0T5w/ooTVhup2GdrFA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/pluginutils": "^5.0.2", "@rollup/pluginutils": "^5.0.2",
@@ -4281,7 +4281,7 @@
"local-pkg": "^0.4.3", "local-pkg": "^0.4.3",
"magic-string": "^0.27.0", "magic-string": "^0.27.0",
"mlly": "^1.1.0", "mlly": "^1.1.0",
"pathe": "^1.0.0", "pathe": "^1.1.0",
"pkg-types": "^1.0.1", "pkg-types": "^1.0.1",
"scule": "^1.0.0", "scule": "^1.0.0",
"strip-literal": "^1.0.0", "strip-literal": "^1.0.0",
@@ -4301,28 +4301,28 @@
} }
}, },
"unplugin-auto-import": { "unplugin-auto-import": {
"version": "0.12.1", "version": "0.12.2",
"resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.12.1.tgz", "resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-0.12.2.tgz",
"integrity": "sha512-J/3ZORq5YGKG+8D5vLLOgqaHNK77izlVN07mQ752yRLqBNDbJiwPRSnUwwYqH5N6rDay1SqnJCHaUdbJ9QMI2w==", "integrity": "sha512-hC4w0GZjPjmLtrxV0u10XO350V9eCtQyEyifXr7B9UGD7SvbbIvKuOcHt58Zd4FAqZJXKWoXkpr9mdhBp85Usw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@antfu/utils": "^0.7.2", "@antfu/utils": "^0.7.2",
"@rollup/pluginutils": "^5.0.2", "@rollup/pluginutils": "^5.0.2",
"local-pkg": "^0.4.2", "local-pkg": "^0.4.3",
"magic-string": "^0.27.0", "magic-string": "^0.27.0",
"unimport": "^1.0.2", "unimport": "^2.0.1",
"unplugin": "^1.0.1" "unplugin": "^1.0.1"
} }
}, },
"unplugin-icons": { "unplugin-icons": {
"version": "0.15.1", "version": "0.15.2",
"resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.15.1.tgz", "resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-0.15.2.tgz",
"integrity": "sha512-d4Gc8A4qIJYIXKueltTwoHfR3Cxsdfnmz8lSN5dsITEyai5tdb0uWpbQkn3j9HUlLDSB1ybdQIf5CItxJT3UDw==", "integrity": "sha512-oWTTdLMuqfEYfZcko+KZHDEOIsqT4OeyJB1e4U7luCOo9gto/JLyHkqfbqjmjkjdQqA3DNHS18WOKh5esqQM5g==",
"dev": true, "dev": true,
"requires": { "requires": {
"@antfu/install-pkg": "^0.1.1", "@antfu/install-pkg": "^0.1.1",
"@antfu/utils": "^0.7.2", "@antfu/utils": "^0.7.2",
"@iconify/utils": "^2.0.9", "@iconify/utils": "^2.1.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"kolorist": "^1.6.0", "kolorist": "^1.6.0",
"local-pkg": "^0.4.2", "local-pkg": "^0.4.2",

View File

@@ -13,7 +13,7 @@
"@element-plus/icons-vue": "^2.0.10", "@element-plus/icons-vue": "^2.0.10",
"@monaco-editor/loader": "^1.3.2", "@monaco-editor/loader": "^1.3.2",
"@vueuse/core": "^9.11.1", "@vueuse/core": "^9.11.1",
"axios": "1.2.3", "axios": "1.2.5",
"chart.js": "^4.2.0", "chart.js": "^4.2.0",
"copy-text-to-clipboard": "^3.0.1", "copy-text-to-clipboard": "^3.0.1",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
@@ -33,8 +33,8 @@
"monaco-editor": "^0.34.1", "monaco-editor": "^0.34.1",
"prettier": "^2.8.3", "prettier": "^2.8.3",
"typescript": "^4.9.4", "typescript": "^4.9.4",
"unplugin-auto-import": "^0.12.1", "unplugin-auto-import": "^0.12.2",
"unplugin-icons": "^0.15.1", "unplugin-icons": "^0.15.2",
"unplugin-vue-components": "^0.22.12", "unplugin-vue-components": "^0.22.12",
"vite": "^4.0.4", "vite": "^4.0.4",
"vite-plugin-vue-markdown": "^0.22.2", "vite-plugin-vue-markdown": "^0.22.2",

1
src/components.d.ts vendored
View File

@@ -13,6 +13,7 @@ declare module '@vue/runtime-core' {
IEpLock: typeof import('~icons/ep/lock')['default'] IEpLock: typeof import('~icons/ep/lock')['default']
NAlert: typeof import('naive-ui')['NAlert'] NAlert: typeof import('naive-ui')['NAlert']
NButton: typeof import('naive-ui')['NButton'] NButton: typeof import('naive-ui')['NButton']
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
NCard: typeof import('naive-ui')['NCard'] NCard: typeof import('naive-ui')['NCard']
NCode: typeof import('naive-ui')['NCode'] NCode: typeof import('naive-ui')['NCode']
NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NConfigProvider: typeof import('naive-ui')['NConfigProvider']

View File

@@ -112,9 +112,11 @@ export function getContestAccess(id: string) {
export function checkContestPassword(contestID: string, password: string) { export function checkContestPassword(contestID: string, password: string) {
return http.post("contest/password", { return http.post("contest/password", {
data: { contest_id: contestID,
contest_id: contestID, password,
password,
},
}) })
} }
export function getContestProblem(contestID: string) {
return http.get("contest/problem", { params: { contest_id: contestID } })
}

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { Contest } from "~/utils/types"
import { ContestType } from "~/utils/constants"
interface Props {
contest: Contest
}
const props = defineProps<Props>()
const isPrivate = computed(
() => props.contest.contest_type === ContestType.private
)
</script>
<template>
<n-tag :type="isPrivate ? 'error' : 'default'">
{{ isPrivate ? "需要密码" : "公开" }}
</n-tag>
</template>

View File

@@ -1,42 +1,129 @@
<script setup lang="ts"> <script setup lang="ts">
import { Contest } from "utils/types" 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 { getACRate, parseTime } from "utils/functions"
import { getContest, getContestAccess } from "../api" 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 { DataTableColumn } from "naive-ui"
import { useUserStore } from "~/shared/store/user"
const props = defineProps<{ const props = defineProps<{
contestID: string contestID: string
}>() }>()
const router = useRouter()
const userStore = useUserStore()
const contest = ref<Contest>() const contest = ref<Contest>()
const problems = ref<Problem[]>([])
const password = ref("")
const [access, toggleAccsess] = useToggle()
async function init() { async function init() {
const res = await getContest(props.contestID) const res = await getContest(props.contestID)
contest.value = res.data contest.value = res.data
if (contest.value?.contest_type === ContestType.private) { if (contest.value?.contest_type === ContestType.private) {
const res = await getContestAccess(props.contestID) const res = await getContestAccess(props.contestID)
// TODO: 这里 access 的逻辑不清楚 toggleAccsess(res.data.access)
console.log(res.data) }
getProblems()
}
async function getProblems() {
try {
const res = await getContestProblem(props.contestID)
problems.value = res.data
} 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)
} }
} }
onMounted(init) onMounted(init)
const isContestAdmin = computed(
() =>
userStore.isSuperAdmin ||
(userStore.isAuthed && contest.value?.created_by.id === userStore.user.id)
)
const passwordFormVisible = computed(
() =>
contest.value?.contest_type === ContestType.private &&
!access.value &&
!isContestAdmin
)
const problemsColumns: DataTableColumn<Problem>[] = [
{ title: "编号", key: "_id", width: 100 },
{ title: "标题", key: "title" },
{ title: "总提交数", key: "submission_number", width: 100 },
{
title: "通过率",
key: "rate",
width: 100,
render: (row) => getACRate(row.accepted_number, row.submission_number),
},
]
function rowProps(row: Problem) {
return {
style: "cursor: pointer",
onClick() {
router.push(`/contest/${props.contestID}/problem/${row._id}`)
},
}
}
</script> </script>
<template> <template>
<n-card v-if="contest"> <div v-if="contest">
<template #header> <n-space align="center">
<n-space align="center"> <n-tag :type="CONTEST_STATUS[contest.status]['type']">
<n-tag :type="CONTEST_STATUS[contest.status]['type']"> {{ CONTEST_STATUS[contest.status]["name"] }}
{{ CONTEST_STATUS[contest.status]["name"] }} </n-tag>
</n-tag> <h2 class="contestTitle">{{ contest.title }}</h2>
<span>{{ contest.title }}</span> <n-icon
<n-icon v-if="contest.contest_type === ContestType.private"
v-if="contest.contest_type === ContestType.private" class="lockIcon"
class="lockIcon" >
> <i-ep-lock />
<i-ep-lock /> </n-icon>
</n-icon> </n-space>
</n-space>
</template>
<div v-html="contest.description"></div> <div v-html="contest.description"></div>
<n-form inline label-placement="left">
<n-form-item v-if="passwordFormVisible" label="需要输入密码才能看到题目">
<n-input
name="ContestPassword"
type="password"
v-model:value="password"
/>
</n-form-item>
<n-form-item v-if="passwordFormVisible">
<n-button @click="checkPassword" :disabled="!password">确认</n-button>
</n-form-item>
<n-form-item>
<n-space>
<n-button type="primary">比赛题目</n-button>
<n-button>提交信息</n-button>
<n-button>比赛排名</n-button>
<n-button>管理员助手</n-button>
</n-space>
</n-form-item>
</n-form>
<n-descriptions bordered :column="isDesktop ? 5 : 2"> <n-descriptions bordered :column="isDesktop ? 5 : 2">
<n-descriptions-item <n-descriptions-item
:span="isDesktop ? 1 : 2" :span="isDesktop ? 1 : 2"
@@ -63,17 +150,34 @@ onMounted(init)
{{ parseTime(contest.end_time, "YYYY年M月D日 hh:mm:ss") }} {{ parseTime(contest.end_time, "YYYY年M月D日 hh:mm:ss") }}
</n-descriptions-item> </n-descriptions-item>
<n-descriptions-item label="比赛类型"> <n-descriptions-item label="比赛类型">
{{ contest.contest_type === ContestType.private ? "需要密码" : "公开" }} <ContestTypeVue :contest="contest" />
</n-descriptions-item> </n-descriptions-item>
<n-descriptions-item label="发起人"> <n-descriptions-item label="发起人">
{{ contest.created_by.username }} {{ contest.created_by.username }}
</n-descriptions-item> </n-descriptions-item>
</n-descriptions> </n-descriptions>
</n-card> </div>
<n-data-table
striped
size="small"
class="problems"
:data="problems"
:columns="problemsColumns"
:row-props="rowProps"
v-if="problems?.length"
></n-data-table>
</template> </template>
<style scoped> <style scoped>
.contestTitle {
font-weight: 500;
margin: 0;
}
.lockIcon { .lockIcon {
transform: translateY(2px); transform: translateY(2px);
} }
.problems {
margin-top: 24px;
}
</style> </style>

View File

@@ -132,7 +132,9 @@ const columns = computed(() => {
{ {
text: true, text: true,
type: "info", type: "info",
onClick: () => router.push("/submission/" + row.id), onClick: () => {
if (row.show_link) router.push("/submission/" + row.id)
},
}, },
() => row.id.slice(0, 12) () => row.id.slice(0, 12)
), ),

View File

@@ -140,6 +140,7 @@ export interface Submission {
info: Info info: Info
language: LANGUAGE language: LANGUAGE
shared: boolean shared: boolean
show_link: boolean
statistic_info: { statistic_info: {
score?: number score?: number
err_info?: string err_info?: string