From c91ef5aac9364d5cbd33bd30dd5459e5eb98cbf0 Mon Sep 17 00:00:00 2001 From: seaHi Date: Thu, 10 Sep 2026 20:46:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=95=E5=85=A5=20CodeMirror=20?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=90=8E=E5=8F=B0=20Markdown=20=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/admin/MarkdownEditor.svelte | 419 ++++++++++++++++++ src/routes/admin/pages/[id]/+page.svelte | 37 +- src/routes/admin/pages/new/+page.svelte | 37 +- src/routes/admin/posts/[id]/+page.svelte | 339 +------------- src/routes/admin/posts/new/+page.svelte | 339 +------------- 5 files changed, 427 insertions(+), 744 deletions(-) create mode 100644 src/lib/components/admin/MarkdownEditor.svelte diff --git a/src/lib/components/admin/MarkdownEditor.svelte b/src/lib/components/admin/MarkdownEditor.svelte new file mode 100644 index 0000000..deb560f --- /dev/null +++ b/src/lib/components/admin/MarkdownEditor.svelte @@ -0,0 +1,419 @@ + + +
+
+ + + + +
+ + + + +
+
+ {#if showAdmonitionMenu} + + + + {/if} + + {#if showAdmonitionMenu} +
+ {#each admonitionTypes as item (item.type)} + + {/each} +
+ {/if} +
+ +
+ + + +
+
+ + diff --git a/src/routes/admin/pages/[id]/+page.svelte b/src/routes/admin/pages/[id]/+page.svelte index 949ace7..c2e0b31 100644 --- a/src/routes/admin/pages/[id]/+page.svelte +++ b/src/routes/admin/pages/[id]/+page.svelte @@ -3,6 +3,7 @@ import { page } from '$app/stores'; import type { PageData, ActionData } from './$types'; import MarkdownRenderer from '$lib/components/MarkdownRenderer.svelte'; + import MarkdownEditor from '$lib/components/admin/MarkdownEditor.svelte'; import TextInput from '$lib/components/admin/TextInput.svelte'; import Button from '$lib/components/admin/Button.svelte'; import Alert from '$lib/components/admin/Alert.svelte'; @@ -70,14 +71,7 @@
{#if mode === 'edit'} - + {:else}
@@ -220,33 +214,6 @@ letter-spacing: 0.5px; } - .form-group textarea { - width: 100%; - padding: 12px 14px; - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 0; - background: rgba(0, 0, 0, 0.015); - font-family: inherit; - font-size: 1.05rem; - color: var(--text-color); - box-sizing: border-box; - transition: - border-color 0.3s ease, - box-shadow 0.3s ease; - } - - .form-group textarea { - resize: vertical; - line-height: 1.8; - min-height: 120px; - } - - .form-group textarea:focus { - outline: none; - border-color: var(--accent-color, #419b38); - box-shadow: 0 0 0 3px rgba(65, 155, 56, 0.08); - } - .sidebar-box { background: transparent; border: none; diff --git a/src/routes/admin/pages/new/+page.svelte b/src/routes/admin/pages/new/+page.svelte index e8140d5..c66741d 100644 --- a/src/routes/admin/pages/new/+page.svelte +++ b/src/routes/admin/pages/new/+page.svelte @@ -3,6 +3,7 @@ import { page } from '$app/stores'; import type { ActionData } from './$types'; import MarkdownRenderer from '$lib/components/MarkdownRenderer.svelte'; + import MarkdownEditor from '$lib/components/admin/MarkdownEditor.svelte'; import TextInput from '$lib/components/admin/TextInput.svelte'; import Button from '$lib/components/admin/Button.svelte'; import Alert from '$lib/components/admin/Alert.svelte'; @@ -79,14 +80,7 @@
{#if mode === 'edit'} - + {:else}
@@ -235,33 +229,6 @@ letter-spacing: 0.5px; } - .form-group textarea { - width: 100%; - padding: 12px 14px; - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 0; - background: rgba(0, 0, 0, 0.015); - font-family: inherit; - font-size: 1.05rem; - color: var(--text-color); - box-sizing: border-box; - transition: - border-color 0.3s ease, - box-shadow 0.3s ease; - } - - .form-group textarea { - resize: vertical; - line-height: 1.8; - min-height: 120px; - } - - .form-group textarea:focus { - outline: none; - border-color: var(--accent-color, #419b38); - box-shadow: 0 0 0 3px rgba(65, 155, 56, 0.08); - } - .sidebar-box { background: transparent; border: none; diff --git a/src/routes/admin/posts/[id]/+page.svelte b/src/routes/admin/posts/[id]/+page.svelte index 6f6183c..9b49931 100644 --- a/src/routes/admin/posts/[id]/+page.svelte +++ b/src/routes/admin/posts/[id]/+page.svelte @@ -6,6 +6,7 @@ import type { PageData, ActionData } from './$types'; import { deserialize } from '$app/forms'; import MarkdownRenderer from '$lib/components/MarkdownRenderer.svelte'; + import MarkdownEditor from '$lib/components/admin/MarkdownEditor.svelte'; import FeaturedImagePicker from '$lib/components/admin/FeaturedImagePicker.svelte'; import SelectDropdown from '$lib/components/admin/SelectDropdown.svelte'; import { parseHugoMarkdown } from '$lib/hugoParser'; @@ -105,60 +106,6 @@ } } - // Markdown 工具栏功能 - function insertAtCursor(before: string, after: string = '', defaultText: string = '') { - const textarea = document.getElementById('content') as HTMLTextAreaElement; - if (!textarea) return; - - const start = textarea.selectionStart; - const end = textarea.selectionEnd; - const selectedText = textarea.value.substring(start, end); - - const insertion = selectedText ? before + selectedText + after : before + defaultText + after; - - content = textarea.value.substring(0, start) + insertion + textarea.value.substring(end); - - // 恢复焦点并选中文字 - setTimeout(() => { - textarea.focus(); - if (selectedText) { - textarea.setSelectionRange(start, start + insertion.length); - } else { - textarea.setSelectionRange( - start + before.length, - start + before.length + defaultText.length - ); - } - }, 0); - } - - function insertMore() { - insertAtCursor('\n\n', '', ''); - } - - let showAdmonitionMenu = $state(false); - const admonitionTypes = [ - { type: 'note', title: '注意', label: '注意 (Note)' }, - { type: 'abstract', title: '摘要', label: '摘要 (Abstract)' }, - { type: 'info', title: '信息', label: '信息 (Info)' }, - { type: 'tip', title: '技巧', label: '技巧 (Tip)' }, - { type: 'success', title: '成功', label: '成功 (Success)' }, - { type: 'question', title: '问题', label: '问题 (Question)' }, - { type: 'warning', title: '警告', label: '警告 (Warning)' }, - { type: 'failure', title: '失败', label: '失败 (Failure)' }, - { type: 'danger', title: '危险', label: '危险 (Danger)' }, - { type: 'bug', title: 'Bug', label: 'Bug' }, - { type: 'example', title: '示例', label: '示例 (Example)' }, - { type: 'quote', title: '引用', label: '引用 (Quote)' } - ]; - - function insertAdmonition(type: string, title: string) { - const openTag = `{{< admonition ${type} "${title}" >}}\n`; - const closeTag = `\n{{< /admonition >}}`; - insertAtCursor('\n' + openTag, closeTag, '在这里输入提示内容...'); - showAdmonitionMenu = false; - } - async function handleImportMarkdown(e: Event) { const target = e.target as HTMLInputElement; const file = target.files?.[0]; @@ -306,151 +253,6 @@
- {#if mode === 'edit'} -
- - - - -
- - - - -
-
- {#if showAdmonitionMenu} - - - - {/if} - - {#if showAdmonitionMenu} -
- {#each admonitionTypes as item (item.type)} - - {/each} -
- {/if} -
- -
- {/if}
- - - -
- - - - -
-
- {#if showAdmonitionMenu} - - - - {/if} - - {#if showAdmonitionMenu} -
- {#each admonitionTypes as item (item.type)} - - {/each} -
- {/if} -
- -
- {/if}