This commit is contained in:
2025-10-06 19:38:44 +08:00
parent 96adf39cba
commit 0d19529632
2 changed files with 109 additions and 27 deletions

View File

@@ -1,8 +1,9 @@
<template>
<n-grid :cols="5" :x-gap="16" v-if="tutorial.id">
<div>
<!-- 桌面端布局 -->
<n-grid :cols="5" :x-gap="16" v-if="tutorial.id && isDesktop">
<n-gi :span="1">
<n-card title="目录" :bordered="false" size="small">
<n-scrollbar :style="{ maxHeight: 'calc(100vh - 180px)' }">
<n-card title="教程目录" :bordered="false" size="small">
<n-list hoverable clickable>
<n-list-item
v-for="(item, index) in titles"
@@ -17,7 +18,6 @@
</n-text>
</n-list-item>
</n-list>
</n-scrollbar>
</n-card>
</n-gi>
@@ -41,6 +41,63 @@
</n-card>
</n-gi>
</n-grid>
<!-- 手机端布局 -->
<template v-if="tutorial.id && !isDesktop">
<n-tabs type="line" animated v-model:value="activeTab">
<n-tab-pane name="catalog" tab="目录">
<n-list hoverable clickable>
<n-list-item
v-for="(item, index) in titles"
:key="item.id"
@click="goToLesson(index + 1)"
>
<n-text
:type="step === index + 1 ? 'primary' : undefined"
:strong="step === index + 1"
>
{{ index + 1 }}. {{ item.title }}
</n-text>
</n-list-item>
</n-list>
</n-tab-pane>
<n-tab-pane name="content" :tab="`第 ${step} 课`">
<MdPreview
preview-theme="vuepress"
:theme="isDark ? 'dark' : 'light'"
:model-value="tutorial.content"
/>
</n-tab-pane>
<n-tab-pane name="code" tab="示例代码" v-if="tutorial.code">
<CodeEditor language="Python3" v-model="tutorial.code" />
</n-tab-pane>
</n-tabs>
<n-divider style="margin: 12px 0" />
<n-flex align="center" justify="space-between">
<n-button
secondary
type="primary"
:disabled="isFirstLesson"
@click="goToPrevLesson"
>
上一课
</n-button>
<n-text>{{ step }} / {{ titles.length }}</n-text>
<n-button
secondary
type="primary"
:disabled="isLastLesson"
@click="goToNextLesson"
>
下一课
</n-button>
</n-flex>
</template>
</div>
</template>
<script setup lang="ts">
@@ -48,6 +105,7 @@ import { MdPreview } from "md-editor-v3"
import "md-editor-v3/lib/preview.css"
import { Tutorial } from "utils/types"
import { getTutorial, getTutorials } from "../api"
import { isDesktop } from "shared/composables/breakpoints"
const CodeEditor = defineAsyncComponent(
() => import("shared/components/CodeEditor.vue"),
@@ -72,12 +130,29 @@ const tutorial = ref<Partial<Tutorial>>({
})
const titles = ref<{ id: number; title: string }[]>([])
const activeTab = ref("content")
const isFirstLesson = computed(() => step.value === 1)
const isLastLesson = computed(() => step.value === titles.value.length)
function goToLesson(lessonNumber: number) {
activeTab.value = "content"
const dest = lessonNumber.toString().padStart(2, "0")
router.push("/learn/" + dest)
}
function goToPrevLesson() {
if (step.value > 1) {
goToLesson(step.value - 1)
}
}
function goToNextLesson() {
if (step.value < titles.value.length) {
goToLesson(step.value + 1)
}
}
async function init() {
const res1 = await getTutorials()
titles.value = res1.data
@@ -101,4 +176,12 @@ watch(
:deep(.md-editor-preview .md-editor-code .md-editor-code-head) {
z-index: 100;
}
:deep(.md-editor-preview h1) {
font-size: 1.6em;
}
:deep(.md-editor-preview h2) {
font-size: 1.4em;
}
</style>

View File

@@ -86,7 +86,6 @@ const menus = computed<MenuOption[]>(() => [
label: () => h(RouterLink, { to: "/learn/01" }, { default: () => "自学" }),
key: "learn",
icon: renderIcon("streamline-emojis:snake"),
show: isDesktop.value,
},
{
label: () => h(RouterLink, { to: "/" }, { default: () => "题库" }),