更新首页列表

This commit is contained in:
2023-11-01 19:03:01 +08:00
parent 9cf73331ea
commit 710f4b5eb9
35 changed files with 261 additions and 241 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "oj-next", "name": "oj-next",
"version": "0.0.1", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "oj-next", "name": "oj-next",
"version": "0.0.1", "version": "1.0.0",
"dependencies": { "dependencies": {
"@codemirror/lang-cpp": "^6.0.2", "@codemirror/lang-cpp": "^6.0.2",
"@codemirror/lang-python": "^6.1.3", "@codemirror/lang-python": "^6.1.3",

View File

@@ -1,7 +1,6 @@
{ {
"name": "oj-next", "name": "oj-next",
"private": true, "version": "1.0.0",
"version": "0.0.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "start": "vite",

View File

@@ -19,7 +19,7 @@ export async function getProblemList(
limit = 10, limit = 10,
keyword: string, keyword: string,
contestID?: string, contestID?: string,
ruleType?: "ACM" | "OI" ruleType?: "ACM" | "OI",
) { ) {
const endpoint = !!contestID ? "admin/contest/problem" : "admin/problem" const endpoint = !!contestID ? "admin/contest/problem" : "admin/problem"
const res = await http.get(endpoint, { const res = await http.get(endpoint, {
@@ -143,7 +143,7 @@ export function getContest(id: string) {
export function addProblemForContest( export function addProblemForContest(
contestID: string, contestID: string,
problemID: number, problemID: number,
displayID: string displayID: string,
) { ) {
return http.post("admin/contest/add_problem_from_public", { return http.post("admin/contest/add_problem_from_public", {
contest_id: contestID, contest_id: contestID,

View File

@@ -40,7 +40,7 @@ const columns: DataTableColumn<Contest>[] = [
h( h(
NTag, NTag,
{ type: CONTEST_STATUS[row.status]["type"], size: "small" }, { type: CONTEST_STATUS[row.status]["type"], size: "small" },
() => CONTEST_STATUS[row.status]["name"] () => CONTEST_STATUS[row.status]["name"],
), ),
}, },
{ {

View File

@@ -18,7 +18,7 @@ async function addProblem() {
await addProblemForContest( await addProblemForContest(
props.contestID, props.contestID,
props.problemID, props.problemID,
displayID.value displayID.value,
) )
emit("added") emit("added")
} catch (err: any) { } catch (err: any) {

View File

@@ -47,7 +47,7 @@ async function getList() {
query.limit, query.limit,
query.keyword, query.keyword,
"", "",
"ACM" "ACM",
) )
total.value = res.total total.value = res.total
problems.value = res.results problems.value = res.results
@@ -56,7 +56,7 @@ watch(
() => props.show, () => props.show,
(value) => { (value) => {
if (value) getList() if (value) getList()
} },
) )
watch(query, getList, { deep: true }) watch(query, getList, { deep: true })
</script> </script>

View File

@@ -33,7 +33,7 @@ const title = computed(
"admin problem edit": "编辑题目", "admin problem edit": "编辑题目",
"admin contest problem create": "新建比赛题目", "admin contest problem create": "新建比赛题目",
"admin contest problem edit": "编辑比赛题目", "admin contest problem edit": "编辑比赛题目",
}[<string>route.name]) })[<string>route.name],
) )
const problem = reactive<BlankProblem>({ const problem = reactive<BlankProblem>({
_id: "", _id: "",
@@ -91,7 +91,7 @@ const languageOptions = [
] ]
const tagOptions = computed(() => const tagOptions = computed(() =>
existingTags.value.map((tag) => ({ label: tag.name, value: tag.name })) existingTags.value.map((tag) => ({ label: tag.name, value: tag.name })),
) )
async function getProblemDetail() { async function getProblemDetail() {
@@ -233,7 +233,7 @@ function detectProblemCompletion() {
// 样例是空的 // 样例是空的
else if ( else if (
problem.samples.some( problem.samples.some(
(sample) => sample.output === "" || sample.input === "" (sample) => sample.output === "" || sample.input === "",
) )
) { ) {
message.error("空样例没有删干净") message.error("空样例没有删干净")

View File

@@ -20,10 +20,10 @@ const title = computed(
({ ({
"admin problem list": "题目列表", "admin problem list": "题目列表",
"admin contest problem list": "比赛题目列表", "admin contest problem list": "比赛题目列表",
}[<string>route.name]) })[<string>route.name],
) )
const isContestProblemList = computed( const isContestProblemList = computed(
() => route.name === "admin contest problem list" () => route.name === "admin contest problem list",
) )
const [show, toggleShow] = useToggle() const [show, toggleShow] = useToggle()
@@ -77,7 +77,7 @@ async function listProblems() {
offset, offset,
query.limit, query.limit,
query.keyword, query.keyword,
props.contestID props.contestID,
) )
total.value = res.total total.value = res.total
problems.value = res.results problems.value = res.results

View File

@@ -28,7 +28,7 @@ const testcaseColumns: DataTableColumn<Testcase>[] = [
h( h(
NButton, NButton,
{ size: "small", onClick: () => deleteTestcase(row.id) }, { size: "small", onClick: () => deleteTestcase(row.id) },
() => "删除" () => "删除",
), ),
}, },
] ]
@@ -48,7 +48,7 @@ const serverColumns: DataTableColumn<Server>[] = [
h( h(
NTag, NTag,
{ type: statusMap[row.status].color, size: "small" }, { type: statusMap[row.status].color, size: "small" },
() => statusMap[row.status].label () => statusMap[row.status].label,
), ),
}, },
{ {
@@ -63,7 +63,7 @@ const serverColumns: DataTableColumn<Server>[] = [
disabled: row.status === "normal", disabled: row.status === "normal",
onClick: () => delJudgeServer(row.hostname), onClick: () => delJudgeServer(row.hostname),
}, },
() => "删除" () => "删除",
), ),
}, },
{ title: "主机", key: "hostname", width: 130 }, { title: "主机", key: "hostname", width: 130 },
@@ -94,7 +94,7 @@ const testcases = ref<Testcase[]>([])
const token = ref("") const token = ref("")
const servers = ref<Server[]>([]) const servers = ref<Server[]>([])
const abnormalServers = computed(() => const abnormalServers = computed(() =>
servers.value.filter((item) => item.status === "abnormal") servers.value.filter((item) => item.status === "abnormal"),
) )
const websiteConfig = reactive({ const websiteConfig = reactive({
@@ -144,7 +144,7 @@ async function delJudgeServer(hostname: string) {
async function deleteAbnormalServers() { async function deleteAbnormalServers() {
const dels = abnormalServers.value.map((item) => const dels = abnormalServers.value.map((item) =>
deleteJudgeServer(item.hostname) deleteJudgeServer(item.hostname),
) )
await Promise.all(dels) await Promise.all(dels)
message.success("删除成功") message.success("删除成功")

1
src/components.d.ts vendored
View File

@@ -24,6 +24,7 @@ declare module 'vue' {
NCheckbox: typeof import('naive-ui')['NCheckbox'] NCheckbox: typeof import('naive-ui')['NCheckbox']
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup'] NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
NCode: typeof import('naive-ui')['NCode'] NCode: typeof import('naive-ui')['NCode']
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDataTable: typeof import('naive-ui')['NDataTable'] NDataTable: typeof import('naive-ui')['NDataTable']
NDatePicker: typeof import('naive-ui')['NDatePicker'] NDatePicker: typeof import('naive-ui')['NDatePicker']

View File

@@ -10,7 +10,7 @@ const router = useRouter()
const learnStore = useLearnStore() const learnStore = useLearnStore()
const Mds = Array.from({ length: learnStore.total }, (_, i) => i + 1).map((v) => const Mds = Array.from({ length: learnStore.total }, (_, i) => i + 1).map((v) =>
defineAsyncComponent(() => import(`./step-${v}/index.md`)) defineAsyncComponent(() => import(`./step-${v}/index.md`)),
) )
watch( watch(
@@ -24,7 +24,7 @@ watch(
router.replace("/learn/step-1") router.replace("/learn/step-1")
} }
}, },
{ immediate: true } { immediate: true },
) )
</script> </script>

View File

@@ -36,7 +36,7 @@ export function getWebsiteConfig() {
export async function getProblemList( export async function getProblemList(
offset = 0, offset = 0,
limit = 10, limit = 10,
searchParams: any = {} searchParams: any = {},
) { ) {
let params: any = { let params: any = {
paging: true, paging: true,
@@ -129,7 +129,7 @@ export async function getContestProblems(contestID: string) {
export function getContestRank( export function getContestRank(
contestID: string, contestID: string,
query: { limit: number; offset: number; force_refresh: "1" | "0" } query: { limit: number; offset: number; force_refresh: "1" | "0" },
) { ) {
return http.get("contest_rank", { return http.get("contest_rank", {
params: { params: {

View File

@@ -24,7 +24,7 @@ const passwordFormVisible = computed(
() => () =>
contestStore.isPrivate && contestStore.isPrivate &&
!contestStore.access && !contestStore.access &&
!contestStore.isContestAdmin !contestStore.isContestAdmin,
) )
</script> </script>

View File

@@ -37,7 +37,7 @@ const columns: DataTableColumn<Contest>[] = [
h( h(
NTag, NTag,
{ type: CONTEST_STATUS[row.status]["type"] }, { type: CONTEST_STATUS[row.status]["type"] },
() => CONTEST_STATUS[row.status]["name"] () => CONTEST_STATUS[row.status]["name"],
), ),
}, },
{ {
@@ -95,13 +95,13 @@ watch(
() => { () => {
query.page = 1 query.page = 1
routerPush() routerPush()
} },
) )
watch( watch(
() => route.name === "contests" && route.query, () => route.name === "contests" && route.query,
(newVal) => { (newVal) => {
if (newVal) listContests() if (newVal) listContests()
} },
) )
function rowProps(row: Contest) { function rowProps(row: Contest) {

View File

@@ -33,7 +33,7 @@ const { resume, pause } = useIntervalFn(
10000, 10000,
{ {
immediate: false, immediate: false,
} },
) )
const query = reactive({ const query = reactive({
@@ -64,7 +64,7 @@ const columns = ref<DataTableColumn<ContestRank>[]>([
type: "info", type: "info",
onClick: () => router.push("/user?name=" + row.user.username), onClick: () => router.push("/user?name=" + row.user.username),
}, },
() => row.user.username () => row.user.username,
), ),
}, },
{ {
@@ -119,7 +119,7 @@ async function addColumns() {
window.open(data.href, "_blank") window.open(data.href, "_blank")
}, },
}, },
() => problem.title () => problem.title,
), ),
render: (row) => { render: (row) => {
if (row.submission_info[problem.id]) { if (row.submission_info[problem.id]) {
@@ -134,7 +134,7 @@ async function addColumns() {
h( h(
NIcon, NIcon,
{ size: 16, style: "transform: translate(-2px, 3px)" }, { size: 16, style: "transform: translate(-2px, 3px)" },
() => h(GoldMedal) () => h(GoldMedal),
), ),
secondsToDuration(status.ac_time), secondsToDuration(status.ac_time),
] ]
@@ -143,7 +143,7 @@ async function addColumns() {
errorNumber = h( errorNumber = h(
"p", "p",
{ style: "margin: 0" }, { style: "margin: 0" },
`(-${status.error_number})` `(-${status.error_number})`,
) )
} }
return h("div", [acTime, errorNumber]) return h("div", [acTime, errorNumber])
@@ -182,7 +182,7 @@ watch(
() => { () => {
query.page = 1 query.page = 1
listRanks() listRanks()
} },
) )
watch(autoRefresh, (checked) => (checked ? resume() : pause())) watch(autoRefresh, (checked) => (checked ? resume() : pause()))

View File

@@ -13,7 +13,7 @@ const contestID = !!route.params.contestID ? route.params.contestID : null
const storageKey = computed( const storageKey = computed(
() => () =>
`problem_${problem.value!._id}_contest_${contestID}_lang_${code.language}` `problem_${problem.value!._id}_contest_${contestID}_lang_${code.language}`,
) )
onMounted(() => { onMounted(() => {
@@ -26,7 +26,7 @@ onMounted(() => {
}) })
const editorHeight = computed(() => const editorHeight = computed(() =>
isDesktop.value ? "calc(100vh - 133px)" : "calc(100vh - 172px)" isDesktop.value ? "calc(100vh - 133px)" : "calc(100vh - 172px)",
) )
function changeCode(v: string) { function changeCode(v: string) {

View File

@@ -23,7 +23,7 @@ const samples = ref<Sample[]>(
msg: "", msg: "",
status: "not_test", status: "not_test",
loading: false, loading: false,
})) })),
) )
async function test(sample: Sample, index: number) { async function test(sample: Sample, index: number) {
@@ -202,7 +202,8 @@ function type(status: ProblemStatus) {
border: 1px solid rgb(239, 239, 245); border: 1px solid rgb(239, 239, 245);
word-break: break-word; word-break: break-word;
box-sizing: border-box; box-sizing: border-box;
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), transition:
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
} }
@@ -219,13 +220,15 @@ function type(status: ProblemStatus) {
width: 100%; width: 100%;
border-spacing: 0; border-spacing: 0;
border: 1px solid rgba(239, 239, 245, 1); border: 1px solid rgba(239, 239, 245, 1);
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), transition:
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
} }
.problemContent > .content > table th { .problemContent > .content > table th {
background-color: rgba(250, 250, 252, 1); background-color: rgba(250, 250, 252, 1);
transition: background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1), transition:
background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1); border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
} }

View File

@@ -21,7 +21,7 @@ const columns: DataTableColumn<Submission>[] = [
render: (row) => render: (row) =>
parseTime( parseTime(
row.create_time, row.create_time,
isDesktop ? "YYYY-MM-DD HH:mm:ss" : "M-D hh:mm" isDesktop ? "YYYY-MM-DD HH:mm:ss" : "M-D hh:mm",
), ),
}, },
{ {
@@ -40,7 +40,7 @@ const columns: DataTableColumn<Submission>[] = [
window.open(data.href, "_blank") window.open(data.href, "_blank")
}, },
}, },
() => row.id.slice(0, 12) () => row.id.slice(0, 12),
) )
} else { } else {
return row.id.slice(0, 12) return row.id.slice(0, 12)

View File

@@ -39,17 +39,21 @@ const { start: fetchSubmission } = useTimeoutFn(
} }
}, },
2000, 2000,
{ immediate: false } { immediate: false },
) )
const judging = computed( const judging = computed(
() => () =>
!!(submission.value && submission.value.result === SubmissionStatus.judging) !!(
submission.value && submission.value.result === SubmissionStatus.judging
),
) )
const pending = computed( const pending = computed(
() => () =>
!!(submission.value && submission.value.result === SubmissionStatus.pending) !!(
submission.value && submission.value.result === SubmissionStatus.pending
),
) )
const submitting = computed( const submitting = computed(
@@ -57,7 +61,7 @@ const submitting = computed(
!!( !!(
submission.value && submission.value &&
submission.value.result === SubmissionStatus.submitting submission.value.result === SubmissionStatus.submitting
) ),
) )
const submitDisabled = computed(() => { const submitDisabled = computed(() => {
@@ -186,7 +190,7 @@ watch(
size: party.variation.skew(2, 0.3), size: party.variation.skew(2, 0.3),
}) })
} }
} },
) )
</script> </script>

View File

@@ -7,6 +7,7 @@ import Pagination from "~/shared/Pagination.vue"
import { NSpace, NTag } from "naive-ui" import { NSpace, NTag } from "naive-ui"
import ProblemStatus from "./components/ProblemStatus.vue" import ProblemStatus from "./components/ProblemStatus.vue"
import { getProblemTagList } from "~/shared/api" import { getProblemTagList } from "~/shared/api"
import { isDesktop } from "~/shared/composables/breakpoints"
interface Tag { interface Tag {
id: number id: number
@@ -36,6 +37,7 @@ const userStore = useUserStore()
const problems = ref<ProblemFiltered[]>([]) const problems = ref<ProblemFiltered[]>([])
const total = ref(0) const total = ref(0)
const tags = ref<Tag[]>([]) const tags = ref<Tag[]>([])
const [showTag, toggleShowTag] = useToggle(isDesktop.value)
const query = reactive<Query>({ const query = reactive<Query>({
keyword: <string>route.query.keyword ?? "", keyword: <string>route.query.keyword ?? "",
@@ -112,14 +114,14 @@ watch(
() => { () => {
query.page = 1 query.page = 1
routerPush() routerPush()
} },
) )
watch( watch(
() => route.path === "/" && route.query, () => route.path === "/" && route.query,
(newVal) => { (newVal) => {
if (newVal) listProblems() if (newVal) listProblems()
} },
) )
// TODO: 这里会在登录时候执行两次有BUG // TODO: 这里会在登录时候执行两次有BUG
@@ -130,7 +132,7 @@ onMounted(() => {
listTags() listTags()
}) })
const columns: DataTableColumn<ProblemFiltered>[] = [ const baseColumns: DataTableColumn<ProblemFiltered>[] = [
{ {
title: "状态", title: "状态",
key: "status", key: "status",
@@ -157,6 +159,12 @@ const columns: DataTableColumn<ProblemFiltered>[] = [
{ title: "通过率", key: "rate", width: 100 }, { title: "通过率", key: "rate", width: 100 },
] ]
const columns = computed(() =>
userStore.isAuthed
? baseColumns
: baseColumns.filter((c: any) => c.key !== "status"),
)
function rowProps(row: ProblemFiltered) { function rowProps(row: ProblemFiltered) {
return { return {
style: "cursor: pointer", style: "cursor: pointer",
@@ -191,20 +199,22 @@ function rowProps(row: ProblemFiltered) {
</n-space> </n-space>
</n-form-item> </n-form-item>
</n-form> </n-form>
<n-button @click="toggleShowTag()" quaternary>标签</n-button>
</n-space> </n-space>
<n-space> <n-collapse-transition :show="showTag">
<div class="tagTitle">标签</div> <n-space>
<n-button <n-button
@click="chooseTag(tag)" @click="chooseTag(tag)"
v-for="tag in tags" v-for="tag in tags"
:key="tag.id" :key="tag.id"
size="small" size="small"
secondary secondary
:type="tag.checked ? 'success' : 'default'" :type="tag.checked ? 'success' : 'default'"
> >
{{ tag.name }} {{ tag.name }}
</n-button> </n-button>
</n-space> </n-space>
</n-collapse-transition>
<n-data-table <n-data-table
striped striped
:data="problems" :data="problems"

View File

@@ -47,7 +47,7 @@ const columns: DataTableColumn<Rank>[] = [
type: "info", type: "info",
onClick: () => router.push("/user?name=" + row.user.username), onClick: () => router.push("/user?name=" + row.user.username),
}, },
() => row.user.username () => row.user.username,
), ),
}, },
{ title: "自我介绍", key: "mood", minWidth: 200 }, { title: "自我介绍", key: "mood", minWidth: 200 },
@@ -67,7 +67,7 @@ watch(
() => { () => {
query.page = 1 query.page = 1
listRanks() listRanks()
} },
) )
onMounted(listRanks) onMounted(listRanks)

View File

@@ -48,11 +48,11 @@ export const useContestStore = defineStore("contest", () => {
() => () =>
userStore.isSuperAdmin || userStore.isSuperAdmin ||
(userStore.isAuthed && (userStore.isAuthed &&
contest.value?.created_by.id === userStore.user!.id) contest.value?.created_by.id === userStore.user!.id),
) )
const isPrivate = computed( const isPrivate = computed(
() => contest.value!.contest_type === ContestType.private () => contest.value!.contest_type === ContestType.private,
) )
async function init(contestID: string) { async function init(contestID: string) {

View File

@@ -102,7 +102,7 @@ watch(
() => { () => {
query.page = 1 query.page = 1
routerPush() routerPush()
} },
) )
watch( watch(
@@ -111,7 +111,7 @@ watch(
route.query, route.query,
(newVal) => { (newVal) => {
if (newVal) listSubmissions() if (newVal) listSubmissions()
} },
) )
const columns = computed(() => { const columns = computed(() => {
@@ -123,7 +123,7 @@ const columns = computed(() => {
render: (row) => render: (row) =>
parseTime( parseTime(
row.create_time, row.create_time,
isDesktop ? "YYYY-MM-DD HH:mm:ss" : "M-D hh:mm" isDesktop ? "YYYY-MM-DD HH:mm:ss" : "M-D hh:mm",
), ),
}, },
{ {
@@ -139,7 +139,7 @@ const columns = computed(() => {
type: "info", type: "info",
onClick: () => router.push("/submission/" + row.id), onClick: () => router.push("/submission/" + row.id),
}, },
() => row.id.slice(0, 12) () => row.id.slice(0, 12),
) )
} else { } else {
return row.id.slice(0, 12) return row.id.slice(0, 12)
@@ -175,7 +175,7 @@ const columns = computed(() => {
} }
}, },
}, },
() => row.problem () => row.problem,
), ),
}, },
{ {
@@ -208,7 +208,7 @@ const columns = computed(() => {
type: "info", type: "info",
onClick: () => router.push("/user?name=" + row.username), onClick: () => router.push("/user?name=" + row.username),
}, },
() => row.username () => row.username,
), ),
}, },
] ]
@@ -225,7 +225,7 @@ const columns = computed(() => {
type: "primary", type: "primary",
onClick: () => rejudge(row.id), onClick: () => rejudge(row.id),
}, },
() => "重新判题" () => "重新判题",
), ),
}) })
} }

