admin pages and menu.
This commit is contained in:
463
package-lock.json
generated
463
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -10,34 +10,34 @@
|
|||||||
"fmt": "prettier --write src *.ts"
|
"fmt": "prettier --write src *.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.0.10",
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
"@monaco-editor/loader": "^1.3.2",
|
"@monaco-editor/loader": "^1.3.2",
|
||||||
"@vueuse/core": "^9.13.0",
|
"@vueuse/core": "^9.13.0",
|
||||||
"axios": "1.3.3",
|
"axios": "1.3.4",
|
||||||
"chart.js": "^4.2.1",
|
"chart.js": "^4.2.1",
|
||||||
"copy-text-to-clipboard": "^3.0.1",
|
"copy-text-to-clipboard": "^3.0.1",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
"naive-ui": "^2.34.3",
|
"naive-ui": "^2.34.3",
|
||||||
"party-js": "^2.2.0",
|
"party-js": "^2.2.0",
|
||||||
"pinia": "^2.0.32",
|
"pinia": "^2.0.33",
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-chartjs": "^5.2.0",
|
"vue-chartjs": "^5.2.0",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/ep": "^1.1.9",
|
"@iconify-json/ep": "^1.1.10",
|
||||||
"@types/node": "^18.14.0",
|
"@types/node": "^18.14.6",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"markdown-it-shiki": "^0.8.0",
|
"markdown-it-shiki": "^0.8.0",
|
||||||
"monaco-editor": "^0.35.0",
|
"monaco-editor": "^0.36.1",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.4",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"unplugin-auto-import": "^0.14.4",
|
"unplugin-auto-import": "^0.15.1",
|
||||||
"unplugin-icons": "^0.15.3",
|
"unplugin-icons": "^0.15.3",
|
||||||
"unplugin-vue-components": "^0.24.0",
|
"unplugin-vue-components": "^0.24.1",
|
||||||
"vite": "^4.1.4",
|
"vite": "^4.1.4",
|
||||||
"vite-plugin-vue-markdown": "^0.22.4",
|
"vite-plugin-vue-markdown": "^0.22.4",
|
||||||
"vue-tsc": "^1.1.7"
|
"vue-tsc": "^1.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/admin/contest/detail.vue
Normal file
0
src/admin/contest/detail.vue
Normal file
0
src/admin/contest/list.vue
Normal file
0
src/admin/contest/list.vue
Normal file
0
src/admin/contest/problems.vue
Normal file
0
src/admin/contest/problems.vue
Normal file
@@ -1,7 +1,11 @@
|
|||||||
<script setup lang="ts"></script>
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<router-view></router-view>
|
<div class="container">1213</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
height: 200vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
0
src/admin/problem/detail.vue
Normal file
0
src/admin/problem/detail.vue
Normal file
0
src/admin/problem/list.vue
Normal file
0
src/admin/problem/list.vue
Normal file
0
src/admin/setting/conf.vue
Normal file
0
src/admin/setting/conf.vue
Normal file
0
src/admin/setting/user.vue
Normal file
0
src/admin/setting/user.vue
Normal 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 |
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -37,6 +37,7 @@ declare module '@vue/runtime-core' {
|
|||||||
NLayout: typeof import('naive-ui')['NLayout']
|
NLayout: typeof import('naive-ui')['NLayout']
|
||||||
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
|
NLayoutContent: typeof import('naive-ui')['NLayoutContent']
|
||||||
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
|
NLayoutHeader: typeof import('naive-ui')['NLayoutHeader']
|
||||||
|
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
NModal: typeof import('naive-ui')['NModal']
|
NModal: typeof import('naive-ui')['NModal']
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Problem } from "utils/types"
|
|||||||
import { code } from "oj/composables/code"
|
import { code } from "oj/composables/code"
|
||||||
import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
|
import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
|
||||||
import Submit from "./Submit.vue"
|
import Submit from "./Submit.vue"
|
||||||
|
import { useUserStore } from "~/shared/store/user"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
problem: Problem
|
problem: Problem
|
||||||
@@ -13,6 +14,7 @@ interface Props {
|
|||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
watch(() => code.language, reset)
|
watch(() => code.language, reset)
|
||||||
|
|
||||||
@@ -25,6 +27,10 @@ function goSubmissions() {
|
|||||||
router.push({ name, query: { problem: props.problem._id } })
|
router.push({ name, query: { problem: props.problem._id } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function edit() {
|
||||||
|
router.push("/admin/problem/edit/" + props.problem.id)
|
||||||
|
}
|
||||||
|
|
||||||
const menu: DropdownOption[] = [
|
const menu: DropdownOption[] = [
|
||||||
{ label: "重置", key: "reset" },
|
{ label: "重置", key: "reset" },
|
||||||
{ label: "提交信息", key: "submissions" },
|
{ label: "提交信息", key: "submissions" },
|
||||||
@@ -88,6 +94,7 @@ function select(key: string) {
|
|||||||
<n-space>
|
<n-space>
|
||||||
<n-button @click="reset">重置</n-button>
|
<n-button @click="reset">重置</n-button>
|
||||||
<n-button @click="goSubmissions">提交信息</n-button>
|
<n-button @click="goSubmissions">提交信息</n-button>
|
||||||
|
<n-button v-if="userStore.isSuperAdmin" @click="edit">编辑</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ const columns = computed(() => {
|
|||||||
h(
|
h(
|
||||||
NButton,
|
NButton,
|
||||||
{ size: "small", onClick: () => rejudge(row.id) },
|
{ size: "small", onClick: () => rejudge(row.id) },
|
||||||
() => "重新评分"
|
() => "重新判题"
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,45 @@ export const routes: RouteRecordRaw[] = [
|
|||||||
{
|
{
|
||||||
path: "/admin",
|
path: "/admin",
|
||||||
component: () => import("~/shared/layout/admin.vue"),
|
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,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -75,7 +75,14 @@ const options = computed<Array<DropdownOption | DropdownDividerOption>>(() => [
|
|||||||
onClick: () => router.push("/setting"),
|
onClick: () => router.push("/setting"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ label: "后台管理", key: "admin", show: userStore.isAdminRole },
|
{
|
||||||
|
label: "后台管理",
|
||||||
|
key: "admin",
|
||||||
|
show: userStore.isAdminRole,
|
||||||
|
props: {
|
||||||
|
onClick: () => router.push("/admin"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{ type: "divider" },
|
{ type: "divider" },
|
||||||
{ label: "退出", key: "logout", props: { onClick: handleLogout } },
|
{ label: "退出", key: "logout", props: { onClick: handleLogout } },
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const monaco = ref<Monaco>()
|
|||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
loader.config({
|
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" } },
|
"vs/nls": { availableLanguages: { "*": "zh-cn" } },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
<template>
|
||||||
<n-layout>
|
<n-layout has-sider position="absolute">
|
||||||
<n-layout-content bordered>
|
<n-layout-sider bordered :native-scrollbar="false">
|
||||||
|
<n-menu :options="options" />
|
||||||
|
</n-layout-sider>
|
||||||
|
<n-layout-content :native-scrollbar="false">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</n-layout-content>
|
</n-layout-content>
|
||||||
|
<Login />
|
||||||
|
<Signup />
|
||||||
</n-layout>
|
</n-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user