后台教程

This commit is contained in:
2025-03-05 14:31:22 +08:00
parent d2d4c2974e
commit 24880e62ad
9 changed files with 48 additions and 19 deletions

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 max="1"> <n-message-provider :max="1">
<n-dialog-provider> <n-dialog-provider>
<router-view></router-view> <router-view></router-view>
<Login /> <Login />

View File

@@ -61,7 +61,7 @@ export class Account {
export class Tutorial { export class Tutorial {
static async list() { static async list() {
const res = await http.get("/tutorial/") const res = await http.get("/tutorial/list")
return res.data return res.data
} }

View File

@@ -22,6 +22,7 @@ import { useMessage } from "naive-ui"
import { Icon } from "@iconify/vue" import { Icon } from "@iconify/vue"
import { user, authed, roleNormal, roleSuper } from "../store/user" import { user, authed, roleNormal, roleSuper } from "../store/user"
import { loginModal } from "../store/modal" import { loginModal } from "../store/modal"
import { step } from "../store/tutorial"
import { Account } from "../api" import { Account } from "../api"
import { Role } from "../utils/type" import { Role } from "../utils/type"
import { router } from "../router" import { router } from "../router"
@@ -61,7 +62,7 @@ const menu = computed(() => [
function clickMenu(name: string) { function clickMenu(name: string) {
switch (name) { switch (name) {
case "dashboard": case "dashboard":
router.push({ name: "tutorial" }) router.push({ name: "tutorial", params: { display: step.value } })
break break
case "admin": case "admin":
window.open(ADMIN_URL) window.open(ADMIN_URL)

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="container"> <div class="container">
<n-flex align="center" class="title"> <n-flex align="center" justify="space-between" class="title">
<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>
@@ -11,6 +11,13 @@
<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>
<n-button
v-if="authed && roleSuper"
quaternary
@click="$router.push({ name: 'tutorial', params: { display: step } })"
>
修改
</n-button>
</n-flex> </n-flex>
<div class="markdown-body" v-html="content" ref="$content"></div> <div class="markdown-body" v-html="content" ref="$content"></div>
</div> </div>
@@ -20,11 +27,10 @@ import { Icon } from "@iconify/vue"
import { html, css, js, tab } from "../store/editors" import { html, css, js, tab } from "../store/editors"
import { computed, onMounted, ref, useTemplateRef, watch } from "vue" import { computed, onMounted, ref, useTemplateRef, watch } from "vue"
import { marked } from "marked" import { marked } from "marked"
import { useStorage } from "@vueuse/core"
import { Tutorial } from "../api" import { Tutorial } from "../api"
import { STORAGE_KEY } from "../utils/const" import { step } from "../store/tutorial"
import { authed, roleSuper } from "../store/user"
const step = useStorage(STORAGE_KEY.STEP, 1)
const displays = ref<number[]>([]) const displays = ref<number[]>([])
const content = ref("") const content = ref("")
const $content = useTemplateRef("$content") const $content = useTemplateRef("$content")

View File

@@ -6,8 +6,8 @@
</n-button> </n-button>
<n-button <n-button
v-for="item in menu" v-for="item in menu"
:type="$route.name === item.name ? 'primary' : 'default'" :type="$route.name === item.route.name ? 'primary' : 'default'"
@click="$router.push({ name: item.name })" @click="$router.push(item.route)"
> >
{{ item.label }} {{ item.label }}
</n-button> </n-button>
@@ -18,9 +18,14 @@
</n-flex> </n-flex>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { step } from "../store/tutorial"
const menu = [ const menu = [
{ label: "教程", name: "tutorial" }, {
{ label: "用户", name: "user-manage" }, label: "教程",
route: { name: "tutorial", params: { display: step.value } },
},
{ label: "用户", route: { name: "user-manage" } },
] ]
</script> </script>
<style scoped> <style scoped>

View File

@@ -6,7 +6,11 @@
v-for="item in list" v-for="item in list"
:key="item.display" :key="item.display"
@click="show(item.display)" @click="show(item.display)"
style="cursor: pointer" class="card"
:header-style="{
backgroundColor:
item.display === tutorial.display ? 'rgba(24, 160, 80, 0.1)' : '',
}"
:embedded="!item.is_public" :embedded="!item.is_public"
> >
<template #header> <template #header>
@@ -72,11 +76,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { marked } from "marked" import { marked } from "marked"
import { computed, onMounted, reactive, ref, watch } from "vue" import { computed, onMounted, reactive, ref, watch } from "vue"
import { useRoute, useRouter } from "vue-router"
import { Icon } from "@iconify/vue" import { Icon } from "@iconify/vue"
import { Tutorial } from "../api" import { Tutorial } from "../api"
import type { TutorialSlim } from "../utils/type" import type { TutorialSlim } from "../utils/type"
import { useDialog, useMessage } from "naive-ui" import { useDialog, useMessage } from "naive-ui"
const route = useRoute()
const router = useRouter()
const message = useMessage() const message = useMessage()
const confirm = useDialog() const confirm = useDialog()
@@ -90,14 +97,11 @@ const tutorial = reactive({
}) })
const canSubmit = computed( const canSubmit = computed(
() => () => tutorial.display && tutorial.title && tutorial.content,
tutorial.display && tutorial.title && tutorial.content,
) )
async function getContent() { async function getContent() {
const res = await Tutorial.list() list.value = await Tutorial.list()
list.value = res.list show(Number(route.params.display))
const data = tutorial.content || res.first?.content
content.value = await marked.parse(data ?? "", { async: true })
} }
function createNew() { function createNew() {
@@ -137,6 +141,7 @@ async function remove(display: number) {
} }
async function show(display: number) { async function show(display: number) {
router.push({ name: "tutorial", params: { display } })
const item = await Tutorial.get(display) const item = await Tutorial.get(display)
tutorial.display = item.display tutorial.display = item.display
tutorial.title = item.title tutorial.title = item.title
@@ -173,6 +178,10 @@ onMounted(getContent)
padding-top: 10px; padding-top: 10px;
} }
.card {
cursor: pointer;
}
.editor { .editor {
height: calc(100vh - 200px); height: calc(100vh - 200px);
} }

View File

@@ -13,7 +13,7 @@ const routes = [
meta: { auth: true }, meta: { auth: true },
children: [ children: [
{ {
path: "tutorial", path: "tutorial/:display",
name: "tutorial", name: "tutorial",
component: () => import("./pages/Markdown.vue"), component: () => import("./pages/Markdown.vue"),
}, },

4
src/store/tutorial.ts Normal file
View File

@@ -0,0 +1,4 @@
import { useStorage } from "@vueuse/core"
import { STORAGE_KEY } from "../utils/const"
export const step = useStorage(STORAGE_KEY.STEP, 1)

View File

@@ -18,6 +18,10 @@ export interface TutorialSlim {
is_public: boolean is_public: boolean
} }
export interface TutorialReturn extends TutorialSlim {
content: string
}
export interface TutorialIn { export interface TutorialIn {
display: number display: number
title: string title: string