feat(web): 富文本编辑器换成 Vditor,不再自研轮子
CI / backend (push) Canceled after 0s

上一版我自己写了 textarea + 正则工具栏,看着能用,一到粘贴图片、
中文输入法、撤销栈就露馅——这几样正是成熟组件积累的部分。按要求改成品。

- 编辑器开关 def: RichTextEditor = Vditor 4.0.1(MIT,ir 模式)
  * 运行时资源自托管:web/scripts/vendor-vditor.mjs 把 lute/i18n/icons
    同步到 public/vditor,predev/prebuild 自动执行,不走 unpkg CDN
    (内网会一直卡在加载)
- 只读渲染换 markdown-it + dompurify,删掉自写的正则渲染器
- 附件直链的令牌只在内存里存在:落库前 stripAttachmentTokens,
  渲染/装载前补回(<img> 带不了 Authorization 头,raw?token= 是必要折中)
- Vditor 不会替我们插入 upload.handler 的返回值(只当提示语 tip.show),
  所以上传完自己 insertMD

验证:pytest 70 passed;e2e richtext 13 项、smoke 7、tickets 8、
knowledge 7 全绿;tsc 0 错;主包 299 kB/gzip 75 kB,Vditor 独立
chunk 385 kB 按需加载。
This commit is contained in:
dadao
2026-10-29 02:30:10 +08:00
parent 7a7ea932a4
commit fbdf54cb4c
18 changed files with 630 additions and 419 deletions
+147 -1
View File
@@ -8,15 +8,19 @@
"name": "dadaotask-web",
"version": "0.1.0",
"dependencies": {
"dompurify": "^3.4.17",
"markdown-it": "^15.0.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.28.0"
"react-router-dom": "^6.28.0",
"vditor": "^4.0.1"
},
"devDependencies": {
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"playwright-core": "^1.51.1",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.2",
@@ -1353,6 +1357,13 @@
"@types/react": "^18.0.0"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
"optional": true
},
"node_modules/@vitejs/plugin-react": {
"version": "4.7.0",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
@@ -1402,6 +1413,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
"version": "3.0.4",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/argparse/-/argparse-3.0.4.tgz",
"integrity": "sha512-fwWK3Zq22l4XJJjx5mcBHGikIk+hJqO+32dPm0Jur7+rtG5x1scOjSfdT9ZKeWHPpsya/l0HHK4tDImBkTrtvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "PSF-2.0"
},
"node_modules/autoprefixer": {
"version": "10.6.2",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/autoprefixer/-/autoprefixer-10.6.2.tgz",
@@ -1643,6 +1670,12 @@
"dev": true,
"license": "Apache-2.0"
},
"node_modules/diff-match-patch": {
"version": "1.0.5",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==",
"license": "Apache-2.0"
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/dlv/-/dlv-1.1.3.tgz",
@@ -1650,6 +1683,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/dompurify": {
"version": "3.4.17",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/dompurify/-/dompurify-3.4.17.tgz",
"integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.466",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.5.466.tgz",
@@ -1657,6 +1699,18 @@
"dev": true,
"license": "ISC"
},
"node_modules/entities": {
"version": "8.1.0",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/entities/-/entities-8.1.0.tgz",
"integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/es-errors/-/es-errors-1.3.0.tgz",
@@ -1971,6 +2025,25 @@
"dev": true,
"license": "MIT"
},
"node_modules/linkify-it": {
"version": "6.1.0",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/linkify-it/-/linkify-it-6.1.0.tgz",
"integrity": "sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/markdown-it"
}
],
"license": "MIT",
"dependencies": {
"uc.micro": "^3.0.0"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -1993,6 +2066,39 @@
"yallist": "^3.0.2"
}
},
"node_modules/markdown-it": {
"version": "15.0.3",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/markdown-it/-/markdown-it-15.0.3.tgz",
"integrity": "sha512-+Az7Ojz3iRGu8KYMvJ6xVhvrxP3eE+vEtJX75Ow88XBpcQNV/0lGX5ZynIIBftREo4DC2jsveAMDaeltmm8QvQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/markdown-it"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^3.0.3",
"entities": "^8.1.0",
"linkify-it": "^6.1.0",
"mdurl": "^2.1.0",
"punycode.js": "^2.3.1",
"uc.micro": "^3.0.0"
},
"bin": {
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/mdurl": {
"version": "2.1.0",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/mdurl/-/mdurl-2.1.0.tgz",
"integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==",
"license": "MIT"
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/merge2/-/merge2-1.4.1.tgz",
@@ -2132,6 +2238,19 @@
"node": ">= 6"
}
},
"node_modules/playwright-core": {
"version": "1.51.1",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/playwright-core/-/playwright-core-1.51.1.tgz",
"integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/postcss": {
"version": "8.5.32",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/postcss/-/postcss-8.5.32.tgz",
@@ -2295,6 +2414,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -2714,6 +2842,12 @@
"node": ">=14.17"
}
},
"node_modules/uc.micro": {
"version": "3.0.0",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/uc.micro/-/uc.micro-3.0.0.tgz",
"integrity": "sha512-U3PppEkleoTnIfi8BozMx3yju3qc/L6SwqWo2Sw+54PX+PX0q9I+r1Um5HCmqD7n9VDX5/v3vQH/AjA6deDdtw==",
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.3.4",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz",
@@ -2752,6 +2886,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/vditor": {
"version": "4.0.1",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/vditor/-/vditor-4.0.1.tgz",
"integrity": "sha512-CyajvPNE8fjCgEAzAaSxVEEWwQMJ5FTIVkNn3k+5IRaldqGwPRr8Dx2QLALHGCby8L/AJ6pQXjXiSPUGhHGmDw==",
"license": "MIT",
"dependencies": {
"diff-match-patch": "^1.0.5"
},
"funding": {
"url": "https://ld246.com/sponsor"
}
},
"node_modules/vite": {
"version": "6.4.5",
"resolved": "http://mirrors.huaweicloud.com/repository/npm/vite/-/vite-6.4.5.tgz",