UI
This commit is contained in:
@@ -60,6 +60,7 @@ import { taskId, taskTab } from "../store/task"
|
||||
import { useRoute, useRouter } from "vue-router"
|
||||
import { TASK_TYPE } from "../utils/const"
|
||||
import Challenge from "./Challenge.vue"
|
||||
import { NButton } from 'naive-ui'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -124,7 +125,6 @@ async function getContent() {
|
||||
content.value = await marked.parse(merged, { async: true })
|
||||
}
|
||||
|
||||
// 用 js 来写的,可以换成 vue 的方式
|
||||
function addButton() {
|
||||
const action = document.createElement("div")
|
||||
action.className = "codeblock-action"
|
||||
@@ -138,29 +138,48 @@ function addButton() {
|
||||
const match = $code.className.match(/-(.*)/)
|
||||
let lang = "html"
|
||||
if (match) lang = match[1].toLowerCase()
|
||||
actions.innerHTML = `<span class="lang">${lang.toUpperCase()}</span><div><div class="btn copy">复制</div><div class="btn">替换</div></div>`
|
||||
const $copy = actions.children[1].children[0] as HTMLDivElement
|
||||
const $replace = actions.children[1].children[1] as HTMLDivElement
|
||||
$copy.onclick = () => {
|
||||
|
||||
const langSpan = document.createElement('span')
|
||||
langSpan.className = 'lang'
|
||||
langSpan.textContent = lang.toUpperCase()
|
||||
|
||||
const btnGroup = document.createElement('div')
|
||||
btnGroup.className = 'btn-group'
|
||||
|
||||
const copyBtn = document.createElement('button')
|
||||
copyBtn.className = 'action-btn'
|
||||
copyBtn.textContent = '复制'
|
||||
|
||||
const replaceBtn = document.createElement('button')
|
||||
replaceBtn.className = 'action-btn'
|
||||
replaceBtn.textContent = '替换'
|
||||
|
||||
btnGroup.appendChild(copyBtn)
|
||||
btnGroup.appendChild(replaceBtn)
|
||||
|
||||
actions.appendChild(langSpan)
|
||||
actions.appendChild(btnGroup)
|
||||
|
||||
copyBtn.onclick = () => {
|
||||
const content = pre.children[1].textContent
|
||||
copyFn(content ?? "")
|
||||
$copy.innerHTML = "已复制"
|
||||
copyBtn.textContent = "已复制"
|
||||
clearTimeout(copyTimer)
|
||||
copyTimer = setTimeout(() => {
|
||||
$copy.innerHTML = "复制"
|
||||
copyBtn.textContent = "复制"
|
||||
}, 1000)
|
||||
}
|
||||
$replace.onclick = () => {
|
||||
|
||||
replaceBtn.onclick = () => {
|
||||
tab.value = lang
|
||||
const content = pre.children[1].textContent
|
||||
if (lang === "html") html.value = content
|
||||
if (lang === "css") css.value = content
|
||||
if (lang === "js") js.value = content
|
||||
// 样式
|
||||
$replace.innerHTML = "已替换"
|
||||
replaceBtn.textContent = "已替换"
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(() => {
|
||||
$replace.innerHTML = "替换"
|
||||
replaceBtn.textContent = "替换"
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
@@ -243,23 +262,35 @@ watch(step, (v) => {
|
||||
"Apple Color Emoji",
|
||||
"Segoe UI Emoji",
|
||||
"Segoe UI Symbol";
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.codeblock-action .lang {
|
||||
font-size: 1.2rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.codeblock-action .btn {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 0;
|
||||
padding: 1rem;
|
||||
.codeblock-action .btn-group {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.codeblock-action .action-btn {
|
||||
height: 28px;
|
||||
padding: 0 14px;
|
||||
font-size: 14px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d9d9d9;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
border-radius: 0.4rem;
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.codeblock-action .btn.copy {
|
||||
right: 60px;
|
||||
.codeblock-action .action-btn:hover {
|
||||
border-color: #18a058;
|
||||
color: #18a058;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -38,49 +38,41 @@
|
||||
</n-flex>
|
||||
</n-gi>
|
||||
|
||||
<n-gi :span="3" class="col">
|
||||
<n-form>
|
||||
<n-grid x-gap="10" :cols="4">
|
||||
<n-gi :span="1">
|
||||
<n-form-item label="序号">
|
||||
<n-input-number v-model:value="tutorial.display" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi :span="2">
|
||||
<n-form-item label="标题">
|
||||
<n-input v-model:value="tutorial.title" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
<n-gi :span="1">
|
||||
<n-form-item label="公开">
|
||||
<n-switch v-model:value="tutorial.is_public" />
|
||||
</n-form-item>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-form-item label="内容">
|
||||
<n-input
|
||||
v-model:value="tutorial.content"
|
||||
type="textarea"
|
||||
class="editor"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-button block type="primary" @click="submit" :disabled="!canSubmit">
|
||||
提交
|
||||
</n-button>
|
||||
</n-form>
|
||||
</n-gi>
|
||||
<n-gi :span="6" class="col">
|
||||
<n-flex vertical>
|
||||
<n-form inline>
|
||||
<n-form-item label="序号" label-placement="left">
|
||||
<n-input-number v-model:value="tutorial.display" />
|
||||
</n-form-item>
|
||||
|
||||
<n-gi :span="3" class="markdwon-body" v-html="content"></n-gi>
|
||||
<n-form-item label="标题" label-placement="left">
|
||||
<n-input v-model:value="tutorial.title" />
|
||||
</n-form-item>
|
||||
|
||||
<n-form-item label="公开" label-placement="left">
|
||||
<n-switch v-model:value="tutorial.is_public" />
|
||||
</n-form-item>
|
||||
<n-form-item label-placement="left">
|
||||
<n-button type="primary" @click="submit" :disabled="!canSubmit">
|
||||
提交
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<MdEditor style="height: calc(100vh - 90px)" v-model="tutorial.content" />
|
||||
</n-flex>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { marked } from "marked"
|
||||
import { computed, onMounted, reactive, ref, watch } from "vue"
|
||||
import { computed, onMounted, reactive, ref } from "vue"
|
||||
import { useRoute, useRouter } from "vue-router"
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { Tutorial } from "../api"
|
||||
import type { TutorialSlim } from "../utils/type"
|
||||
import { useDialog, useMessage } from "naive-ui"
|
||||
import { MdEditor } from "md-editor-v3"
|
||||
//@ts-ignore
|
||||
import "md-editor-v3/lib/style.css"
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -148,7 +140,6 @@ async function show(display: number) {
|
||||
tutorial.title = item.title
|
||||
tutorial.content = item.content
|
||||
tutorial.is_public = item.is_public
|
||||
content.value = await marked.parse(item.content, { async: true })
|
||||
}
|
||||
|
||||
async function togglePublic(display: number) {
|
||||
@@ -160,13 +151,6 @@ async function togglePublic(display: number) {
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => tutorial.content,
|
||||
async (v: string) => {
|
||||
content.value = await marked.parse(v, { async: true })
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(getContent)
|
||||
</script>
|
||||
<style scoped>
|
||||
@@ -186,10 +170,4 @@ onMounted(getContent)
|
||||
.editor {
|
||||
height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
.markdwon-body {
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user