From d386d736412a8eedd2dfdd7cf9229b3ab4bc37b4 Mon Sep 17 00:00:00 2001 From: sunlei Date: Wed, 1 Jul 2026 19:16:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AF=B9=E9=BD=90Markdown=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8=E4=B8=BB=E9=A2=98=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../components/markdown/KtMilkdownEditor.scss | 30 +++++++++++++++++-- .../markdown/KtMilkdownEditor.spec.ts | 21 +++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7ac7b6c..6c32aef 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ pnpm run build:antdv-next - QQBot / 账号连接页拆分 OneBot 连接、QQ 登录、NapCat 运行和运行说明列;更新登录通过 SSE 展示 quick / password / captcha / new-device / qrcode 每步中文进度,密码登录触发 QQ 安全验证时在弹窗内完成腾讯验证码并回交 API,新设备验证二维码和腾讯验证码分开展示;行操作“运行态”打开只读抽屉,展示 NapCat runtime/protocol/session behavior profile、风险模式和登录事件证据。 - QQBot / 插件平台页保留在线命令能力表,并提供 manifest 校验、本地插件安装、安装记录、运行事件和账号绑定抽屉,接口走 `/qqbot/plugin-platform/*`。 - 博客管理 / 文章管理提供“预览”行操作,打开隐藏二级路由 `/blog/article/:articleId/preview`;预览页按 NapCat WebUI 的微服务嵌入形态实现,iframe 独占容器,文章标题、状态、预览 Host 和返回/刷新/新窗口操作放在右下角悬浮卡片,不占用 iframe 布局空间。新增隐藏路由和按钮权限需要同步 API `blog-menu.sql` / `vben-admin-init.sql` 中的 `BlogArticlePreview` 与 `BlogArticlePreviewButton`。 -- 博客管理 / 文章表单支持 Markdown、富文本 HTML、源码 HTML 三种编辑模式:Markdown 继续使用 Milkdown/Crepe 并保存 `contentFormat=markdown`;富文本 HTML 使用 Tiptap 并保存 `contentFormat=html`;源码 HTML 用于保留 WordPress/Argon 运行时 DOM,同样保存 `contentFormat=html`。Milkdown/Crepe 必须先引入 `@milkdown/crepe/theme/common/style.css`,再引入具体主题 CSS,否则生产包只有主题变量没有工具栏/菜单布局样式。 +- 博客管理 / 文章表单支持 Markdown、富文本 HTML、源码 HTML 三种编辑模式:Markdown 继续使用 Milkdown/Crepe 并保存 `contentFormat=markdown`;富文本 HTML 使用 Tiptap 并保存 `contentFormat=html`;源码 HTML 用于保留 WordPress/Argon 运行时 DOM,同样保存 `contentFormat=html`。Milkdown/Crepe 必须先引入 `@milkdown/crepe/theme/common/style.css`,再引入具体主题 CSS,否则生产包只有主题变量没有工具栏/菜单布局样式;组件 SCSS 还必须把 `--crepe-color-*` 映射到 Admin `hsl(var(--...))` 主题 token,并覆盖 common 默认大 padding/root overflow,避免暗色模式脱节和首次空编辑器出现内部滚动条。 ## 部署说明 diff --git a/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.scss b/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.scss index b67859f..07e6f63 100644 --- a/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.scss +++ b/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.scss @@ -6,6 +6,7 @@ border: 1px solid hsl(var(--border)); border-radius: 8px; background: hsl(var(--background)); + color: hsl(var(--foreground)); &--disabled { opacity: 0.72; @@ -19,7 +20,7 @@ min-height: inherit; width: 100%; flex: 1; - overflow: auto; + overflow: hidden; } &__placeholder { @@ -32,17 +33,42 @@ } .milkdown { + --crepe-color-background: hsl(var(--background)); + --crepe-color-on-background: hsl(var(--foreground)); + --crepe-color-surface: hsl(var(--card)); + --crepe-color-surface-low: hsl(var(--muted)); + --crepe-color-on-surface: hsl(var(--foreground)); + --crepe-color-on-surface-variant: hsl(var(--muted-foreground)); + --crepe-color-outline: hsl(var(--border)); + --crepe-color-primary: hsl(var(--primary)); + --crepe-color-secondary: hsl(var(--accent)); + --crepe-color-on-secondary: hsl(var(--accent-foreground)); + --crepe-color-inverse: hsl(var(--background-deep)); + --crepe-color-on-inverse: hsl(var(--foreground)); + --crepe-color-inline-code: hsl(var(--primary)); + --crepe-color-error: hsl(var(--destructive)); + --crepe-color-hover: hsl(var(--accent)); + --crepe-color-selected: hsl(var(--primary) / 14%); + --crepe-color-inline-area: hsl(var(--muted)); + min-height: inherit; + color: hsl(var(--foreground)); + background: hsl(var(--background)); } .milkdown .editor { min-height: inherit; - padding: 18px 22px; + padding: 0; outline: none; } .milkdown .ProseMirror { min-height: calc(var(--kt-milkdown-min-height, 360px) - 36px); + padding: 18px 22px; outline: none; } + + .milkdown .milkdown-top-bar { + border-bottom: 1px solid hsl(var(--border)); + } } diff --git a/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.spec.ts b/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.spec.ts index de5cce2..563b729 100644 --- a/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.spec.ts +++ b/apps/web-antdv-next/src/components/markdown/KtMilkdownEditor.spec.ts @@ -6,6 +6,10 @@ import { describe, expect, it } from 'vitest'; const dirname = path.dirname(fileURLToPath(import.meta.url)); const source = readFileSync(path.join(dirname, 'KtMilkdownEditor.tsx'), 'utf8'); +const styleSource = readFileSync( + path.join(dirname, 'KtMilkdownEditor.scss'), + 'utf8', +); describe('ktMilkdownEditor styles', () => { it('loads Crepe base layout styles before the selected theme variables', () => { @@ -19,4 +23,21 @@ describe('ktMilkdownEditor styles', () => { expect(commonStyleIndex).toBeGreaterThanOrEqual(0); expect(frameThemeIndex).toBeGreaterThan(commonStyleIndex); }); + + it('maps Crepe theme variables to Admin design tokens', () => { + expect(styleSource).toContain( + '--crepe-color-background: hsl(var(--background));', + ); + expect(styleSource).toContain('--crepe-color-surface: hsl(var(--card));'); + expect(styleSource).toContain( + '--crepe-color-primary: hsl(var(--primary));', + ); + }); + + it('keeps the first empty editor render from creating an internal scrollbar', () => { + expect(styleSource).toMatch(/&__root\s*\{[\s\S]*?overflow:\s*hidden;/); + expect(styleSource).toMatch( + /\.milkdown \.ProseMirror\s*\{[\s\S]*?padding:\s*18px 22px;/, + ); + }); });