添加教程

This commit is contained in:
2025-02-25 20:15:40 +08:00
parent fad4b2b867
commit eec8c3d0ac
8 changed files with 126 additions and 59 deletions

View File

@@ -11,6 +11,9 @@
color: red;
}
```
```js
console.log("122")
```
## 什么是 HTML

View File

@@ -1 +1,7 @@
# 什么是 CSS
```css
.greeting {
color: red;
}
```

View File

@@ -29,10 +29,10 @@ whenever(ctrl_r, () => {})
:date-locale="dateZhCN"
inline-theme-disabled
>
<n-split :default-size="1 / 3" min="300px" max="700px">
<n-split :default-size="1 / 3" min="300px" max="800px">
<template #1><Tutorial /></template>
<template #2>
<n-split direction="vertical">
<n-split direction="vertical" min="200px">
<template #1>
<Editors />
</template>

View File

@@ -2,8 +2,8 @@
<n-tabs
style="height: 100%"
pane-class="pane"
:default-value="currentTab"
type="segment"
:value="tab"
type="card"
@update:value="changeTab"
>
<n-tab-pane name="html" tab="HTML">
@@ -24,11 +24,11 @@
</template>
<Editor language="css" :font-size="size" v-model:value="css" />
</n-tab-pane>
<n-tab-pane name="js" tab="JavaScript">
<n-tab-pane name="js" tab="JS">
<template #tab>
<n-flex align="center">
<Icon icon="skill-icons:javascript" :width="20"></Icon>
<span>JavaScript</span>
<span>JS</span>
</n-flex>
</template>
<Editor language="js" :font-size="size" v-model:value="js" />
@@ -71,14 +71,13 @@
<script lang="ts" setup>
import { Icon } from "@iconify/vue"
import Editor from "./Editor.vue"
import { useStorage } from "@vueuse/core"
import { html, css, js, reset, size, changeSize } from "../store"
import { html, css, js, reset, tab, size } from "../store"
const currentTab = useStorage("web-tab", "html")
function changeTab(tab: "html" | "css" | "js" | "actions") {
if (tab === "actions") return
currentTab.value = tab
function changeTab(name: string) {
tab.value = name
}
function changeSize(num: number) {
size.value = num
}
</script>
<style scoped>

View File

@@ -46,9 +46,10 @@ onMounted(preview)
</script>
<style scoped>
.title {
height: 40px;
background-color: rgb(247, 247, 250);
height: 43px;
padding: 0 20px;
border-bottom: 1px solid rgb(239, 239, 245);
box-sizing: border-box;
}
.titleText {
font-size: 16px;

View File

@@ -7,24 +7,35 @@
<n-button text @click="prev" :disabled="step === '01'">
<Icon :width="24" icon="pepicons-pencil:arrow-left"></Icon>
</n-button>
<n-button text @click="next" :disabled="end">
<n-button text @click="next" :disabled="last">
<Icon :width="24" icon="pepicons-pencil:arrow-right"></Icon>
</n-button>
</n-flex>
</n-flex>
<div class="markdown-body" v-html="content"></div>
<div class="markdown-body" v-html="content" ref="$content"></div>
</div>
</template>
<script lang="ts" setup>
import { Icon } from "@iconify/vue"
import { step, prev, next } from "../store"
import { onMounted, ref, shallowRef, watch } from "vue"
import { tab, html, js, css } from "../store"
import { onMounted, ref, useTemplateRef, watch } from "vue"
import { marked } from "marked"
import { markedHighlight } from "marked-highlight"
import hljs from "highlight.js"
import { useStorage } from "@vueuse/core"
const end = ref(false)
const content = shallowRef("")
const step = useStorage("web-turtorial-step", "01")
const last = ref(false)
const content = ref("")
const $content = useTemplateRef("$content")
function prev() {
let num = parseInt(step.value) - 1
step.value = num.toString().padStart(2, "0")
}
function next() {
let num = parseInt(step.value) + 1
step.value = num.toString().padStart(2, "0")
}
async function getContent() {
const res = await fetch(`/turtorial/${step.value}/README.md`)
@@ -32,29 +43,49 @@ async function getContent() {
if (!!data) {
const html = await marked.parse(data, { async: true })
content.value = html
end.value = false
last.value = false
} else {
end.value = true
content.value = ""
last.value = true
}
}
onMounted(() => {
getContent()
marked.use({
gfm: true,
async: true,
// 用 js 来写的,可以换成 vue 的方式
function addCopyButton() {
const div = document.createElement("div")
div.className = "my-action-btn"
const pres = $content.value!.querySelectorAll("pre")
pres.forEach((pre) => {
let timer = 0
const copy = div.cloneNode(true) as HTMLDivElement
pre.appendChild(copy)
copy.onclick = () => {
// 功能
const outer = pre.childNodes[0] as HTMLPreElement
const match = outer.className.match(/-(.*)/)
let lang = "html"
if (match) lang = match[1].toLowerCase()
tab.value = lang
if (lang === "html") html.value = pre.textContent
if (lang === "css") css.value = pre.textContent
if (lang === "js") js.value = pre.textContent
// 样式
copy.classList.add("click")
clearTimeout(timer)
timer = setTimeout(() => {
copy.classList.remove("click")
}, 1000)
}
})
marked.use(
markedHighlight({
langPrefix: "hljs language-",
highlight(code, lang) {
const language = hljs.getLanguage(lang) ? lang : "plaintext"
return hljs.highlight(code, { language }).value
},
}),
)
})
watch(step, getContent)
}
async function render() {
await getContent()
addCopyButton()
}
onMounted(render)
watch(step, render)
</script>
<style scoped>
.container {
@@ -63,10 +94,11 @@ watch(step, getContent)
height: 100%;
}
.title {
height: 40px;
background-color: rgb(247, 247, 250);
height: 43px;
padding: 0 20px;
flex-shrink: 0;
border-bottom: 1px solid rgb(239, 239, 245);
box-sizing: border-box;
}
.preview {
font-size: 16px;
@@ -77,3 +109,22 @@ watch(step, getContent)
overflow: auto;
}
</style>
<style>
.markdown-body pre {
position: relative;
}
.my-action-btn {
position: absolute;
top: 0;
right: 0;
padding: 1rem;
cursor: pointer;
border-radius: 0.4rem;
}
.my-action-btn::before {
content: "替换";
}
.my-action-btn.click::before {
content: "已替换";
}
</style>

View File

@@ -1,10 +1,28 @@
import { createApp } from "vue"
import { create } from "naive-ui"
import { marked } from "marked"
import "normalize.css"
import "github-markdown-css/github-markdown-light.css"
import "highlight.js/styles/github.min.css"
import App from "./App.vue"
import { markedHighlight } from "marked-highlight"
import hljs from "highlight.js"
marked.use({
gfm: true,
async: true,
})
marked.use(
markedHighlight({
langPrefix: "hljs language-",
highlight(code, lang) {
const language = hljs.getLanguage(lang) ? lang : "plaintext"
return hljs.highlight(code, { language }).value
},
}),
)
const app = createApp(App)
const naive = create()
app.use(naive)

View File

@@ -3,12 +3,16 @@ import { useStorage } from "@vueuse/core"
const defaultHTML = `<div class="welcome">黄岩一职</div>`
const defaultCSS = `.welcome {
color: red;
font-size: 24px;
}`
export const html = useStorage("web-html", defaultHTML)
export const css = useStorage("web-css", defaultCSS)
export const js = useStorage("web-js", "")
export const tab = useStorage("web-tab", "html")
export const size = useStorage("web-fontsize", 24)
export function reset(lang: "html" | "css" | "js") {
if (lang === "html") {
html.value = defaultHTML
@@ -18,18 +22,3 @@ export function reset(lang: "html" | "css" | "js") {
js.value = ""
}
}
export const size = useStorage("web-fontsize", 24)
export function changeSize(num: number) {
size.value = num
}
export const step = useStorage("web-turtorial-step", "01")
export function prev() {
let num = parseInt(step.value) - 1
step.value = num.toString().padStart(2, "0")
}
export function next() {
let num = parseInt(step.value) + 1
step.value = num.toString().padStart(2, "0")
}