use composables.

This commit is contained in:
2023-01-13 23:20:36 +08:00
parent d45783334d
commit d3caa5438d
15 changed files with 2360 additions and 72 deletions

1147
public/dark.json Normal file

File diff suppressed because it is too large Load Diff

1144
public/light.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ import Md from "./step-1/index.md"
import Monaco from "../shared/monaco/index.vue" import Monaco from "../shared/monaco/index.vue"
const route = useRoute() const route = useRoute()
console.log(route.params.step) // console.log(route.params.step)
const code = ref("") const code = ref("")
@@ -19,6 +19,7 @@ function change(value: string) {
<Md /> <Md />
{{ code }} {{ code }}
</el-col> </el-col>
<!-- TODO: 这里有BUG -->
<el-col :span="8"> <el-col :span="8">
<Monaco :value="code" @change="change" /> <Monaco :value="code" @change="change" />
</el-col> </el-col>

View File

@@ -1,6 +1,7 @@
import { createRouter, createWebHistory } from "vue-router" import { createRouter, createWebHistory } from "vue-router"
import { createPinia } from "pinia" import { createPinia } from "pinia"
import "normalize.css" import "normalize.css"
import "element-plus/theme-chalk/dark/css-vars.css"
import loader from "@monaco-editor/loader" import loader from "@monaco-editor/loader"
import storage from "utils/storage" import storage from "utils/storage"
@@ -9,7 +10,7 @@ import { STORAGE_KEY } from "utils/constants"
import { routes } from "./routes" import { routes } from "./routes"
import App from "./App.vue" import App from "./App.vue"
import { useLoginStore } from "~/shared/store/login" import { toggleLogin } from "./shared/composables/modal"
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
@@ -19,8 +20,7 @@ const router = createRouter({
router.beforeEach((to, from, next) => { 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)) {
const login = useLoginStore() toggleLogin(true)
login.show()
next("/") next("/")
} else { } else {
next() next()
@@ -32,9 +32,14 @@ router.beforeEach((to, from, next) => {
const pinia = createPinia() const pinia = createPinia()
Promise.all([loader.init(), fetch("/dracula.json")]).then(([monaco, dark]) => { loader.init().then((monaco) => {
window.monaco = monaco window.monaco = monaco
dark.json().then((data) => monaco.editor.defineTheme("dark", data)) fetch("/dark.json").then((data) =>
data.json().then((theme) => monaco.editor.defineTheme("dark", theme))
)
fetch("/light.json").then((data) =>
data.json().then((theme) => monaco.editor.defineTheme("light", theme))
)
}) })
loader.config({ loader.config({

View File

@@ -1,10 +1,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { TabsPaneContext } from "element-plus"
import { SOURCES } from "utils/constants" import { SOURCES } from "utils/constants"
import { Problem } from "utils/types" import { Problem } from "utils/types"
import Monaco from "~/shared/Monaco/index.vue" import Monaco from "~/shared/Monaco/index.vue"
import { useCodeStore } from "oj/store/code" import { useCodeStore } from "oj/store/code"
import { submissionExists } from "oj/api" import { submissionExists } from "oj/api"
import { TabsPaneContext } from "element-plus"
import SubmitPanel from "./SubmitPanel.vue" import SubmitPanel from "./SubmitPanel.vue"
import TestcasePanel from "./TestcasePanel.vue" import TestcasePanel from "./TestcasePanel.vue"
@@ -68,8 +68,8 @@ function onTab(pane: TabsPaneContext) {
class="editor" class="editor"
:language="code.language" :language="code.language"
:value="code.value" :value="code.value"
height="calc(100vh - 621px)"
@change="change" @change="change"
height="calc(100vh - 621px)"
/> />
<el-tabs type="border-card" @tab-click="onTab" v-model="tab"> <el-tabs type="border-card" @tab-click="onTab" v-model="tab">
<TestcasePanel /> <TestcasePanel />

View File

@@ -13,6 +13,7 @@ export const routes = [
{ {
path: "status", path: "status",
component: () => import("oj/status/list.vue"), component: () => import("oj/status/list.vue"),
meta: { requiresAuth: true },
}, },
{ {
path: "status/:statusID", path: "status/:statusID",
@@ -44,7 +45,7 @@ export const routes = [
{ {
path: "/learn", path: "/learn",
component: () => import("~/shared/layout/default.vue"), component: () => import("~/shared/layout/default.vue"),
children: [{ path: ":step*", component: () => import("learn/index.vue") }], children: [{ path: "", component: () => import("learn/index.vue") }],
}, },
{ {
path: "/admin", path: "/admin",

View File

@@ -1,11 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useLoginStore } from "../store/login" import { Sunny, Moon } from "@element-plus/icons-vue"
import { useSignupStore } from "~/shared/store/signup"
import { logout } from "../api" import { logout } from "../api"
import { useUserStore } from "../store/user" import { useUserStore } from "../store/user"
import { isDark, toggleDark } from "~/shared/composables/dark"
import { toggleLogin, toggleSignup } from "~/shared/composables/modal"
const loginStore = useLoginStore()
const signupStore = useSignupStore()
const userStore = useUserStore() const userStore = useUserStore()
const router = useRouter() const router = useRouter()
@@ -34,26 +33,33 @@ onMounted(userStore.getMyProfile)
<el-menu-item index="/status">提交</el-menu-item> <el-menu-item index="/status">提交</el-menu-item>
<el-menu-item index="/rank">排名</el-menu-item> <el-menu-item index="/rank">排名</el-menu-item>
</el-menu> </el-menu>
<div v-if="userStore.isFinished && !userStore.isAuthed" class="actions"> <el-space class="actions">
<el-button @click="loginStore.show">登录</el-button> <el-button
<el-button @click="signupStore.show">注册</el-button> circle
</div> :icon="isDark ? Sunny : Moon"
<div v-if="userStore.isFinished && userStore.isAuthed" class="actions"> @click="toggleDark()"
<el-dropdown @command="handleDropdown"> ></el-button>
<el-button>{{ userStore.user.username }}</el-button> <div v-if="userStore.isFinished && !userStore.isAuthed">
<template #dropdown> <el-button @click="toggleLogin(true)">登录</el-button>
<el-dropdown-menu> <el-button @click="toggleSignup(true)">注册</el-button>
<el-dropdown-item>我的主页</el-dropdown-item> </div>
<el-dropdown-item>我的提交</el-dropdown-item> <div v-if="userStore.isFinished && userStore.isAuthed">
<el-dropdown-item>我的设置</el-dropdown-item> <el-dropdown @command="handleDropdown">
<el-dropdown-item v-if="userStore.isAdminRole"> <el-button>{{ userStore.user.username }}</el-button>
后台管理 <template #dropdown>
</el-dropdown-item> <el-dropdown-menu>
<el-dropdown-item divided command="logout">退出</el-dropdown-item> <el-dropdown-item>我的主页</el-dropdown-item>
</el-dropdown-menu> <el-dropdown-item>我的提交</el-dropdown-item>
</template> <el-dropdown-item>我的设置</el-dropdown-item>
</el-dropdown> <el-dropdown-item v-if="userStore.isAdminRole">
</div> 后台管理
</el-dropdown-item>
<el-dropdown-item divided command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-space>
</template> </template>
<style scoped> <style scoped>

View File

@@ -1,12 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { FormInstance } from "element-plus" import { FormInstance } from "element-plus"
import { useSignupStore } from "~/shared/store/signup"
import { login } from "../api" import { login } from "../api"
import { useLoginStore } from "../store/login" import { loginModal, toggleLogin, toggleSignup } from "../composables/modal"
import { useUserStore } from "../store/user" import { useUserStore } from "../store/user"
const loginStore = useLoginStore()
const signupStore = useSignupStore()
const userStore = useUserStore() const userStore = useUserStore()
const loginRef = ref<FormInstance>() const loginRef = ref<FormInstance>()
const form = reactive({ const form = reactive({
@@ -29,15 +26,15 @@ async function submit() {
if (valid) { if (valid) {
await execute() await execute()
if (!error.value) { if (!error.value) {
loginStore.hide() toggleLogin(false)
userStore.getMyProfile() userStore.getMyProfile()
} }
} }
} }
function goSignup() { function goSignup() {
loginStore.hide() toggleLogin(false)
signupStore.show() toggleSignup(true)
} }
</script> </script>
@@ -46,7 +43,7 @@ function goSignup() {
style="max-width: 400px" style="max-width: 400px"
:close-on-click-modal="false" :close-on-click-modal="false"
:close-on-press-escape="false" :close-on-press-escape="false"
v-model="loginStore.visible" v-model="loginModal"
title="登录" title="登录"
> >
<el-form <el-form

View File

@@ -3,6 +3,7 @@ import type * as Monaco from "monaco-editor"
import { LANGUAGE_VALUE } from "utils/constants" import { LANGUAGE_VALUE } from "utils/constants"
import { LANGUAGE } from "utils/types" import { LANGUAGE } from "utils/types"
import { isMobile } from "utils/breakpoints" import { isMobile } from "utils/breakpoints"
import { isDark } from "../composables/dark"
interface Props { interface Props {
value: string value: string
@@ -35,7 +36,7 @@ onMounted(function () {
editor = window.monaco.editor.create(monacoEditorRef.value, { editor = window.monaco.editor.create(monacoEditorRef.value, {
model, model,
theme: "dark", // 官方自带三种主题vs, hc-black, or vs-dark theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
minimap: { minimap: {
enabled: false, enabled: false,
}, },
@@ -78,6 +79,10 @@ onMounted(function () {
model.setValue(props.value) model.setValue(props.value)
} }
}) })
watchEffect(() => {
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
})
}) })
onUnmounted(() => { onUnmounted(() => {

View File

@@ -1,13 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { useSignupStore } from "../store/signup" import { signupModal } from "../composables/modal"
const store = useSignupStore()
</script> </script>
<template> <template>
<el-dialog <el-dialog
:close-on-click-modal="false" :close-on-click-modal="false"
:close-on-press-escape="false" :close-on-press-escape="false"
v-model="store.visible" v-model="signupModal"
title="注册" title="注册"
> >
</el-dialog> </el-dialog>

View File

@@ -0,0 +1,2 @@
export const isDark = useDark({ storageKey: "theme-appearance" })
export const toggleDark = useToggle(isDark)

View File

@@ -0,0 +1,2 @@
export const [loginModal, toggleLogin] = useToggle()
export const [signupModal, toggleSignup] = useToggle()

View File

@@ -3,6 +3,7 @@ import type * as Monaco from "monaco-editor"
import { LANGUAGE_VALUE } from "utils/constants" import { LANGUAGE_VALUE } from "utils/constants"
import { LANGUAGE } from "utils/types" import { LANGUAGE } from "utils/types"
import { isMobile } from "utils/breakpoints" import { isMobile } from "utils/breakpoints"
import { isDark } from "../composables/dark"
interface Props { interface Props {
value: string value: string
@@ -35,7 +36,7 @@ onMounted(function () {
editor = window.monaco.editor.create(monacoEditorRef.value, { editor = window.monaco.editor.create(monacoEditorRef.value, {
model, model,
theme: "dark", // 官方自带三种主题vs, hc-black, or vs-dark theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
minimap: { minimap: {
enabled: false, enabled: false,
}, },
@@ -78,6 +79,10 @@ onMounted(function () {
model.setValue(props.value) model.setValue(props.value)
} }
}) })
watchEffect(() => {
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
})
}) })
onUnmounted(() => { onUnmounted(() => {

View File

@@ -1,13 +0,0 @@
export const useLoginStore = defineStore("login", () => {
const [visible] = useToggle()
function show() {
visible.value = true
}
function hide() {
visible.value = false
}
return { visible, show, hide }
})

View File

@@ -1,13 +0,0 @@
export const useSignupStore = defineStore("signup", () => {
const [visible] = useToggle()
function show() {
visible.value = true
}
function hide() {
visible.value = false
}
return { visible, show, hide }
})