This commit is contained in:
2025-03-04 22:26:58 +08:00
parent dce79ef27d
commit 175e4c0d68
7 changed files with 114 additions and 84 deletions

1
components.d.ts vendored
View File

@@ -8,6 +8,7 @@ export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
Corner: typeof import('./src/components/Corner.vue')['default']
Editor: typeof import('./src/components/Editor.vue')['default'] Editor: typeof import('./src/components/Editor.vue')['default']
Editors: typeof import('./src/components/Editors.vue')['default'] Editors: typeof import('./src/components/Editors.vue')['default']
Login: typeof import('./src/components/Login.vue')['default'] Login: typeof import('./src/components/Login.vue')['default']

View File

@@ -25,7 +25,7 @@ watch(authed, (v) => {
<template> <template>
<n-config-provider class="myContainer" :locale="zhCN" :date-locale="dateZhCN"> <n-config-provider class="myContainer" :locale="zhCN" :date-locale="dateZhCN">
<n-modal-provider> <n-modal-provider>
<n-message-provider> <n-message-provider max="1">
<n-dialog-provider> <n-dialog-provider>
<router-view></router-view> <router-view></router-view>
<Login /> <Login />

View File

@@ -73,6 +73,11 @@ export class Tutorial {
return res.data return res.data
} }
static async togglePublic(display: number) {
const res = await http.put(`/tutorial/public/${display}`)
return res.data
}
static async remove(display: number) { static async remove(display: number) {
await http.delete(`/tutorial/${display}`) await http.delete(`/tutorial/${display}`)
} }

80
src/components/Corner.vue Normal file
View File

@@ -0,0 +1,80 @@
<template>
<n-flex align="center" class="corner">
<template v-if="user.loaded && authed">
<n-button type="primary" secondary @click="submit">提交</n-button>
<n-dropdown :options="menu" @select="clickMenu">
<n-button>{{ user.username }}</n-button>
</n-dropdown>
</template>
<n-button
v-if="user.loaded && !authed"
@click="handleLogin"
secondary
type="primary"
>
登录
</n-button>
</n-flex>
</template>
<script lang="ts" setup>
import { computed, h } from "vue"
import { useMessage } from "naive-ui"
import { Icon } from "@iconify/vue"
import { user, authed, roleNormal } from "../store/user"
import { loginModal } from "../store/modal"
import { Account } from "../api"
import { Role } from "../utils/type"
import { router } from "../router"
const message = useMessage()
const menu = computed(() => [
{
label: "后台",
key: "dashboard",
show: !roleNormal.value,
icon: () =>
h(Icon, {
icon: "streamline-emojis:robot-face-1",
}),
},
{
label: "退出",
key: "logout",
icon: () =>
h(Icon, {
icon: "streamline-emojis:hot-beverage-2",
}),
},
])
function clickMenu(name: string) {
switch (name) {
case "dashboard":
router.push({ name: "tutorial" })
break
case "logout":
handleLogout()
break
}
}
function handleLogin() {
loginModal.value = true
}
async function handleLogout() {
await Account.logout()
user.username = ""
user.role = Role.Normal
}
function submit() {
message.error("未实装")
}
</script>
<style scoped>
.corner {
margin-right: 20px;
}
</style>

View File

@@ -67,69 +67,15 @@
</n-flex> </n-flex>
</n-tab-pane> </n-tab-pane>
<template #suffix> <template #suffix>
<n-flex align="center" class="suffix"> <Corner />
<template v-if="user.loaded && authed">
<n-button type="primary" secondary @click="submit">提交</n-button>
<n-dropdown :options="menu" @select="clickMenu">
<n-button>{{ user.username }}</n-button>
</n-dropdown>
</template>
<n-button
v-if="user.loaded && !authed"
@click="handleLogin"
secondary
type="primary"
>
登录
</n-button>
</n-flex>
</template> </template>
</n-tabs> </n-tabs>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Icon } from "@iconify/vue" import { Icon } from "@iconify/vue"
import Editor from "./Editor.vue" import Editor from "./Editor.vue"
import Corner from "./Corner.vue"
import { html, css, js, tab, size, reset } from "../store/editors" import { html, css, js, tab, size, reset } from "../store/editors"
import { user, authed, roleNormal } from "../store/user"
import { loginModal } from "../store/modal"
import { Account } from "../api"
import { Role } from "../utils/type"
import { router } from "../router"
import { computed, h } from "vue"
import { useMessage } from "naive-ui"
const message = useMessage()
const menu = computed(() => [
{
label: "后台",
key: "dashboard",
show: !roleNormal.value,
icon: () =>
h(Icon, {
icon: "streamline-emojis:robot-face-1",
}),
},
{
label: "退出",
key: "logout",
icon: () =>
h(Icon, {
icon: "streamline-emojis:hot-beverage-2",
}),
},
])
function clickMenu(name: string) {
switch (name) {
case "dashboard":
router.push({ name: "tutorial" })
break
case "logout":
handleLogout()
break
}
}
function changeTab(name: string) { function changeTab(name: string) {
tab.value = name tab.value = name
@@ -138,20 +84,6 @@ function changeTab(name: string) {
function changeSize(num: number) { function changeSize(num: number) {
size.value = num size.value = num
} }
function handleLogin() {
loginModal.value = true
}
async function handleLogout() {
await Account.logout()
user.username = ""
user.role = Role.Normal
}
function submit() {
message.error("未实装")
}
</script> </script>
<style scoped> <style scoped>
.pane { .pane {
@@ -166,8 +98,4 @@ function submit() {
.label { .label {
font-size: 16px; font-size: 16px;
} }
.suffix {
margin-right: 20px;
}
</style> </style>

View File

@@ -4,10 +4,10 @@
<n-flex align="center"> <n-flex align="center">
<Icon icon="twemoji:open-book" :width="20"></Icon> <Icon icon="twemoji:open-book" :width="20"></Icon>
<n-text class="preview">教程(测试版)</n-text> <n-text class="preview">教程(测试版)</n-text>
<n-button text @click="prev" :disabled="prevDisabled"> <n-button text @click="prev" v-if="!hideNav" :disabled="prevDisabled">
<Icon :width="24" icon="pepicons-pencil:arrow-left"></Icon> <Icon :width="24" icon="pepicons-pencil:arrow-left"></Icon>
</n-button> </n-button>
<n-button text @click="next" :disabled="nextDisabled"> <n-button text @click="next" v-if="!hideNav" :disabled="nextDisabled">
<Icon :width="24" icon="pepicons-pencil:arrow-right"></Icon> <Icon :width="24" icon="pepicons-pencil:arrow-right"></Icon>
</n-button> </n-button>
</n-flex> </n-flex>
@@ -29,10 +29,13 @@ const displays = ref<number[]>([])
const content = ref("") const content = ref("")
const $content = useTemplateRef("$content") const $content = useTemplateRef("$content")
const hideNav = computed(() => displays.value.length <= 1)
const prevDisabled = computed(() => { const prevDisabled = computed(() => {
const i = displays.value.indexOf(step.value) const i = displays.value.indexOf(step.value)
return i <= 0 return i <= 0
}) })
const nextDisabled = computed(() => { const nextDisabled = computed(() => {
const i = displays.value.indexOf(step.value) const i = displays.value.indexOf(step.value)
return i === displays.value.length - 1 return i === displays.value.length - 1

View File

@@ -7,16 +7,20 @@
:key="item.display" :key="item.display"
@click="show(item.display)" @click="show(item.display)"
style="cursor: pointer" style="cursor: pointer"
:embedded="!item.is_public"
> >
<template #header> <template #header>
<n-flex align="center"> <n-flex align="center">
<Icon <n-button text @click.stop="togglePublic(item.display)">
:icon=" <Icon
item.is_public width="24"
? 'twemoji:check-mark-button' :icon="
: 'twemoji:locked' item.is_public
" ? 'twemoji:check-mark-button'
></Icon> : 'twemoji:locked'
"
></Icon>
</n-button>
<span>{{ item.display }}{{ item.title }}</span> <span>{{ item.display }}{{ item.title }}</span>
</n-flex> </n-flex>
</template> </template>
@@ -143,6 +147,15 @@ async function show(display: number) {
content.value = await marked.parse(item.content, { async: true }) content.value = await marked.parse(item.content, { async: true })
} }
async function togglePublic(display: number) {
const data = await Tutorial.togglePublic(display)
message.success(data.message)
list.value = list.value.map((item) => {
if (item.display === display) item.is_public = !item.is_public
return item
})
}
watch( watch(
() => tutorial.value.content, () => tutorial.value.content,
async (v: string) => { async (v: string) => {