admin pages and menu.

This commit is contained in:
2023-03-09 18:22:06 +08:00
parent 237861ab3f
commit 89ba38ba0a
19 changed files with 456 additions and 136 deletions

463
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,34 +10,34 @@
"fmt": "prettier --write src *.ts"
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.10",
"@element-plus/icons-vue": "^2.1.0",
"@monaco-editor/loader": "^1.3.2",
"@vueuse/core": "^9.13.0",
"axios": "1.3.3",
"axios": "1.3.4",
"chart.js": "^4.2.1",
"copy-text-to-clipboard": "^3.0.1",
"date-fns": "^2.29.3",
"highlight.js": "^11.7.0",
"naive-ui": "^2.34.3",
"party-js": "^2.2.0",
"pinia": "^2.0.32",
"pinia": "^2.0.33",
"vue": "^3.2.47",
"vue-chartjs": "^5.2.0",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@iconify-json/ep": "^1.1.9",
"@types/node": "^18.14.0",
"@iconify-json/ep": "^1.1.10",
"@types/node": "^18.14.6",
"@vitejs/plugin-vue": "^4.0.0",
"markdown-it-shiki": "^0.8.0",
"monaco-editor": "^0.35.0",
"monaco-editor": "^0.36.1",
"prettier": "^2.8.4",
"typescript": "^4.9.5",
"unplugin-auto-import": "^0.14.4",
"unplugin-auto-import": "^0.15.1",
"unplugin-icons": "^0.15.3",
"unplugin-vue-components": "^0.24.0",
"unplugin-vue-components": "^0.24.1",
"vite": "^4.1.4",
"vite-plugin-vue-markdown": "^0.22.4",
"vue-tsc": "^1.1.7"
"vue-tsc": "^1.2.0"
}
}

View File

View File

View File

View File

@@ -1,7 +1,11 @@
<script setup lang="ts"></script>
<template>
<router-view></router-view>
<div class="container">1213</div>
</template>
<style scoped></style>
<style scoped>
.container {
height: 200vh;
}
</style>

View File

View File

View File

View File

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

Before

Width:  |  Height:  |  Size: 496 B

View File

@@ -289,5 +289,5 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component,ComponentPublicInstance,ComputedRef,InjectionKey,PropType,Ref,VNode } from 'vue'
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
}

1
src/components.d.ts vendored
View File

@@ -37,6 +37,7 @@ declare module '@vue/runtime-core' {
NLayout: typeof import('naive-ui')['NLayout']
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NMenu: typeof import('naive-ui')['NMenu']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']

View File

@@ -5,6 +5,7 @@ import { Problem } from "utils/types"
import { code } from "oj/composables/code"
import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
import Submit from "./Submit.vue"
import { useUserStore } from "~/shared/store/user"
interface Props {
problem: Problem
@@ -13,6 +14,7 @@ interface Props {
const props = defineProps<Props>()
const route = useRoute()
const router = useRouter()
const userStore = useUserStore()
watch(() => code.language, reset)
@@ -25,6 +27,10 @@ function goSubmissions() {
router.push({ name, query: { problem: props.problem._id } })
}
function edit() {
router.push("/admin/problem/edit/" + props.problem.id)
}
const menu: DropdownOption[] = [
{ label: "重置", key: "reset" },
{ label: "提交信息", key: "submissions" },
@@ -88,6 +94,7 @@ function select(key: string) {
<n-space>
<n-button @click="reset">重置</n-button>
<n-button @click="goSubmissions">提交信息</n-button>
<n-button v-if="userStore.isSuperAdmin" @click="edit">编辑</n-button>
</n-space>
</n-form-item>
</n-form>

View File

@@ -215,7 +215,7 @@ const columns = computed(() => {
h(
NButton,
{ size: "small", onClick: () => rejudge(row.id) },
() => "重新评分"
() => "重新判题"
),
})
}

View File

@@ -95,6 +95,45 @@ export const routes: RouteRecordRaw[] = [
{
path: "/admin",
component: () => import("~/shared/layout/admin.vue"),
children: [{ path: "", component: () => import("admin/index.vue") }],
children: [
{ path: "", component: () => import("admin/index.vue") },
{ path: "user", component: () => import("admin/setting/user.vue") },
{ path: "conf", component: () => import("admin/setting/conf.vue") },
{ path: "problems", component: () => import("admin/problem/list.vue") },
{
path: "problem/create",
component: () => import("admin/problem/detail.vue"),
},
{
path: "problem/:problemID/edit",
component: () => import("admin/problem/detail.vue"),
props: true,
},
{ path: "contests", component: () => import("admin/contest/list.vue") },
{
path: "contest/create",
component: () => import("admin/contest/detail.vue"),
},
{
path: "contest/:contestID/edit",
component: () => import("admin/contest/detail.vue"),
props: true,
},
{
path: "contest/:contestID/problems",
component: () => import("admin/contest/detail.vue"),
props: true,
},
{
path: "contest/:contestID/problem/create",
component: () => import("admin/problem/detail.vue"),
props: true,
},
{
path: "contest/:contestID/problem/:problemID/edit",
component: () => import("admin/problem/detail.vue"),
props: true,
},
],
},
]

View File

@@ -75,7 +75,14 @@ const options = computed<Array<DropdownOption | DropdownDividerOption>>(() => [
onClick: () => router.push("/setting"),
},
},
{ label: "后台管理", key: "admin", show: userStore.isAdminRole },
{
label: "后台管理",
key: "admin",
show: userStore.isAdminRole,
props: {
onClick: () => router.push("/admin"),
},
},
{ type: "divider" },
{ label: "退出", key: "logout", props: { onClick: handleLogout } },
])
@@ -136,7 +143,7 @@ function run() {
<n-button circle @click="toggleDark()">
<template #icon>
<n-icon v-if="isDark"><i-ep-sunny /></n-icon>
<n-icon v-else> <i-ep-moon /></n-icon>
<n-icon v-else><i-ep-moon /></n-icon>
</template>
</n-button>
</n-space>

View File

@@ -4,7 +4,7 @@ export const monaco = ref<Monaco>()
export async function init() {
loader.config({
paths: { vs: "https://cdn.staticfile.org/monaco-editor/0.34.1/min/vs" },
paths: { vs: "https://cdn.staticfile.org/monaco-editor/0.36.1/min/vs" },
"vs/nls": { availableLanguages: { "*": "zh-cn" } },
})

View File

@@ -1,11 +1,39 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import { MenuOption } from "naive-ui"
import Login from "../Login.vue"
import Signup from "../Signup.vue"
const options: MenuOption[] = [
{ label: "题目", key: "problem", disabled: true },
{ label: "题目列表", key: "problem list" },
{ label: "创建题目", key: "create problem" },
{ label: "用户", key: "user", disabled: true },
{ label: "用户列表", key: "user list" },
{ label: "导入用户", key: "user import" },
{ label: "比赛", key: "contest", disabled: true },
{ label: "比赛列表", key: "contest list" },
{ label: "创建比赛", key: "create contest" },
{ label: "其他", key: "other", disabled: true },
{ label: "系统配置", key: "config" },
{ label: "公告配置", key: "announcement" },
]
</script>
<template>
<n-layout>
<n-layout-content bordered>
<n-layout has-sider position="absolute">
<n-layout-sider bordered :native-scrollbar="false">
<n-menu :options="options" />
</n-layout-sider>
<n-layout-content :native-scrollbar="false">
<router-view></router-view>
</n-layout-content>
<Login />
<Signup />
</n-layout>
</template>
<style scoped></style>
<style scoped>
.content {
padding: 16px;
}
</style>