223 lines
5.9 KiB
Vue
223 lines
5.9 KiB
Vue
<script setup lang="ts">
|
|
import { logout } from "../api"
|
|
import { useUserStore } from "../store/user"
|
|
import { useConfigStore } from "../store/config"
|
|
import { toggleLogin, toggleSignup } from "~/shared/composables/modal"
|
|
import { RouterLink } from "vue-router"
|
|
import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
|
|
import {
|
|
screenSwitchLabel,
|
|
switchScreenMode,
|
|
} from "~/shared/composables/switchScreen"
|
|
import { Icon } from "@iconify/vue"
|
|
|
|
const isDark = useDark()
|
|
const toggleDark = useToggle(isDark)
|
|
const userStore = useUserStore()
|
|
const configStore = useConfigStore()
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const active = computed(() => {
|
|
const path = route.path.split("/")[1] || "problem"
|
|
return !["user", "setting"].includes(path) ? path : ""
|
|
})
|
|
|
|
async function handleLogout() {
|
|
await logout()
|
|
userStore.clearProfile()
|
|
router.replace("/")
|
|
}
|
|
|
|
function renderIcon(icon: string) {
|
|
return () => h(Icon, { icon, width: 24, height: 24 })
|
|
}
|
|
|
|
const avatars = [
|
|
"streamline-emojis:man-with-chinese-cap-1",
|
|
"streamline-emojis:cat-face",
|
|
"streamline-emojis:china",
|
|
"streamline-emojis:chicken",
|
|
"streamline-emojis:eyes",
|
|
"streamline-emojis:elephant",
|
|
"streamline-emojis:hear-no-evil-monkey",
|
|
"streamline-emojis:panda-face",
|
|
"streamline-emojis:penguin-1",
|
|
"streamline-emojis:rooster",
|
|
"streamline-emojis:star-struck-1",
|
|
"streamline-emojis:tomato",
|
|
"streamline-emojis:rocket",
|
|
"streamline-emojis:sparkles",
|
|
"streamline-emojis:money-bag",
|
|
"streamline-emojis:ghost",
|
|
"streamline-emojis:game-dice",
|
|
"streamline-emojis:ewe-1",
|
|
"streamline-emojis:artist-palette",
|
|
"streamline-emojis:baby-bottle",
|
|
]
|
|
|
|
const avatar = ref(avatars[Math.floor(Math.random() * avatars.length)])
|
|
|
|
function getRandomAvatar() {
|
|
avatar.value = avatars[Math.floor(Math.random() * avatars.length)]
|
|
}
|
|
|
|
onMounted(() => {
|
|
userStore.getMyProfile()
|
|
configStore.getConfig()
|
|
})
|
|
|
|
const menus = computed<MenuOption[]>(() => [
|
|
{
|
|
label: () => h(RouterLink, { to: "/" }, { default: () => "题库" }),
|
|
key: "problem",
|
|
icon: renderIcon("streamline-emojis:jack-o-lantern"),
|
|
},
|
|
{
|
|
label: () =>
|
|
h(RouterLink, { to: "/submission" }, { default: () => "提交" }),
|
|
key: "submission",
|
|
icon: renderIcon("streamline-emojis:lemon"),
|
|
},
|
|
{
|
|
label: () => h(RouterLink, { to: "/contest" }, { default: () => "比赛" }),
|
|
key: "contest",
|
|
icon: renderIcon("streamline-emojis:direct-hit"),
|
|
},
|
|
{
|
|
label: () => h(RouterLink, { to: "/rank" }, { default: () => "排名" }),
|
|
key: "rank",
|
|
icon: renderIcon("streamline-emojis:crown"),
|
|
},
|
|
{
|
|
label: () =>
|
|
h(RouterLink, { to: "/announcement" }, { default: () => "公告" }),
|
|
key: "announcement",
|
|
icon: renderIcon("streamline-emojis:police-car-light"),
|
|
},
|
|
{
|
|
label: () => h(RouterLink, { to: "/admin" }, { default: () => "后台" }),
|
|
show: userStore.isAdminRole,
|
|
key: "admin",
|
|
icon: renderIcon("streamline-emojis:unicorn-face"),
|
|
},
|
|
])
|
|
|
|
const options: Array<DropdownOption | DropdownDividerOption> = [
|
|
{
|
|
label: "我的主页",
|
|
key: "home",
|
|
icon: renderIcon("streamline-emojis:clipboard"),
|
|
props: {
|
|
onClick: () => router.push("/user"),
|
|
},
|
|
},
|
|
{
|
|
label: "我的消息",
|
|
key: "message",
|
|
icon: renderIcon("streamline-emojis:herb"),
|
|
props: {
|
|
onClick: () => router.push("/message"),
|
|
},
|
|
},
|
|
{
|
|
label: "我的提交",
|
|
key: "status",
|
|
icon: renderIcon("streamline-emojis:bar-chart"),
|
|
props: {
|
|
onClick: () => router.push("/submission?myself=1"),
|
|
},
|
|
},
|
|
{
|
|
label: "我的设置",
|
|
key: "setting",
|
|
icon: renderIcon("streamline-emojis:game-dice"),
|
|
props: {
|
|
onClick: () => router.push("/setting"),
|
|
},
|
|
},
|
|
{ type: "divider" },
|
|
{
|
|
label: "退出",
|
|
key: "logout",
|
|
icon: renderIcon("streamline-emojis:bathtub"),
|
|
props: { onClick: handleLogout },
|
|
},
|
|
]
|
|
|
|
function goHome() {
|
|
router.push("/")
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<n-space justify="space-between" align="center">
|
|
<n-space align="center">
|
|
<n-flex align="center" class="title" @click="goHome">
|
|
<Icon icon="streamline-emojis:dog" :width="30" :height="30"></Icon>
|
|
<div>
|
|
{{ configStore.config?.website_name }}
|
|
</div>
|
|
</n-flex>
|
|
<n-menu
|
|
v-if="isDesktop"
|
|
mode="horizontal"
|
|
:options="menus"
|
|
:value="active"
|
|
/>
|
|
</n-space>
|
|
<n-space align="center">
|
|
<n-dropdown v-if="isMobile" :options="menus">
|
|
<n-button>
|
|
<Icon
|
|
icon="streamline-emojis:clipboard"
|
|
:height="24"
|
|
:width="24"
|
|
></Icon>
|
|
<span style="padding-left: 8px">菜单</span>
|
|
</n-button>
|
|
</n-dropdown>
|
|
<n-button
|
|
v-if="
|
|
isDesktop &&
|
|
(route.name === 'problem' || route.name === 'contest problem')
|
|
"
|
|
@click="switchScreenMode"
|
|
>
|
|
{{ screenSwitchLabel }}
|
|
</n-button>
|
|
<div v-if="userStore.isFinished">
|
|
<n-dropdown v-if="userStore.isAuthed" :options="options">
|
|
<n-button @click="getRandomAvatar">
|
|
<Icon :icon="avatar" :height="24" :width="24"></Icon>
|
|
<span style="padding-left: 8px">{{
|
|
userStore.user!.username
|
|
}}</span>
|
|
</n-button>
|
|
</n-dropdown>
|
|
<n-flex align="center" v-else>
|
|
<n-button @click="toggleLogin(true)">登录</n-button>
|
|
<n-button
|
|
v-if="configStore.config?.allow_register"
|
|
@click="toggleSignup(true)"
|
|
>
|
|
注册
|
|
</n-button>
|
|
</n-flex>
|
|
</div>
|
|
<n-button circle @click="toggleDark()">
|
|
<template #icon>
|
|
<Icon v-if="isDark" icon="ph:sun"></Icon>
|
|
<Icon v-else icon="ph:moon"></Icon>
|
|
</template>
|
|
</n-button>
|
|
</n-space>
|
|
</n-space>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.title {
|
|
font-size: 18px;
|
|
cursor: pointer;
|
|
}
|
|
</style>
|