View File

@@ -39,7 +39,7 @@ watch(
() => props.modelValue, () => props.modelValue,
(v) => { (v) => {
code.value = v code.value = v
} },
) )
const emit = defineEmits(["update:modelValue"]) const emit = defineEmits(["update:modelValue"])

View File

@@ -10,7 +10,7 @@ interface Props {
const props = defineProps<Props>() const props = defineProps<Props>()
const isPrivate = computed( const isPrivate = computed(
() => props.contest.contest_type === ContestType.private () => props.contest.contest_type === ContestType.private,
) )
</script> </script>

View File

@@ -6,7 +6,10 @@ import { isDark, toggleDark } from "~/shared/composables/dark"
import { toggleLogin, toggleSignup } from "~/shared/composables/modal" import { toggleLogin, toggleSignup } from "~/shared/composables/modal"
import { RouterLink } from "vue-router" import { RouterLink } from "vue-router"
import { isDesktop, isMobile } from "~/shared/composables/breakpoints" import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
import { screenSwitchLabel, switchScreenMode } from "~/shared/composables/switchScreen" import {
screenSwitchLabel,
switchScreenMode,
} from "~/shared/composables/switchScreen"
import { code } from "~/shared/composables/learn" import { code } from "~/shared/composables/learn"
import { useLearnStore } from "~/learn/store" import { useLearnStore } from "~/learn/store"

View File

@@ -22,7 +22,7 @@ export function loadChart() {
Colors, Colors,
Title, Title,
Tooltip, Tooltip,
Legend Legend,
) )
isLoaded.value = true isLoaded.value = true
} }

