重构主题切换和题目页面的切换

This commit is contained in:
2024-07-05 13:05:02 +08:00
parent fdf0de9db1
commit 2e260db9e5
4 changed files with 91 additions and 84 deletions

View File

@@ -2,7 +2,11 @@
import { getProblem } from "oj/api" import { getProblem } from "oj/api"
import { ScreenMode } from "utils/constants" import { ScreenMode } from "utils/constants"
import { isDesktop, isMobile } from "~/shared/composables/breakpoints" import { isDesktop, isMobile } from "~/shared/composables/breakpoints"
import { screenMode } from "~/shared/composables/switchScreen" import {
bothAndProblem,
resetScreenMode,
screenMode,
} from "~/shared/composables/switchScreen"
import { problem } from "../composables/problem" import { problem } from "../composables/problem"
const Editor = defineAsyncComponent(() => import("./components/Editor.vue")) const Editor = defineAsyncComponent(() => import("./components/Editor.vue"))
@@ -33,14 +37,8 @@ const props = withDefaults(defineProps<Props>(), {
const errMsg = ref("无数据") const errMsg = ref("无数据")
const bothAndProblem = computed(
() =>
screenMode.value === ScreenMode.both ||
screenMode.value === ScreenMode.problem,
)
async function init() { async function init() {
screenMode.value = ScreenMode.both resetScreenMode()
try { try {
const res = await getProblem(props.problemID, props.contestID) const res = await getProblem(props.problemID, props.contestID)
problem.value = res.data problem.value = res.data
@@ -55,13 +53,11 @@ onMounted(init)
onBeforeUnmount(() => { onBeforeUnmount(() => {
problem.value = null problem.value = null
errMsg.value = "无数据" errMsg.value = "无数据"
screenMode.value = ScreenMode.both resetScreenMode()
}) })
watch(isMobile, (value) => { watch(isMobile, (value) => {
if (value) { if (value) resetScreenMode()
screenMode.value = ScreenMode.both
}
}) })
</script> </script>

View File

@@ -3,10 +3,8 @@ import { Icon } from "@iconify/vue"
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 { toggleLogin, toggleSignup } from "~/shared/composables/modal" import { toggleLogin, toggleSignup } from "~/shared/composables/modal"
import { import { screenMode, switchScreenMode } from "~/shared/composables/switchScreen"
screenSwitchLabel, import { avatar, getRandomAvatar } from "~/utils/constants"
switchScreenMode,
} from "~/shared/composables/switchScreen"
import { logout } from "../api" import { logout } from "../api"
import { useConfigStore } from "../store/config" import { useConfigStore } from "../store/config"
import { useUserStore } from "../store/user" import { useUserStore } from "../store/user"
@@ -32,35 +30,6 @@ function renderIcon(icon: string) {
return () => h(Icon, { icon, width: 24, height: 24 }) 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(() => { onMounted(() => {
userStore.getMyProfile() userStore.getMyProfile()
configStore.getConfig() configStore.getConfig()
@@ -102,7 +71,7 @@ const menus = computed<MenuOption[]>(() => [
}, },
]) ])
const options: Array<DropdownOption | DropdownDividerOption> = [ const options = computed<Array<DropdownOption | DropdownDividerOption>>(() => [
{ {
label: "我的主页", label: "我的主页",
key: "home", key: "home",
@@ -135,6 +104,18 @@ const options: Array<DropdownOption | DropdownDividerOption> = [
onClick: () => router.push("/setting"), onClick: () => router.push("/setting"),
}, },
}, },
{
label: isDark.value ? "浅色主题" : "深色主题",
key: "theme",
icon: renderIcon(
isDark.value
? "twemoji:sun-behind-small-cloud"
: "twemoji:cloud-with-lightning-and-rain",
),
props: {
onClick: () => toggleDark(),
},
},
{ type: "divider" }, { type: "divider" },
{ {
label: "退出", label: "退出",
@@ -142,7 +123,7 @@ const options: Array<DropdownOption | DropdownDividerOption> = [
icon: renderIcon("streamline-emojis:hot-beverage-2"), icon: renderIcon("streamline-emojis:hot-beverage-2"),
props: { onClick: handleLogout }, props: { onClick: handleLogout },
}, },
] ])
function goHome() { function goHome() {
router.push("/") router.push("/")
@@ -150,20 +131,22 @@ function goHome() {
</script> </script>
<template> <template>
<n-space justify="space-between" align="center"> <n-flex justify="space-between" align="center">
<n-space align="center"> <n-flex align="center">
<n-flex align="center" class="title" @click="goHome"> <n-flex align="center" class="title" @click="goHome">
<Icon icon="streamline-emojis:dog" :width="30" :height="30"></Icon> <Icon icon="streamline-emojis:dog" :width="30" :height="30"></Icon>
<div v-if="isDesktop">{{ configStore.config?.website_name }}</div> <div v-if="isDesktop">{{ configStore.config?.website_name }}</div>
</n-flex> </n-flex>
<div>
<n-menu <n-menu
v-if="isDesktop" v-if="isDesktop"
mode="horizontal" mode="horizontal"
:options="menus" :options="menus"
:value="active" :value="active"
/> />
</n-space> </div>
<n-space align="center"> </n-flex>
<n-flex align="center">
<n-dropdown v-if="isMobile" :options="menus" size="large"> <n-dropdown v-if="isMobile" :options="menus" size="large">
<n-button>菜单</n-button> <n-button>菜单</n-button>
</n-dropdown> </n-dropdown>
@@ -172,9 +155,9 @@ function goHome() {
isDesktop && isDesktop &&
(route.name === 'problem' || route.name === 'contest problem') (route.name === 'problem' || route.name === 'contest problem')
" "
@click="switchScreenMode" @click="() => switchScreenMode()"
> >
{{ screenSwitchLabel }} {{ screenMode }}
</n-button> </n-button>
<div v-if="userStore.isFinished"> <div v-if="userStore.isFinished">
<n-dropdown v-if="userStore.isAuthed" :options="options" size="large"> <n-dropdown v-if="userStore.isAuthed" :options="options" size="large">
@@ -186,8 +169,11 @@ function goHome() {
</n-button> </n-button>
</n-dropdown> </n-dropdown>
<n-flex align="center" v-else> <n-flex align="center" v-else>
<n-button @click="toggleLogin(true)">登录</n-button> <n-button secondary type="primary" @click="toggleLogin(true)">
登录
</n-button>
<n-button <n-button
tertiary
v-if="configStore.config?.allow_register" v-if="configStore.config?.allow_register"
@click="toggleSignup(true)" @click="toggleSignup(true)"
> >
@@ -195,14 +181,8 @@ function goHome() {
</n-button> </n-button>
</n-flex> </n-flex>
</div> </div>
<n-button circle @click="toggleDark()"> </n-flex>
<template #icon> </n-flex>
<Icon v-if="isDark" icon="ph:sun"></Icon>
<Icon v-else icon="ph:moon"></Icon>
</template>
</n-button>
</n-space>
</n-space>
</template> </template>
<style scoped> <style scoped>

View File

@@ -1,17 +1,19 @@
import { ScreenMode } from "~/utils/constants" import { ScreenMode } from "~/utils/constants"
export const screenMode = ref(ScreenMode.both) export const { state: screenMode, next: switchScreenMode } = useCycleList(
Object.values(ScreenMode),
{
initialValue: ScreenMode.both,
},
)
export const screenSwitchLabel = computed(() => { export function resetScreenMode() {
if (screenMode.value === ScreenMode.both) return "题目 | 自测" screenMode.value = ScreenMode.both
else if (screenMode.value === ScreenMode.problem) return "仅题目"
return "仅自测"
})
export function switchScreenMode() {
if (screenMode.value === ScreenMode.both) {
screenMode.value = ScreenMode.problem
return
}
screenMode.value += 1
} }
export const bothAndProblem = computed(
() =>
screenMode.value === ScreenMode.both ||
screenMode.value === ScreenMode.problem,
)

View File

@@ -271,7 +271,36 @@ export const CODE_TEMPLATES = {
} }
export enum ScreenMode { export enum ScreenMode {
problem, both = "题目 | 自测",
code, code = "仅自测",
both, problem = "仅题目",
}
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",
]
export const avatar = ref(AVATARS[Math.floor(Math.random() * AVATARS.length)])
export function getRandomAvatar() {
avatar.value = AVATARS[Math.floor(Math.random() * AVATARS.length)]
} }