add admin head.

This commit is contained in:
2023-03-13 19:42:29 +08:00
parent 89ba38ba0a
commit 341f0a0a2f
18 changed files with 197 additions and 18 deletions

6
package-lock.json generated
View File

@@ -17,6 +17,7 @@
"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",
"normalize.css": "^8.0.1",
"party-js": "^2.2.0", "party-js": "^2.2.0",
"pinia": "^2.0.33", "pinia": "^2.0.33",
"vue": "^3.2.47", "vue": "^3.2.47",
@@ -2966,6 +2967,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/normalize.css": {
"version": "8.0.1",
"resolved": "https://registry.npmmirror.com/normalize.css/-/normalize.css-8.0.1.tgz",
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
},
"node_modules/npm-run-path": { "node_modules/npm-run-path": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz",

View File

@@ -19,6 +19,7 @@
"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",
"normalize.css": "^8.0.1",
"party-js": "^2.2.0", "party-js": "^2.2.0",
"pinia": "^2.0.33", "pinia": "^2.0.33",
"vue": "^3.2.47", "vue": "^3.2.47",

View File

@@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import "normalize.css"
import { zhCN, dateZhCN, darkTheme } from "naive-ui" import { zhCN, dateZhCN, darkTheme } from "naive-ui"
import { isDark } from "./shared/composables/dark" import { isDark } from "./shared/composables/dark"
import hljs from "highlight.js/lib/core" import hljs from "highlight.js/lib/core"

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>contest detail</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>contest list</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>contest problem list</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>problem detail</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>problem list</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>announcement</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>conf</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>user import</div>
</template>
<style scoped></style>

7
src/admin/user/list.vue Normal file
View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>user list</div>
</template>
<style scoped></style>

View File

@@ -18,6 +18,7 @@ router.beforeEach((to, from, next) => {
if (to.matched.some((record) => record.meta.requiresAuth)) { if (to.matched.some((record) => record.meta.requiresAuth)) {
if (!storage.get(STORAGE_KEY.AUTHED)) { if (!storage.get(STORAGE_KEY.AUTHED)) {
toggleLogin(true) toggleLogin(true)
next("/")
} else { } else {
next() next()
} }

View File

@@ -1,5 +1,7 @@
import { RouteRecordRaw } from "vue-router" import { RouteRecordRaw } from "vue-router"
import { getProfile } from "./shared/api"
import { loadChart } from "./shared/composables/chart" import { loadChart } from "./shared/composables/chart"
import { USER_TYPE } from "./utils/constants"
export const routes: RouteRecordRaw[] = [ export const routes: RouteRecordRaw[] = [
{ {
@@ -95,42 +97,80 @@ export const routes: RouteRecordRaw[] = [
{ {
path: "/admin", path: "/admin",
component: () => import("~/shared/layout/admin.vue"), component: () => import("~/shared/layout/admin.vue"),
meta: { requiresAuth: true },
beforeEnter: async () => {
const res = await getProfile()
if (res.data.user.admin_type === USER_TYPE.REGULAR_USER) return "/"
},
children: [ children: [
{ path: "", component: () => import("admin/index.vue") }, { path: "", name: "home", component: () => import("admin/index.vue") },
{ path: "user", component: () => import("admin/setting/user.vue") }, {
{ path: "conf", component: () => import("admin/setting/conf.vue") }, path: "config",
{ path: "problems", component: () => import("admin/problem/list.vue") }, name: "config",
component: () => import("admin/setting/config.vue"),
},
{
path: "announcement",
name: "announcement",
component: () => import("admin/setting/announcement.vue"),
},
{
path: "user/list",
name: "user list",
component: () => import("admin/user/list.vue"),
},
{
path: "user/import",
name: "user importing",
component: () => import("admin/user/import.vue"),
},
{
path: "problem/list",
name: "problem list",
component: () => import("admin/problem/list.vue"),
},
{ {
path: "problem/create", path: "problem/create",
name: "problem create",
component: () => import("admin/problem/detail.vue"), component: () => import("admin/problem/detail.vue"),
}, },
{ {
path: "problem/:problemID/edit", path: "problem/:problemID/edit",
name: "problem edit",
component: () => import("admin/problem/detail.vue"), component: () => import("admin/problem/detail.vue"),
props: true, props: true,
}, },
{ path: "contests", component: () => import("admin/contest/list.vue") }, {
path: "contest/list",
name: "contest list",
component: () => import("admin/contest/list.vue"),
},
{ {
path: "contest/create", path: "contest/create",
name: "contest create",
component: () => import("admin/contest/detail.vue"), component: () => import("admin/contest/detail.vue"),
}, },
{ {
path: "contest/:contestID/edit", path: "contest/:contestID/edit",
name: "contest edit",
component: () => import("admin/contest/detail.vue"), component: () => import("admin/contest/detail.vue"),
props: true, props: true,
}, },
{ {
path: "contest/:contestID/problems", path: "contest/:contestID/problems",
name: "contest problems",
component: () => import("admin/contest/detail.vue"), component: () => import("admin/contest/detail.vue"),
props: true, props: true,
}, },
{ {
path: "contest/:contestID/problem/create", path: "contest/:contestID/problem/create",
name: "contest problem create",
component: () => import("admin/problem/detail.vue"), component: () => import("admin/problem/detail.vue"),
props: true, props: true,
}, },
{ {
path: "contest/:contestID/problem/:problemID/edit", path: "contest/:contestID/problem/:problemID/edit",
name: "contest problem edit",
component: () => import("admin/problem/detail.vue"), component: () => import("admin/problem/detail.vue"),
props: true, props: true,
}, },

View File

@@ -1,4 +1,5 @@
import http from "utils/http" import http from "utils/http"
import { Profile } from "~/utils/types"
export function login(data: { username: string; password: string }) { export function login(data: { username: string; password: string }) {
return http.post("login", data) return http.post("login", data)
@@ -9,5 +10,5 @@ export function logout() {
} }
export function getProfile(username: string = "") { export function getProfile(username: string = "") {
return http.get("profile", { params: { username } }) return http.get<Profile>("profile", { params: { username } })
} }

View File

@@ -1,34 +1,100 @@
<script setup lang="ts"> <script setup lang="ts">
import { MenuOption } from "naive-ui" import { MenuOption } from "naive-ui"
import { RouterLink } from "vue-router"
import Login from "../Login.vue" import Login from "../Login.vue"
import Signup from "../Signup.vue"
const route = useRoute()
const options: MenuOption[] = [ const options: MenuOption[] = [
{ label: "题目", key: "problem", disabled: true }, {
{ label: "题目列表", key: "problem list" }, label: () => h(RouterLink, { to: "/admin" }, { default: () => "首页" }),
{ label: "创建题目", key: "create problem" }, key: "home",
},
{
label: "题目",
key: "problem",
disabled: true,
},
{
label: () =>
h(
RouterLink,
{ to: "/admin/problem/list" },
{ default: () => "题目列表" }
),
key: "problem list",
},
{
label: () =>
h(
RouterLink,
{ to: "/admin/problem/create" },
{ default: () => "创建题目" }
),
key: "problem create",
},
{ label: "用户", key: "user", disabled: true }, { label: "用户", key: "user", disabled: true },
{ label: "用户列表", key: "user list" }, {
{ label: "导入用户", key: "user import" }, label: () =>
h(RouterLink, { to: "/admin/user/list" }, { default: () => "用户列表" }),
key: "user list",
},
{
label: () =>
h(
RouterLink,
{ to: "/admin/user/import" },
{ default: () => "导入用户" }
),
key: "user importing",
},
{ label: "比赛", key: "contest", disabled: true }, { label: "比赛", key: "contest", disabled: true },
{ label: "比赛列表", key: "contest list" }, {
{ label: "创建比赛", key: "create contest" }, label: () =>
h(
RouterLink,
{ to: "/admin/contest/list" },
{ default: () => "比赛列表" }
),
key: "contest list",
},
{
label: () =>
h(
RouterLink,
{ to: "/admin/contest/create" },
{ default: () => "创建比赛" }
),
key: "contest create",
},
{ label: "其他", key: "other", disabled: true }, { label: "其他", key: "other", disabled: true },
{ label: "系统配置", key: "config" }, {
{ label: "公告配置", key: "announcement" }, label: () =>
h(RouterLink, { to: "/admin/config" }, { default: () => "系统配置" }),
key: "config",
},
{
label: () =>
h(
RouterLink,
{ to: "/admin/announcement" },
{ default: () => "公告配置" }
),
key: "announcement",
},
] ]
const active = computed(() => (route.name as string) || "")
</script> </script>
<template> <template>
<n-layout has-sider position="absolute"> <n-layout has-sider position="absolute">
<n-layout-sider bordered :native-scrollbar="false"> <n-layout-sider bordered :native-scrollbar="false">
<n-menu :options="options" /> <n-menu :options="options" :value="active" />
</n-layout-sider> </n-layout-sider>
<n-layout-content :native-scrollbar="false"> <n-layout-content :native-scrollbar="false">
<router-view></router-view> <router-view></router-view>
</n-layout-content> </n-layout-content>
<Login /> <Login />
<Signup />
</n-layout> </n-layout>
</template> </template>