View File

@@ -26,7 +26,7 @@ const options: MenuOption[] = [
h( h(
RouterLink, RouterLink,
{ to: "/admin/problem/list" }, { to: "/admin/problem/list" },
{ default: () => "题目列表" } { default: () => "题目列表" },
), ),
key: "admin problem list", key: "admin problem list",
}, },
@@ -35,7 +35,7 @@ const options: MenuOption[] = [
h( h(
RouterLink, RouterLink,
{ to: "/admin/problem/create" }, { to: "/admin/problem/create" },
{ default: () => "新建题目" } { default: () => "新建题目" },
), ),
key: "admin problem create", key: "admin problem create",
}, },
@@ -50,7 +50,7 @@ const options: MenuOption[] = [
h( h(
RouterLink, RouterLink,
{ to: "/admin/user/generate" }, { to: "/admin/user/generate" },
{ default: () => "批量生成" } { default: () => "批量生成" },
), ),
key: "admin user generate", key: "admin user generate",
}, },
@@ -60,7 +60,7 @@ const options: MenuOption[] = [
h( h(
RouterLink, RouterLink,
{ to: "/admin/contest/list" }, { to: "/admin/contest/list" },
{ default: () => "比赛列表" } { default: () => "比赛列表" },
), ),
key: "admin contest list", key: "admin contest list",
}, },
@@ -69,7 +69,7 @@ const options: MenuOption[] = [
h( h(
RouterLink, RouterLink,
{ to: "/admin/contest/create" }, { to: "/admin/contest/create" },
{ default: () => "新建比赛" } { default: () => "新建比赛" },
), ),
key: "admin contest create", key: "admin contest create",
}, },
@@ -84,7 +84,7 @@ const options: MenuOption[] = [
h( h(
RouterLink, RouterLink,
{ to: "/admin/announcement" }, { to: "/admin/announcement" },
{ default: () => "公告配置" } { default: () => "公告配置" },
), ),
key: "admin announcement", key: "admin announcement",
}, },

