重构主题切换和题目页面的切换
This commit is contained in:
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) {
|
export const bothAndProblem = computed(
|
||||||
screenMode.value = ScreenMode.problem
|
() =>
|
||||||
return
|
screenMode.value === ScreenMode.both ||
|
||||||
}
|
screenMode.value === ScreenMode.problem,
|
||||||
screenMode.value += 1
|
)
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user