@itshixun/qckeditor-vue3
v1.2.4
Published
Vue3 components for CKEditor 5
Readme
@itshixun/qckeditor-vue3
Vue 3 组件库,用于 CKEditor 5 富文本编辑器。
本次更新为文档补充:将
v1.1.1~v1.1.3期间已发布的能力完整收录进 README,便于查阅。已收录的近期能力:
QCKEditor组件,支持classic/inline/document三种编辑器类型(v1.1.3)locale属性,透传CKEditor/QCKEditor/QCKClassic/QCKEditorPro,按语言动态加载 CKEditor 5 官方语言包,支持运行时切换dataNormalizer属性,透传CKEditor/QCKEditor/QCKClassic/QCKEditorPro,用于格式化初始值与外部modelValue变更(v1.1.1 / v1.1.2)CKEditor与QCKContent的fontSize属性(px),通过 CSS 变量--ck-content-font-size应用QCKEditor默认合并「段落 + 标题 1~5」的标题配置与「标记(marker)」高亮配置,与历史版本行为保持一致QCKEditorPro的disableExit属性与exit-blocked事件,用于在编辑器内部异步操作(PendingActions)期间控制是否退出编辑模式
安装
npm install @itshixun/qckeditor-vue3
# 或
pnpm add @itshixun/qckeditor-vue3
# 或
yarn add @itshixun/qckeditor-vue3对等依赖
本库需要以下对等依赖:
npm install vue@^3.0.0 ckeditor5@>=42.0.0使用
引入样式
在使用组件前,需要引入本库的样式文件:
import '@itshixun/qckeditor-vue3/dist/index.css';本样式文件已内置 CKEditor 5 官方样式,无需再单独引入
ckeditor5/ckeditor5.css。
基础编辑器(CKEditor)
底层 CKEditor 封装组件,支持任意 CKEditor 构建:
<template>
<CKEditor v-model="content" :editor="ClassicEditor" :config="editorConfig" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ClassicEditor } from 'ckeditor5';
import { CKEditor } from '@itshixun/qckeditor-vue3';
import '@itshixun/qckeditor-vue3/dist/index.css';
const content = ref('');
const editorConfig = {
toolbar: ['bold', 'italic', 'link', 'bulletedList', 'numberedList'],
};
</script>通用编辑器(QCKEditor)
支持三种编辑器类型,由 type prop 切换:
classic(默认):经典编辑器,工具栏固定在顶部inline:行内编辑器,工具栏在选中内容时浮动显示document:文档编辑器(基于DecoupledEditor),工具栏与编辑区分离挂载,高度自适应容器
QCKEditor 在用户传入 config 之上,预设了与历史版本一致的默认配置:
- 标题:在官方「段落 + 标题 1~3」基础上扩展支持「标题 4 / 标题 5」(
heading4/heading5),并通过src/styles/index.css统一定义了 h1~h5 字号样式 - 高亮(Highlight):仅提供「标记(marker)」一项,配套样式
.marker-default
用户 config 中显式提供的同名字段会覆盖默认配置(深度合并)。
<template>
<!-- 经典编辑器(默认) -->
<QCKEditor v-model="content" :config="editorConfig" />
<!-- 行内编辑器 -->
<QCKEditor v-model="content" type="inline" :config="editorConfig" />
<!-- 文档编辑器(工具栏与编辑区独立容器) -->
<QCKEditor v-model="content" type="document" :config="editorConfig" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { QCKEditor } from '@itshixun/qckeditor-vue3';
import '@itshixun/qckeditor-vue3/dist/index.css';
const content = ref('');
const editorConfig = {
toolbar: ['bold', 'italic', 'link'],
};
</script>经典编辑器(QCKClassic)
预配置的经典编辑器组件:
<template>
<QCKClassic v-model="content" :config="editorConfig" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { QCKClassic } from '@itshixun/qckeditor-vue3';
import '@itshixun/qckeditor-vue3/dist/index.css';
const content = ref('');
const editorConfig = {
toolbar: ['bold', 'italic', 'link'],
};
</script>专业版编辑器(QCKEditorPro)
点击激活的编辑器,提升页面性能:
<template>
<QCKEditorPro
v-model="content"
:config="editorConfig"
placeholder="点击编辑内容"
:min-height="100"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { QCKEditorPro } from '@itshixun/qckeditor-vue3';
import '@itshixun/qckeditor-vue3/dist/index.css';
const content = ref('');
const editorConfig = {
toolbar: ['bold', 'italic'],
};
</script>内容展示(QCKContent)
安全渲染编辑器内容:
<template>
<QCKContent :content="htmlContent" />
</template>
<script setup lang="ts">
import { QCKContent } from '@itshixun/qckeditor-vue3';
import '@itshixun/qckeditor-vue3/dist/index.css';
const htmlContent = '<p>Hello <strong>World</strong></p>';
</script>图片预览(imagePreview)
默认关闭。开启后,点击内容中的图片会打开内置轻量预览弹层(QCKImageViewer),支持关闭(遮罩/×/Esc)、左右切换(按钮/方向键)、计数、缩放(按钮/滚轮)、拖拽平移、双击复位、全屏与底部缩略图栏:
<template>
<!-- 布尔用法:全部默认配置 -->
<QCKContent :content="htmlContent" :image-preview="true" />
<!-- 对象用法:覆盖配置 -->
<QCKContent :content="htmlContent" :image-preview="{ zIndex: 3000, navbar: false }" />
</template>imagePreview 对象形式支持的选项(QCKImagePreviewOptions):
| 选项 | 类型 | 默认值 | 说明 | |------|------|--------|------| | zIndex | Number | 2100 | 预览弹层 z-index | | loop | Boolean | true | 切换到底/顶后是否循环 | | navbar | Boolean | true | 底部缩略图栏(图片数 > 1 时显示) | | fullscreen | Boolean | true | 是否显示全屏按钮 |
QCKEditorPro 同样支持 imagePreview,会透传给预览态的内容区(预览态点击图片只打开预览,不会激活编辑器)。
图片加载失败重试(imageRetry)
默认开启。适用于新生成的富文本内容中,图片资源正在服务端上传、尚未存储到对象存储的场景:图片加载失败后按固定间隔自动重试,直到成功或达到最大次数。重试通过重设相同 src 触发重新请求,不追加额外参数,不会破坏签名 URL:
<template>
<!-- 布尔用法:全部默认配置(可省略) -->
<QCKContent :content="htmlContent" :image-retry="true" />
<!-- 对象用法:覆盖配置 -->
<QCKContent :content="htmlContent" :image-retry="{ maxRetries: 20, interval: 1000 }" />
<!-- 关闭重试 -->
<QCKContent :content="htmlContent" :image-retry="false" />
</template>imageRetry 对象形式支持的选项(QCKImageRetryOptions):
| 选项 | 类型 | 默认值 | 说明 | |------|------|--------|------| | maxRetries | Number | 10 | 单张图片最大重试次数 | | interval | Number | 500 | 重试间隔(毫秒) |
QCKEditorPro / QCKEditor / QCKClassic 同样支持 imageRetry:预览态(QCKContent)与编辑态(编辑区 DOM)均生效。
界面语言(locale)
所有编辑器组件(CKEditor / QCKEditor / QCKClassic / QCKEditorPro)支持 locale 属性,默认 'zh-cn'。组件会按 locale 动态加载对应的 CKEditor 5 官方语言包(构建为独立的懒加载 chunk,不增加首屏体积),并写入编辑器配置的 language 与 translations 字段:
<template>
<!-- 简体中文(默认) -->
<QCKEditor v-model="content" locale="zh-cn" />
<!-- 英文 / 日文 / 繁体中文 -->
<QCKEditor v-model="content" locale="en" />
<QCKEditorPro v-model="content" locale="ja" />
<QCKClassic v-model="content" locale="zh" />
</template>- 写法兼容:
'zhCN'/'zh-CN'/'zh_CN'/'ZH-CN'等写法均可,内部统一规范化为 CKEditor 5 语言包格式(小写 + 连字符) - 支持语言:覆盖 CKEditor 5 官方全部语言包(
zh-cn、zh、en、en-gb、ja、ko、de、fr、pt-br等 70+);传入不支持的语言时会输出警告并回退为默认语言 - 动态变更:运行时修改
locale会销毁并以当前内容重建编辑器实例(CKEditor 5 不支持运行时原地切换语言);重建对外只触发一次新的ready事件,不会触发destroy - 优先级:
config中显式传入的language/translations优先于locale的派生值
<script setup lang="ts">
import { ref } from 'vue';
const locale = ref('zh-cn');
// 运行时切换:编辑器保留内容重建,界面语言变为英文
locale.value = 'en';
</script>上传适配器
本库提供了三种上传适配器,用于处理编辑器中的文件上传(图片、音视频等):
1. XHR 上传适配器(createXHRUploadAdapter)
基于 XMLHttpRequest 的标准上传方式,适用于大多数后端直传场景:
import { FileRepository } from 'ckeditor5';
import { createXHRUploadAdapter } from '@itshixun/qckeditor-vue3';
const editorConfig = {
// ...其他配置
extraPlugins: [
function CustomUploadAdapterPlugin(editor) {
editor.plugins.get(FileRepository).createUploadAdapter = (loader) => {
return createXHRUploadAdapter(loader, {
uploadUrl: 'https://api.example.com/upload',
headers: { Authorization: 'Bearer token' },
withCredentials: true,
fieldName: 'file',
extraFormData: { category: 'images' },
});
};
},
],
};2. 自定义异步上传适配器(createAsyncUploadAdapter)
支持自定义异步上传流程,适用于 S3、华为云 OBS 等需要多步上传的场景:
import { FileRepository } from 'ckeditor5';
import { createAsyncUploadAdapter } from '@itshixun/qckeditor-vue3';
const editorConfig = {
extraPlugins: [
function CustomUploadAdapterPlugin(editor) {
editor.plugins.get(FileRepository).createUploadAdapter = (loader) => {
return createAsyncUploadAdapter(loader, async (file, updateProgress) => {
// 示例:上传到对象存储(预签名 URL 模式)
// 第1步:从后端获取预签名上传 URL
const presignRes = await fetch('/api/presign-upload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: file.name, contentType: file.type }),
});
const { uploadUrl, finalUrl } = await presignRes.json();
// 第2步:直传到对象存储(使用 XMLHttpRequest 以便监听进度)
await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('PUT', uploadUrl, true);
xhr.setRequestHeader('Content-Type', file.type);
xhr.upload.addEventListener('progress', (evt) => {
if (evt.lengthComputable && updateProgress) {
updateProgress(Math.round((evt.loaded / evt.total) * 100));
}
});
xhr.addEventListener('load', () => {
xhr.status >= 200 && xhr.status < 300 ? resolve(void 0) : reject('上传失败');
});
xhr.addEventListener('error', () => reject('上传出错'));
xhr.send(file);
});
// 第3步:返回最终访问 URL
return finalUrl;
});
};
},
],
};也可以返回 UploadResponse 对象:
createAsyncUploadAdapter(loader, async (file, updateProgress) => {
// ...上传逻辑
return {
default: 'https://cdn.example.com/image.jpg', // 默认 URL(必须)
url: 'https://cdn.example.com/image.jpg', // 可选
// 还可以返回其他字段
};
});3. Mock 上传适配器(createMockUploadAdapter)
用于演示环境,模拟上传过程和进度更新:
import { FileRepository } from 'ckeditor5';
import { createMockUploadAdapter } from '@itshixun/qckeditor-vue3';
const editorConfig = {
extraPlugins: [
function CustomUploadAdapterPlugin(editor) {
editor.plugins.get(FileRepository).createUploadAdapter = (loader) => {
return createMockUploadAdapter(loader);
};
},
],
};Props
CKEditor
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| editor | Function | 必填 | CKEditor 构建类 |
| config | Object | {} | 编辑器配置 |
| tagName | String | 'div' | 渲染标签 |
| disabled | Boolean | false | 是否禁用 |
| disableTwoWayDataBinding | Boolean | false | 禁用双向绑定 |
| dataNormalizer | Function | (data) => data | 格式化初始值与外部 modelValue 变更的函数 |
| fontSize | Number | - | 编辑区正文字号(px),通过 CSS 变量 --ck-content-font-size 应用 |
| locale | String | 'zh-cn' | 编辑器界面语言,兼容 'zhCN' / 'zh-CN' / 'zh_CN' 等写法;动态变更会重建编辑器实例(见界面语言说明) |
QCKEditor
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| type | 'classic' \| 'inline' \| 'document' | 'classic' | 编辑器类型,非法值兜底为 classic |
| config | Object | {} | 编辑器配置(与默认 highlight / heading 配置深度合并) |
| tagName | String | 'div' | 渲染标签 |
| disabled | Boolean | false | 是否禁用 |
| disableTwoWayDataBinding | Boolean | false | 禁用双向绑定 |
| dataNormalizer | Function | (data) => data | 格式化初始值与外部 modelValue 变更的函数 |
| imageRetry | Boolean | Object | true | 编辑区图片加载失败重试(见 QCKContent 图片重试说明) |
| locale | String | 'zh-cn' | 编辑器界面语言,透传给 CKEditor(见界面语言说明) |
默认配置说明:QCKEditor 预置了「段落 + 标题 1~5」的标题选项,以及「标记(marker)」高亮选项,与历史版本编辑器一致。用户 config 中显式提供同名字段会覆盖默认值。
QCKEditor 在 ready 等事件中传入的编辑器实例类型为 ClassicEditor | InlineEditor | DecoupledEditor(即 QEditorInstance 联合类型)。
QCKClassic
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| config | Object | {} | 编辑器配置 |
| tagName | String | 'div' | 渲染标签 |
| disabled | Boolean | false | 是否禁用 |
| disableTwoWayDataBinding | Boolean | false | 禁用双向绑定 |
| dataNormalizer | Function | (data) => data | 格式化初始值与外部 modelValue 变更的函数 |
| imageRetry | Boolean | Object | true | 编辑区图片加载失败重试(透传给 QCKEditor) |
| locale | String | 'zh-cn' | 编辑器界面语言,透传给 QCKEditor(见界面语言说明) |
QCKEditorPro
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| config | Object | {} | 编辑器配置 |
| content | String | - | 初始内容 |
| placeholder | String | '点击编辑内容' | 占位文本 |
| minHeight | Number | 95 | 最小高度(px) |
| height | Number | 0 | 固定高度(px) |
| fitHeight | Boolean | false | 适应父容器高度 |
| contentBgColor | String | 'rgb(0 0 0 / 2%)' | 背景色 |
| contentBgHoverColor | String | 'rgb(0 0 0 / 4%)' | 悬停背景色 |
| borderRadius | Number | 4 | 圆角大小 |
| sanitize | Boolean | true | 是否清洗 HTML |
| horizontalPadding | Number | String | 'var(--ck-spacing-standard)' | 预览态水平 padding,数字按 px 处理(透传 QCKContent) |
| verticalPadding | Number | String | 'var(--ck-spacing-standard)' | 预览态垂直 padding,用法同 horizontalPadding(透传 QCKContent) |
| trimMargin | Boolean | true | 预览态消除首尾段落 margin(透传 QCKContent) |
| imagePreview | Boolean | Object | false | 预览态图片预览(见 QCKContent 图片预览说明) |
| imageRetry | Boolean | Object | true | 预览态与编辑态图片加载失败重试(见 QCKContent 图片重试说明) |
| dataNormalizer | Function | (data) => data | 格式化初始内容与外部变更的函数 |
| disableExit | Boolean | false | 外部禁用退出编辑模式;与编辑器内部异步操作(PendingActions)共同决定 exit-blocked |
| locale | String | 'zh-cn' | 编辑器界面语言,透传给 QCKClassic(见界面语言说明) |
QCKContent
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| content | String | - | HTML 内容 |
| containerClass | String | '' | 容器类名 |
| sanitize | Boolean | true | 是否清洗 HTML |
| math | Boolean | true | 是否渲染 span.math-tex 中的 KaTeX 公式 |
| highlightCode | Boolean | true | 是否对 pre code 应用 highlight.js 高亮 |
| fontSize | Number | - | 渲染区正文字号(px),通过 CSS 变量 --ck-content-font-size 应用 |
| horizontalPadding | Number | String | 'var(--ck-spacing-standard)' | 水平 padding,数字按 px 处理 |
| verticalPadding | Number | String | 'var(--ck-spacing-standard)' | 垂直 padding,用法同 horizontalPadding |
| trimMargin | Boolean | true | 消除首尾段落 margin:首个 p 上间距、末个 p 下间距置 0 |
| imagePreview | Boolean | Object | false | 图片预览,true 或 QCKImagePreviewOptions 对象(见上文) |
| imageRetry | Boolean | Object | true | 图片加载失败重试,true 或 QCKImageRetryOptions 对象(见上文) |
事件
所有编辑器组件都支持以下事件:
| 事件 | 参数 | 说明 | |------|------|------| | ready | (editor) | 编辑器就绪 | | destroy | - | 编辑器销毁 | | blur | (event, editor) | 失去焦点 | | focus | (event, editor) | 获得焦点 | | input | (data, event, editor) | 内容变化 |
QCKEditorPro 还会发出:
| 事件 | 参数 | 说明 |
|------|------|------|
| exit-blocked | (reason: 'pending-actions' \| 'prop') | 退出编辑模式被阻止。pending-actions 表示编辑器内部仍有异步操作(如上传未完成),prop 表示 disableExit 为 true |
类型导出
import type {
Props,
ExtractEditorType,
QCKImagePreviewOptions,
QCKImageRetryOptions,
QEditorType,
QEditorInstance,
} from '@itshixun/qckeditor-vue3';注意事项
CKEditor 授权: 本库使用 CKEditor 5 的 GPL 授权。如需商业使用,请购买 CKEditor 商业授权。
样式文件: 导入
@itshixun/qckeditor-vue3/dist/index.css即可,该文件已内置 CKEditor 5 官方样式。HTML 清洗:
QCKContent和QCKEditorPro默认开启 XSS 防护,会自动过滤危险标签和属性。QCKEditor默认配置:QCKEditor会自动合并「段落 + 标题 1~5」的标题选项与「标记(marker)」高亮选项。覆盖方式:在config.heading.options或config.highlight.options中提供完整数组(lodashmerge按索引合并)。dataNormalizer: 用于在写入编辑器前格式化内容(初始值与外部modelValue变更都会经过此函数)。返回值为字符串;常见用途:清理历史版本遗留的脏标签、统一空白与编码。异步操作与退出编辑:
QCKEditorPro在PendingActions仍有任务时(即文件上传、撤销恢复队列非空等)会阻止退出编辑模式,并通过exit-blocked事件通知业务方。可结合disableExit强制阻止。locale与动态切换: 语言包按需懒加载,仅使用到的语言会进入运行时加载。修改locale会重建编辑器实例(内容保留),频繁切换有性能开销;若config中已显式配置language/translations,则以config为准。
License
MIT
