# clywind.rich-editor **Repository Path**: clywind/clywind.rich-editor ## Basic Information - **Project Name**: clywind.rich-editor - **Description**: No description available - **Primary Language**: Unknown - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-24 - **Last Updated**: 2026-09-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # @clywind/rich-editor Vue 3 富文本编辑器 npm 包。编辑能力基于 Halo / TipTap,附件查询、上传和存储策略通过宿主提供的 `RichEditorAttachmentProvider` 接入,不绑定具体后端。 ## 许可证 本包包含从 [Halo editor](https://github.com/halo-dev/halo/tree/main/ui/packages/editor) 移植和适配的源码,按 `GPL-3.0-only` 发布。发布包包含完整协议 `COPYING`、版权与来源声明 `LICENSE` / `NOTICE`、`src/` 源码及构建配置;分发本包或衍生版本时须一并提供对应源码。 ## 安装 ```bash npm install @clywind/rich-editor vue element-plus ``` `vue` 和 `element-plus` 是 peer dependencies。TipTap、Floating Vue、Iconify 生成代码和编辑器运行依赖由本包管理,宿主不需要安装它们。 发布 `0.4.0` 后,需要固定该版本的项目可安装 `@clywind/rich-editor@0.4.0`;已有项目不会因为插件仓库源码变化而自动更新 npm 依赖。 ## 仓库内开发 推荐开发环境: - Node.js `22.22.2`(LTS) - npm `10.9.x` 安装依赖前先确认版本: ```bash node -v npm -v ``` 使用 nvm 时,可以在仓库根目录执行: ```bash nvm install 22.22.2 nvm use 22.22.2 ``` 项目声明的可用范围为 Node.js `^22.22.2 || >=24.15.0`、npm `>=10.9.0 <12`。不在此范围内的环境可能在安装或构建富文本包时失败。 从本仓库拉取代码后,在仓库根目录安装依赖: ```bash npm ci npm test npm run typecheck:demo npm run build ``` `npm ci` 会执行 `prepare` 并生成 `dist`。构建顺序为先生成 JS/CSS,再生成类型声明,最终产物包含 `rich-editor.js`、`style.css`、`index.d.ts` 及其余声明文件。 修改源码后重新构建: ```bash npm run build ``` 也可以直接从 Gitee 安装开发版本: ```bash npm install git+https://gitee.com/clywind/clywind.rich-editor.git ``` ## 本地 Demo 在仓库根目录执行 `npm ci` 后运行: ```bash npm run dev ``` 本机打开终端输出的 Vite 地址。Demo 使用内存中的 Mock 附件分组、存储策略和图片列表,支持搜索、排序、网格/列表、详情、插入及本地图片上传;无需启动后端。Demo 绑定了 `v-model:title` 并提供“显示文章标题”开关,方便验证标题开关、纯文本标题及正文 HTML 的独立状态。开发服务监听 `0.0.0.0`,同一局域网的设备可通过 `http://<本机局域网IP>:/` 访问。 通过 Demo 顶部的“单编辑器 / 多编辑器”标签切换视图。单编辑器 Demo 的“工具栏按钮”展开区位于插件工具栏上方,不会遮挡工具栏;可勾选可选按钮、全选或仅保留基础按钮并实时查看效果。访问 `/multi-editor` 可查看两个上下排列的默认工具栏编辑器:一个有标题、一个无标题,各自拥有独立的正文滚动和附件数据。页面滚动越过编辑器顶部时,仅当前编辑器的工具栏固定于视口顶部;离开该编辑器后自动切换或收起。 从 `0.4.0` 起,npm 发布包包含预构建的 `demo-dist/`、Demo 源码和独立启动命令。项目安装该包后,无需 Vite 或插件开发依赖即可启动 Demo: ```bash npx clywind-rich-editor-demo ``` 默认在 `http://127.0.0.1:4173/` 访问单编辑器,`/multi-editor` 查看多编辑器。可用 `npx clywind-rich-editor-demo --port 4190` 指定端口;局域网访问可加 `--host 0.0.0.0`。npm 网站不会直接托管互动 Demo;Mock 图片来自在线图片服务,需要网络才能显示缩略图。只有修改 Demo 源码时才需要克隆仓库并运行 `npm run dev`。 ## 按需使用 ```vue ``` ## 全局安装 ```ts import { createApp } from "vue"; import ElementPlus from "element-plus"; import RichEditorPlugin from "@clywind/rich-editor"; import "element-plus/dist/index.css"; import "@clywind/rich-editor/style.css"; const app = createApp(App); app.use(ElementPlus); app.use(RichEditorPlugin, { attachmentProvider, attachmentSortOptions: [ { label: "最新上传", value: "created-desc" }, ], }); app.mount("#app"); ``` 全局注册后可以直接使用: ```vue ``` 组件实例上的 `attachmentProvider` 和 `attachmentSortOptions` 优先于全局配置。 ## 多编辑器实例 同一页面可以挂载多个编辑器,并为每个实例提供不同的 provider、分组和业务上下文: ```vue ``` 附件配置按 editor 实例隔离,不会覆盖或串用其他编辑器的 provider。 ## Attachment Provider ```ts type RichEditorAttachmentId = string | number; type RichEditorAttachmentContext = Record; interface RichEditorAttachmentGroup { id: RichEditorAttachmentId; name: string; } interface RichEditorStorageStrategy { id: RichEditorAttachmentId; name: string; } interface RichEditorAttachment { id: RichEditorAttachmentId; url: string; thumbnailUrl?: string; size: number; displayFileName?: string; originalFileName?: string; objectKey?: string; contentType?: string; groupId?: RichEditorAttachmentId | null; groupName?: string | null; metadata?: Record; } interface RichEditorAttachmentProvider { listGroups?: (input: RichEditorGroupQuery) => Promise; listStorageStrategies?: (input: RichEditorGroupQuery) => Promise; listImages: (input: RichEditorImageQuery) => Promise; uploadImage: (input: RichEditorUploadInput) => Promise; uploadVideo?: (input: RichEditorUploadInput) => Promise; uploadAudio?: (input: RichEditorUploadInput) => Promise; uploadAttachment?: (input: RichEditorUploadAttachmentInput) => Promise; } ``` ### 查询参数 `listImages(input)` 会收到: - `keyword`:文件名关键词。 - `pageIndex` / `pageSize`:分页参数,页码从 1 开始。 - `groupId` / `isUngrouped`:分组条件。 - `storageStrategyId`:存储策略 ID。 - `sort`:当前排序值。内置值包括创建时间、名称、大小正倒序;自定义排序值会原样传给 provider。 - `context`:宿主传入的业务上下文。 返回的 `total` 必须是全部命中数量,不是当前页数量。 ### 上传参数 上传 input 包含: - `file`:原始文件。 - `groupId`:上传目标分组。 - `context`:业务上下文。 - `signal`:用于取消请求的 `AbortSignal`。 - `onProgress(progress)`:上传进度回调,范围为 0 到 100。 上传结果必须包含可访问的 `url`。 ## 缩略图 附件选择器优先加载 `thumbnailUrl`,详情和插入编辑器始终使用原始 `url`。列表数据较多时,不应把数 MB 原图作为网格缩略图。 天翼云 ZOS 示例: ```ts function buildZosThumbnailUrl(url: string) { const parsed = new URL(url); parsed.searchParams.set( "x-zos-process", "image/resize,w_220,h_180", ); return parsed.toString(); } ``` 接口映射: ```ts return { ...attachment, thumbnailUrl: buildZosThumbnailUrl(attachment.url), }; ``` 非 ZOS 后端可以返回 CDN 缩略图、对象存储图片样式 URL 或自己的缩略图接口地址。未提供 `thumbnailUrl` 时组件会使用浏览器端低分辨率 canvas 作为回退。 ## 组件 API ### RichEditor - `modelValue: string`:HTML 内容,支持 `v-model`。 - `editorHeight?: string`:实例高度,例如 `"520px"`;设置后正文独立滚动,工具栏保持在实例顶部。 - `toolbarItems?: RichEditorToolbarItemId[]`:可选按钮的固定 ID 白名单。不传显示全部;传 `[]` 仅保留基础按钮。仅控制顶部显示,不禁用快捷键或正文编辑命令。 - `showTitle?: boolean`:是否显示固定在正文上方的标题输入框,默认 `true`。 - `title?: string`:文章标题,支持 `v-model:title`。 - `titlePlaceholder?: string`:标题占位提示,默认 `请输入标题`。 - `placeholder?: string`:默认 `输入 / 以选择输入类型`。 - `attachmentProvider?: RichEditorAttachmentProvider`:实例附件 provider。 - `attachmentContext?: Record`:业务上下文。 - `groupId?: string | number`:默认附件分组。 - `attachmentSortOptions?: RichEditorAttachmentSortOption[]`:排序选项。 - `update:modelValue`:内容更新事件。 - `update:title`:标题更新事件。 标题和正文相互独立:`v-model:title` 返回标题纯文本,`v-model` 返回正文 HTML。标题不会写入正文 HTML。 不需要标题时只绑定正文即可: ```vue ``` 六点图标是 Halo / TipTap 的原生块拖拽手柄。将内容块拖入正文并在蓝色落点线处释放,即可调整顺序;点击图标不会打开菜单。 多个编辑器纵向排在同一页面时,页面滚动会让当前编辑器的工具栏吸附到浏览器顶部,且同一时间最多悬浮一条。按每个实例实际可用空间设置 `editorHeight`(如 `"520px"`),长内容只在本实例的正文区域内滚动;不设置高度时由宿主布局决定编辑器的可用空间。 每条工具栏的全屏按钮只作用于所属编辑器:编辑器以页面内固定定位填满浏览器视口,保留浏览器标签栏,并不调用浏览器的 `requestFullscreen()`。再次点击按钮或按 `Esc` 可退出;同页其他编辑器不会进入该模式。 全屏时工具栏顶部会保留绿色状态线,右侧按钮显示“退出全屏”文字;无需记住快捷键也能识别当前状态并退出。 全屏时工具栏提示、下拉菜单、`/` 命令菜单与附件选择器也会显示在当前编辑器内。 图片及附件库中的“上传”会打开系统文件选择器;由于插件使用页面内的视口模式,选择或取消文件后编辑器仍保持铺满视口。需要隐藏浏览器标签栏时,可由用户按 F11 切换浏览器窗口级全屏,插件不控制 F11。 工具栏白名单按编辑器实例分别传入,不会影响同页其他编辑器: ```vue ``` 基础按钮始终显示,不能通过 `toolbarItems` 关闭:`insert`、`undo`、`redo`、`heading`、`bold`、`italic`、`fullscreen`。 公开可配置的 `RichEditorToolbarItemId` 只有:`clearFormat`、`formatBrush`、`fontSize`、`underline`、`strike`、`code`、`color`、`highlight`、`blockquote`、`bulletList`、`orderedList`、`taskList`、`codeBlock`、`align`、`lineHeight`、`find`、`sidebar`。可从包导入 `richEditorToolbarItemIds` 查看完整可选列表,或用 `richEditorCoreToolbarItemIds` 查看基础列表。子菜单随顶层按钮一起显示;移除 `sidebar` 时右侧栏也同步隐藏,其他正文功能不受影响。 ### AttachmentImageSelector - `modelValue: boolean`:弹窗显示状态。 - `multiple?: boolean`:是否多选。 - `max?: number`:最多选择数量。 - `groupId?: string | number`:默认分组。 - `context?: Record`:业务上下文。 - `attachmentProvider?: RichEditorAttachmentProvider`:附件 provider。 - `sortOptions?: RichEditorAttachmentSortOption[]`:排序选项。 - `confirm(images)`:确认选择事件。 ## 构建和测试 ```bash npm test npm run typecheck:demo npm run build npm pack --dry-run --registry=https://registry.npmjs.org/ ``` ## 发布检查 1. `npm run build` 成功,`dist` 包含 ESM、CSS 和类型声明。 2. `npm test` 全部通过。 3. `npm pack --dry-run` 包含 JS、CSS、类型声明、完整许可证、`src/`、Demo 源码、预构建的 `demo-dist/`、启动命令与构建配置;不含 `tests/` 或 `node_modules/`。 4. 使用生成的 tarball 在独立 Vue 项目中验证安装、类型检查和构建,并从该项目运行 `npx clywind-rich-editor-demo` 检查两个 Demo 页面。 5. 发布包包含完整的 `COPYING`、`LICENSE`、`NOTICE`、`src/` 和构建配置。 6. 确认版本为 `0.4.0`,再执行 `npm publish --access public --registry=https://registry.npmjs.org/`;npm 要求动态验证码时在自己的终端完成验证,不要将验证码写入仓库。 7. 发布后执行 `npm view @clywind/rich-editor@0.4.0 version --registry=https://registry.npmjs.org/` 确认版本可用。