添加教程
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
```js
|
||||
console.log("122")
|
||||
```
|
||||
|
||||
## 什么是 HTML
|
||||
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
# 什么是 CSS
|
||||
# 什么是 CSS
|
||||
|
||||
```css
|
||||
.greeting {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
18
src/main.ts
18
src/main.ts
@@ -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)
|
||||
|
||||
19
src/store.ts
19
src/store.ts
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user