@itshixun/qckeditor-plugin-video
v1.0.0
Published
CKEditor 5 plugin for video upload and playback
Readme
@itshixun/qckeditor-plugin-video
CKEditor 5 插件:视频上传与播放。在编辑器中插入 <video> 元素,支持本地上传和直接插入视频 URL,上传过程中显示进度条。
安装
npm install @itshixun/qckeditor-plugin-video
# 或
pnpm add @itshixun/qckeditor-plugin-video依赖 ckeditor5(peerDependencies,需自行安装)。
基本使用
1. 引入样式
import '@itshixun/qckeditor-plugin-video/dist/index.css';2. 注册插件
import { ClassicEditor } from 'ckeditor5';
import { QstVideo } from '@itshixun/qckeditor-plugin-video';
const editor = await ClassicEditor.create(element, {
plugins: [
// ... 其他插件
QstVideo,
],
toolbar: [
// ... 其他按钮
'uploadVideo', // 工具栏按钮名称:上传视频
],
qstVideo: {
upload: {
types: ['mp4', 'webm', 'ogg'],
maxSize: 100 * 1024 * 1024, // 100MB
},
progress: {
showProgressBar: true,
useReadOnlyLock: false,
},
},
});3. Vue 中使用
<script setup lang="ts">
import { ClassicEditor } from 'ckeditor5';
import { QstVideo } from '@itshixun/qckeditor-plugin-video';
const config = {
editor: ClassicEditor,
plugins: [/* ... */, QstVideo],
toolbar: ['bold', 'italic', 'uploadVideo'],
qstVideo: {
upload: {
types: ['mp4', 'webm'],
maxSize: 50 * 1024 * 1024,
},
},
};
</script>
<template>
<ckeditor :editor="config.editor" :config="config" />
</template>配置项
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| upload.types | string[] | 否 | 允许上传的文件扩展名列表,如 ['mp4', 'webm', 'ogg'] |
| upload.maxSize | number | 否 | 最大文件大小(字节),超出会提示警告 |
| progress.showProgressBar | boolean | 否 | 是否显示上传进度条,默认 true |
| progress.useReadOnlyLock | boolean | 否 | 上传期间是否锁定编辑器为只读模式,默认 false |
上传配置
本插件依赖 CKEditor 5 的 FileRepository 插件处理文件上传。你需要配置一个自定义的 UploadAdapter,否则上传功能无法正常工作。
自定义 UploadAdapter 实现示例
import type { FileLoader, UploadAdapter } from 'ckeditor5';
class MyUploadAdapter implements UploadAdapter {
private loader: FileLoader;
private xhr?: XMLHttpRequest;
constructor(loader: FileLoader) {
this.loader = loader;
}
upload(): Promise<{ default: string }> {
return this.loader.file.then(
(file) =>
new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject, file!);
this._sendRequest(file!);
})
);
}
abort(): void {
if (this.xhr) {
this.xhr.abort();
}
}
private _initRequest(): void {
const xhr = (this.xhr = new XMLHttpRequest());
xhr.open('POST', 'https://your-api.com/upload', true);
xhr.responseType = 'json';
}
private _initListeners(
resolve: (value: { default: string }) => void,
reject: (reason?: string) => void,
file: File
): void {
const xhr = this.xhr!;
const loader = this.loader;
const genericErrorText = `无法上传文件: ${file.name}.`;
xhr.addEventListener('error', () => reject(genericErrorText));
xhr.addEventListener('abort', () => reject());
xhr.addEventListener('load', () => {
const response = xhr.response;
if (!response || response.error) {
return reject(response?.error?.message || genericErrorText);
}
// 响应必须包含 default 字段(视频 URL)
resolve({ default: response.url });
});
if (xhr.upload) {
xhr.upload.addEventListener('progress', (evt) => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
private _sendRequest(file: File): void {
const data = new FormData();
data.append('file', file);
this.xhr!.send(data);
}
}在编辑器中注册 UploadAdapter
function MyUploadAdapterPlugin(editor: any) {
editor.plugins.get('FileRepository').createUploadAdapter = (loader: FileLoader) => {
return new MyUploadAdapter(loader);
};
}
const editor = await ClassicEditor.create(element, {
plugins: [
// ... 其他插件
MyUploadAdapterPlugin, // 必须先注册,再注册 QstVideo
QstVideo,
],
toolbar: ['uploadVideo'],
});重要:
MyUploadAdapterPlugin必须在QstVideo之前注册到plugins数组中- 上传响应必须返回
{ default: '视频URL' }格式,default字段会被提取为视频的src - 上传失败时会自动移除占位视频元素,并通过
Notification插件显示警告
命令说明
插件注册了两个命令,分别对应不同的使用场景:
videoInsert — 插入已有视频
通过视频 URL 直接插入已有视频(不上传):
editor.execute('videoInsert', {
src: 'https://example.com/videos/demo.mp4',
controls: true, // 可选,默认 true
});uploadVideo — 上传本地视频
通常由工具栏按钮自动触发,也可手动调用:
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = fileInput.files![0];
editor.execute('uploadVideo', { file });输出 HTML
保存态(Data Downcast)
<figure class="video ck-qst-video">
<video controls src="https://example.com/videos/demo.mp4"></video>
</figure>编辑态(Editing Downcast)
编辑态会渲染为 CKEditor 5 的 Widget,带有选中高亮和进度条:
<figure class="video ck-qst-video ck-widget" contenteditable="false">
<video controls src="https://example.com/videos/demo.mp4"></video>
<!-- 上传时显示进度条 -->
<div class="ck-qst-video-progress-bar" style="width: 45%"></div>
</figure>数据兼容性
插件在 upcast(粘贴/加载 HTML)时兼容旧版格式:
| 新版本 | 旧版本 |
|--------|--------|
| <figure class="ck-qst-video"> | <figure class="video"> |
旧版 HTML 会被自动转换为新版数据模型,保存时输出新版格式(同时保留 video 类以兼容旧样式):
<!-- 旧版输入 -->
<figure class="video">
<video controls src="..."></video>
</figure>
<!-- 新版输出 -->
<figure class="video ck-qst-video">
<video controls src="..."></video>
</figure>类型支持
导入插件后,EditorConfig 类型自动扩展,TypeScript 可直接识别 qstVideo 配置:
const config: EditorConfig = {
// 无类型错误,qstVideo 已声明
qstVideo: {
upload: { types: ['mp4'], maxSize: 1024 * 1024 },
},
};导出 API
import {
QstVideo, // 主插件(Editing + Upload)
QstVideoEditing, // 编辑插件(Schema + Conversion + videoInsert 命令)
QstVideoInsertCommand, // 插入已有视频的命令
QstVideoUpload, // 上传功能聚合插件
QstVideoUploadEditing, // 上传编辑插件(自动上传处理 + 进度管理)
QstVideoUploadUI, // 上传 UI 插件(工具栏按钮)
QstVideoUploadProgress, // 上传进度条插件
QstVideoUploadCommand, // 上传视频的命令
insertVideo, // 工具函数:插入视频元素
isVideoAllowed, // 工具函数:检查是否允许插入视频
type QstVideoConfig,
type VideoInsertCommandOptions,
type UploadVideoCommandOptions,
} from '@itshixun/qckeditor-plugin-video';