CKeditor 是一款可定制的适合开发人员使用的富文本编辑器
编辑器官网
官网
构建页面
常用插件
Code tag
Easy Image
Find / Replace
Font Size and Family
Letter-spacing
Line Height
Page Break
Paragraph Indentation
Statistics of the number of characters
Stored automatically in the browser storage
Text Transform
div id="editor">
Sample
This is an instance of the classic editor build.
You can use this sample to validate whether your custom build works fine.
官方文档
安装
npm install --save @ckeditor/ckeditor5-vue @ckeditor/ckeditor5-build-classic
main.js
引入
import Vue from 'vue';
import CKEditor from '@ckeditor/ckeditor5-vue';
Vue.use(CKEditor);
单文件组件使用
自定义图片上传插件必需架构
// 自定义文件上传
class MyUploadAdapter {
constructor(loader, url) {
// 上传期间要使用的 FileLoader 实例。
this.loader = loader;
// 服务器后端的上传URL。 这 是XMLHttpRequest 将图像数据发送到的地址。
this.url = url;
}
// 上传
upload() {
return new Promise((resolve, reject) => {
// 自定义上传逻辑。。。 假设 response 为响应数据
// 上传失败时必须调用 reject() 函数
if (!response || response.error) {
return reject(errorMessage);
}
// 如果上传成功,请调用 resolve() 并传入至少包含 {default: URL} 的对象,该 URL 为上传图像在服务器上的地址。
resolve({
default: response.url // // 此 URL 将用于在内容中显示图像。
});
});
}
// 中止上传
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
}
自定义图片上传插件
// 自定义文件上传
class MyUploadAdapter {
constructor(loader, url) {
// 上传期间要使用的 FileLoader 实例。
this.loader = loader;
// 服务器后端的上传URL。 这 是XMLHttpRequest 将图像数据发送到的地址。
this.url = url;
}
// 开始上传
upload() {
return new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject);
this._sendRequest();
});
}
// 中止上传
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
// 使用传递给构造函数的 URL 初始化 XMLHttpRequest 对象。
_initRequest() {
const xhr = (this.xhr = new XMLHttpRequest());
// 请注意,您的请求可能会有所不同。 由您和您的编辑器集成来选择正确的通信渠道。 此示例使用带有JSON的POST请求作为数据结构,但您的配置可能不同。
xhr.open("POST", this.url, true);
xhr.responseType = "json";
}
// 初始化 XMLHttpRequest 侦听器。
_initListeners(resolve, reject) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = "Couldn't upload file:" + ` ${loader.file.name}.`;
xhr.addEventListener("error", () => reject(genericErrorText));
xhr.addEventListener("abort", () => reject());
xhr.addEventListener("load", () => {
const response = xhr.response;
// 上传失败时必须调用 reject() 函数。
if (!response || response.error) {
return reject(response && response.error ? response.error.message : genericErrorText);
}
// 如果上传成功,请使用至少包含 {default: URL} 的对象,该 URL 为上传图像在服务器上的地址。
resolve({
default: response.url // // 此URL将用于在内容中显示图像。
});
});
// 支持上传进度。 FileLoader 具有 uploadTotal 和 uploaded 属性,如果使用它们,在编辑器的界面中将会显示上传的进度条。
if (xhr.upload) {
xhr.upload.addEventListener("progress", evt => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
// 准备数据并发送请求。
_sendRequest() {
// 准备表单数据。
const data = new FormData();
data.append("upload", this.loader.file);
// 发送请求。
this.xhr.send(data);
}
}
对接插件,以便在初始化编辑器时使用
// 对接插件
function MyCustomUploadAdapterPlugin(editor) {
editor.plugins.get("FileRepository").createUploadAdapter = loader => {
// Configure the URL to the upload script in your back-end here!
return new MyUploadAdapter(loader, "https://img.sqydt.darongshutech.com/");
};
}
初始化编辑器,并加载自定义图片上传功能
// 创建编辑器实例,并加载自定义图片上传功能
ClassicEditor.create(document.querySelector("#editor"), {
extraPlugins: [MyCustomUploadAdapterPlugin]
})
.then(editor => {
window.editor = editor;
})
.catch(err => {
console.error(err.stack);
});
import axios from "axios";
window.axios = axios;
function MyCustomUploadAdapterPlugin(editor) {
editor.plugins.get("FileRepository").createUploadAdapter = loader => {
// Configure the URL to the upload script in your back-end here!
return new MyUploadAdapter(loader, 10, "api/public/getUploadToken");
};
}
// 自定义七妞云上传
class MyUploadAdapter {
constructor(loader, maxSize, getToken) {
// 上传期间要使用的 FileLoader 实例。
this.loader = loader;
// 图片大小限制
this.maxSize = maxSize;
// 获取上传七牛云token,接口地址
this.upTokenUrl = getToken;
// 允许上传的图片格式
this.acceptString = ".jpg,.jpeg,.png,.gif,.JPG,.JPEG,.PNG,.GIF";
// 七牛云上传地址
this.qiniuUrl = "http://up.qiniu.com";
// 文件存放地址
this.baseUrl = "https://img.rmsq.com/";
}
// 上传
upload() {
return new Promise((resolve, reject) => {
this._uploadFile(resolve, reject);
});
}
// 中止上传
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
_uploadFile(resolveCK, rejectCK) {
const file = this.loader.file;
const isLt = file.size / 1024 / 1024 < this.maxSize;
// const fileExt = _file.type.split("/")[1]; // .mp3 在火狐和ie 读取的格式为 mpeg
const lastIndex = file.name.lastIndexOf(".");
const fileExt = file.name.substring(+lastIndex + 1); // 上传的文件的格式
const allAcceptArr = this.acceptString.split(","); // 允许上传的格式
if (!this._isInArr("." + fileExt, allAcceptArr)) {
console.error("请上传格式为" + this.acceptString + "的文件!");
return false;
}
if (this.maxSize && !isLt) {
console.error("请上传小于" + this.maxSize + "MB的文件!");
return false;
}
// 使用FormData对象上传文件
var formData = new FormData();
formData.append("file", file);
// 获取七牛云TOKEN
this._getQiniuToken(fileExt).then(resData => {
formData.append("token", resData.data.token);
formData.append("key", resData.data.key);
// 上传文件至七牛云
this._requestQiniu(formData)
.then(response => {
// 如果上传成功,请调用 resolve() 并传入至少包含 {default: URL} 的对象,该 URL 为上传图像在服务器上的地址。
resolveCK({
default: this.baseUrl + response.key // // 此 URL 将用于在内容中显示图像。
});
})
.catch(err => {
// 上传失败
rejectCK(err);
});
});
}
_isInArr(_val, _arr) {
return _arr.some(item => {
if (item === _val) {
return true;
}
});
}
_getQiniuToken(fileExt) {
return new Promise((resolve, reject) => {
axios.get(this.upTokenUrl, { params: { file_ext: fileExt } }).then(res => {
if (res && res.data && res.data.code === 2000) {
resolve(res.data);
} else {
console.error(res.data.msg);
}
});
});
}
_requestQiniu(formData) {
const config = {
headers: { "Content-Type": "multipart/form-data" },
onUploadProgress: progressEvent => {
// 上传进度
this.loader.uploadTotal = progressEvent.total;
this.loader.uploaded = progressEvent.loaded;
}
};
return new Promise((resolve, reject) => {
axios.post(this.qiniuUrl, formData, config).then(res => {
if (res && res.status == 200) {
resolve(res.data);
} else {
console.error(res.data.msg);
}
});
});
}
}
使用
initEditor() {
ClassicEditor.create(document.querySelector("#sqbCkEditor"), {
// The configuration of the editor.
language: "zh-cn",
toolbar: ["undo", "redo", "|", "heading", "|", "fontSize", "bold", "italic", "link", "bulletedList", "numberedList", "blockQuote", "imageUpload", "imageStyle:full", "imageStyle:side"],
extraPlugins: [MyCustomUploadAdapterPlugin] // 使用自定义上传图片插件
// ckfinder: {
// uploadUrl: "http://up.qiniu.com"
// }
})
.then(editor => {
editor.setData("默认内容
");
this.ckEditor = editor;
})
.catch(msg => {
console.log("msg", msg);
});
},
文档描述
汉化
import "@ckeditor/ckeditor5-build-classic/build/translations/zh-cn.js";
ClassicEditor.create(document.querySelector("#sqbCkEditor"), {
// The configuration of the editor.
language: "zh-cn",
// ckfinder: {
// uploadUrl: "http://up.qiniu.com"
// }
})
.then(editor => {
console.log("asd", editor);
})
.catch(msg => {
console.log("msg", msg);
});
设置内容
this.ckEditor.setData("内容
");
获取编辑器的内容,HTML
const editorContent = this.ckEditor.getData();
获取编辑器的内容,纯文本
this.ckEditor.getData().replace(/<[^>]*>/gi, "");
获取编辑器的内容,过滤图片
this.ckEditor.getData().replace(/|\/>)/gi, "");
获取编辑器的内容,过滤上传的图片
this.ckEditor.getData().replace(/
获取可用工具栏配置
Array.from(this.formControl.ckEditor.ui.componentFactory.names())
const _editorDom = this.ckEditor.editing.view.domRoots.get("main").outerHTML;
与 CKEditor 4 不同,CKEditor 5 实现了自定义数据模型。这意味着加载到编辑器中的每个内容都需要转换为该模型,然后再渲染回视图。
每种内容都必须由某些功能处理。该功能定义了HTML(视图)和编辑器模型之间的双向转换。
ClassicEditor.create(document.querySelector('#showContent')).then(editor => {
editor.isReadOnly = true; // 将编辑器设为只读
editor.setData(editorContent) // 显示内容 editorContent
}).catch(msg => {
console.error(msg);
});