From 98d8099b5d5ea4ef16f354359f0ce96453a791db Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 16 Mar 2026 18:23:31 +0800 Subject: [PATCH] fix --- src/components/Preview.vue | 6 +- src/components/Tutorial.vue | 144 +++++++++++++++++------------------- src/pages/Submissions.vue | 54 +++++++------- 3 files changed, 98 insertions(+), 106 deletions(-) diff --git a/src/components/Preview.vue b/src/components/Preview.vue index ce9fe5a..2504383 100644 --- a/src/components/Preview.vue +++ b/src/components/Preview.vue @@ -5,15 +5,15 @@ 下载 全屏 清空 - 查看代码 + 代码 复制链接 - 查看代码 + 代码 diff --git a/src/components/Tutorial.vue b/src/components/Tutorial.vue index da66fa9..0f8ddee 100644 --- a/src/components/Tutorial.vue +++ b/src/components/Tutorial.vue @@ -11,6 +11,27 @@ import { step } from "../store/tutorial" import { taskId } from "../store/task" import { useRouter } from "vue-router" +marked.use({ + renderer: { + code({ text, lang }) { + const language = lang?.toLowerCase() ?? "html" + return `
+
+ ${language.toUpperCase()} +
+ + +
+
+
${text}
+
` + }, + link({ href, text }) { + return `${text}` + }, + }, +}) + const router = useRouter() const tutorialIds = ref([]) const content = ref("") @@ -28,12 +49,12 @@ const nextDisabled = () => { function prev() { const i = tutorialIds.value.indexOf(step.value) - step.value = tutorialIds.value[i - 1] + step.value = tutorialIds.value[i - 1] as number } function next() { const i = tutorialIds.value.indexOf(step.value) - step.value = tutorialIds.value[i + 1] + step.value = tutorialIds.value[i + 1] as number } defineExpose({ tutorialIds, prevDisabled, nextDisabled, prev, next }) @@ -44,91 +65,43 @@ async function prepare() { content.value = "暂无教程" } if (!tutorialIds.value.includes(step.value)) { - step.value = tutorialIds.value[0] + step.value = tutorialIds.value[0] as number } } -async function getContent() { +async function render() { const data = await Tutorial.get(step.value) taskId.value = data.task_ptr const merged = `# #${data.display} ${data.title}\n${data.content}` content.value = await marked.parse(merged, { async: true }) } -function addButton() { - const existing = $content.value?.querySelectorAll(".codeblock-action") ?? [] - for (const el of existing) el.remove() +function flash(btn: HTMLButtonElement, done: string, original: string) { + btn.textContent = done + setTimeout(() => { + btn.textContent = original + }, 1000) +} - const action = document.createElement("div") - action.className = "codeblock-action" - const pres = $content.value?.querySelectorAll("pre") ?? [] - for (const pre of pres) { - let timer = 0 - let copyTimer = 0 - const actions = action.cloneNode() as HTMLDivElement - pre.insertBefore(actions, pre.children[0]) - const $code = pre.childNodes[1] as HTMLPreElement - const match = $code.className.match(/-(.*)/) - let lang = "html" - if (match) lang = match[1].toLowerCase() +function setupCodeActions() { + $content.value?.addEventListener("click", (e: MouseEvent) => { + const btn = (e.target as HTMLElement).closest("[data-action]") + if (!btn) return + const wrapper = btn.closest("[data-lang]")! + const lang = wrapper.dataset.lang ?? "html" + const code = wrapper.querySelector("code")?.textContent ?? "" - 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 ?? "") - copyBtn.textContent = "已复制" - clearTimeout(copyTimer) - copyTimer = setTimeout(() => { - copyBtn.textContent = "复制" - }, 1000) - } - - replaceBtn.onclick = () => { + if (btn.dataset.action === "copy") { + copyFn(code) + flash(btn, "已复制", "复制") + } else if (btn.dataset.action === "replace") { 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 - replaceBtn.textContent = "已替换" - clearTimeout(timer) - timer = setTimeout(() => { - replaceBtn.textContent = "替换" - }, 1000) + if (lang === "html") html.value = code + if (lang === "css") css.value = code + if (lang === "js") js.value = code + flash(btn, "已替换", "替换") } - } -} - -function modifyLink() { - const links = $content.value?.querySelectorAll("a") ?? [] - for (const link of links) { - link.target = "_blank" - } -} - -async function render() { - await getContent() - addButton() - modifyLink() + }) } async function init() { @@ -136,7 +109,10 @@ async function init() { render() } -onMounted(init) +onMounted(() => { + setupCodeActions() + init() +}) watch(step, (v) => { router.push({ name: "home-tutorial", params: { display: v } }) render() @@ -160,8 +136,24 @@ watch(step, (v) => { font-family: Monaco; } -.codeblock-action { +.markdown-body .codeblock-wrapper { + padding: 1rem; + background-color: #f6f8fa; + border-radius: 6px; margin-bottom: 1rem; + overflow: auto; +} + +.markdown-body .codeblock-wrapper pre { + padding: 0; + background-color: transparent; + border-radius: 0; + margin-bottom: 0; + overflow: visible; +} + +.codeblock-action { + margin-bottom: 0.5rem; font-family: v-sans, system-ui, diff --git a/src/pages/Submissions.vue b/src/pages/Submissions.vue index 29483cc..83afa14 100644 --- a/src/pages/Submissions.vue +++ b/src/pages/Submissions.vue @@ -1,7 +1,7 @@