View File

@@ -11,13 +11,13 @@ export const useUserStore = defineStore("user", () => {
const isAdminRole = computed( const isAdminRole = computed(
() => () =>
user.value?.admin_type === USER_TYPE.ADMIN || user.value?.admin_type === USER_TYPE.ADMIN ||
user.value?.admin_type === USER_TYPE.SUPER_ADMIN user.value?.admin_type === USER_TYPE.SUPER_ADMIN,
) )
const isSuperAdmin = computed( const isSuperAdmin = computed(
() => user.value?.admin_type === USER_TYPE.SUPER_ADMIN () => user.value?.admin_type === USER_TYPE.SUPER_ADMIN,
) )
const hasProblemPermission = computed( const hasProblemPermission = computed(
() => user.value?.problem_permission !== PROBLEM_PERMISSION.NONE () => user.value?.problem_permission !== PROBLEM_PERMISSION.NONE,
) )
async function getMyProfile() { async function getMyProfile() {

View File

@@ -100,7 +100,7 @@ export const createTheme = ({
}, },
{ {
dark: variant === "dark", dark: variant === "dark",
} },
) )
const highlightStyle = HighlightStyle.define(styles) const highlightStyle = HighlightStyle.define(styles)

View File

@@ -92,7 +92,7 @@ const oneDarkTheme = EditorView.theme(
}, },
}, },
}, },
{ dark: true } { dark: true },
) )
/// The highlighting style for code in the One Dark theme. /// The highlighting style for code in the One Dark theme.

