diff --git a/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.spec.tsx b/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.spec.tsx index 3018cb8..fd8a89c 100644 --- a/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.spec.tsx +++ b/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.spec.tsx @@ -31,8 +31,11 @@ class FakeEditor { public commands = { focus: () => true, - setContent: (value: string) => { + setContent: (value: string, options?: { emitUpdate?: boolean }) => { this.html = value; + if (options?.emitUpdate !== false) { + this.options.onUpdate?.({ editor: this }); + } return true; }, }; @@ -316,6 +319,8 @@ describe('ktTiptapHtmlEditor', () => { ); await nextTick(); + expect(wrapper.emitted('update:modelValue')).toHaveLength(1); + expect(wrapper.emitted('change')).toHaveLength(1); expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([ '
新正文
', ]); diff --git a/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.tsx b/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.tsx index 877e969..e5991d9 100644 --- a/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.tsx +++ b/apps/web-antdv-next/src/components/richText/KtTiptapHtmlEditor.tsx @@ -540,7 +540,7 @@ export default defineComponent({ return; } - currentEditor.commands.setContent(value); + currentEditor.commands.setContent(value, { emitUpdate: false }); emitHtmlChange(readEditorHtml(currentEditor, value), previousHtml); } @@ -557,7 +557,7 @@ export default defineComponent({ if (nextHtml === currentHtml.value) return; currentHtml.value = nextHtml; - editor.value?.commands.setContent(nextHtml); + editor.value?.commands.setContent(nextHtml, { emitUpdate: false }); }, ); diff --git a/apps/web-antdv-next/src/views/blog/modules/article-form.spec.ts b/apps/web-antdv-next/src/views/blog/modules/article-form.spec.ts index 4601a25..fe37d39 100644 --- a/apps/web-antdv-next/src/views/blog/modules/article-form.spec.ts +++ b/apps/web-antdv-next/src/views/blog/modules/article-form.spec.ts @@ -19,7 +19,7 @@ describe('blog article form helpers', () => { const values = getBlogArticleEditFormValues({ categories: ['技术'], contentHtml: - 'select 1;',
+ 'select 1;
',
contentMarkdown: '```sql\nselect 1;\n```',
excerpt: '摘要',
id: '50',
@@ -34,6 +34,43 @@ describe('blog article form helpers', () => {
expect(values.content).toContain('hljs-codeblock');
});
+ it('keeps existing markdown articles in markdown mode when rendered html is present', () => {
+ const values = getBlogArticleEditFormValues({
+ categories: [],
+ contentHtml: '正文
', + contentMarkdown: '# 标题\n\n正文', + excerpt: '', + id: '52', + slug: 'markdown-post', + status: 'publish', + tags: [], + title: 'Markdown 文章', + }); + + expect(values.editorMode).toBe('markdown'); + expect(values.contentFormat).toBe('markdown'); + expect(values.content).toBe('# 标题\n\n正文'); + }); + + it('keeps markdown fenced code articles in markdown mode after Argon base rendering', () => { + const values = getBlogArticleEditFormValues({ + categories: [], + contentHtml: + 'const a = 1;',
+ contentMarkdown: '```ts\nconst a = 1;\n```',
+ excerpt: '',
+ id: '53',
+ slug: 'markdown-code-post',
+ status: 'publish',
+ tags: [],
+ title: 'Markdown 代码文章',
+ });
+
+ expect(values.editorMode).toBe('markdown');
+ expect(values.contentFormat).toBe('markdown');
+ expect(values.content).toBe('```ts\nconst a = 1;\n```');
+ });
+
it('edits plain HTML articles with the rich HTML editor by default', () => {
const values = getBlogArticleEditFormValues({
categories: [],
diff --git a/apps/web-antdv-next/src/views/blog/modules/article-form.ts b/apps/web-antdv-next/src/views/blog/modules/article-form.ts
index d98614d..dad373c 100644
--- a/apps/web-antdv-next/src/views/blog/modules/article-form.ts
+++ b/apps/web-antdv-next/src/views/blog/modules/article-form.ts
@@ -16,8 +16,9 @@ export const BLOG_ARTICLE_HTML_TEXTAREA_CLASS =
'blog-article-form__html-source';
export const BLOG_ARTICLE_MARKDOWN_MIN_HEIGHT = 560;
-const ARGON_HTML_PATTERN =
- /\b(?:wp-block-|hljs-codeblock|hljs-ln|hljs-control|fancybox-wrapper|lazyload|collapse-block)/;
+const ARGON_BASE_CODEBLOCK_PATTERN = /\b(?:wp-block-code|hljs-codeblock)\b/;
+const SOURCE_ONLY_HTML_PATTERN =
+ /\b(?:hljs-ln|hljs-control|fancybox-wrapper|lazyload|collapse-block|wp-block-(?!code\b)[\w-]+)/;
const HTML_TAG_PATTERN = /<\/?[a-z][\s\S]*>/i;
/**
@@ -118,16 +119,18 @@ export function getContentFormatForEditorMode(
}
/**
- * Chooses the editor mode for an existing article without forcing imported Argon HTML through Tiptap or Markdown.
+ * Chooses the editor mode for an existing article without forcing Markdown source or imported Argon HTML into the wrong editor.
* @param article Article row returned by the local blog API.
- * @returns Source HTML for Argon runtime snapshots, rich HTML for plain tags, otherwise Markdown.
+ * @returns Markdown for source-backed articles, source HTML for strong runtime DOM, rich HTML for plain tags.
*/
export function getBlogArticleEditorMode(
article?: Partial