View File

@@ -12,7 +12,7 @@ async function download(url: string) {
const headers = res.headers const headers = res.headers
const link = document.createElement("a") const link = document.createElement("a")
link.href = window.URL.createObjectURL( link.href = window.URL.createObjectURL(
new window.Blob([res.data], { type: headers["content-type"] }) new window.Blob([res.data], { type: headers["content-type"] }),
) )
link.download = (headers["content-disposition"] || "").split("filename=")[1] link.download = (headers["content-disposition"] || "").split("filename=")[1]
document.body.appendChild(link) document.body.appendChild(link)

View File

@@ -18,7 +18,7 @@ export function filterEmptyValue(object: any) {
} }
export function getTagColor( export function getTagColor(
tag: "Low" | "Mid" | "High" | "简单" | "中等" | "困难" tag: "Low" | "Mid" | "High" | "简单" | "中等" | "困难",
) { ) {
return <"success" | "info" | "error">{ return <"success" | "info" | "error">{
Low: "success", Low: "success",
@@ -39,7 +39,7 @@ export function parseTime(utc: Date | string, format = "YYYY年M月D日") {
export function duration( export function duration(
start: Date | string, start: Date | string,
end: Date | string, end: Date | string,
showSeconds = false showSeconds = false,
): string { ): string {
const duration = intervalToDuration({ const duration = intervalToDuration({
start: getTime(parseISO(start.toString())), start: getTime(parseISO(start.toString())),
@@ -141,8 +141,8 @@ export function decode(bytes?: string) {
const latin = atob(bytes ?? "") const latin = atob(bytes ?? "")
return new TextDecoder("utf-8").decode( return new TextDecoder("utf-8").decode(
Uint8Array.from({ length: latin.length }, (_, index) => Uint8Array.from({ length: latin.length }, (_, index) =>
latin.charCodeAt(index) latin.charCodeAt(index),
) ),
) )
} }

View File

@@ -19,7 +19,7 @@ http.interceptors.response.use(
}, },
(err) => { (err) => {
return Promise.reject(err) return Promise.reject(err)
} },
) )
export default http export default http