第一步在vue2项目下安装
npm install --save @ckeditor/ckeditor5-build-decoupled-document
第二 项目下新建一个plugins的文件夹将这个包ckeditor5-build-classic放入
(包在页面最上方 有个下载按钮 可以下载)
刚开始时 ckeditor5-build-classic文件夹下无node_modules,需要找到 项目下的ruoyi-ui\src\plugins\ckeditor5-build-classic文件地址,cmd打开,然后输入npm install 安装下 ckeditor5-build-classic文件夹下就有这个了node_modules
第三步 (为了兼容wold和pdf的粘贴)
找到plugins\ckeditor5-build-classic\node_modules@ckeditor\ckeditor5-paste-from-office\src\filters\image.js 这个文件,然后打开,搜索regexPictureHeader,将某段代码进行替换如下
//图片替换
regexPictureHeader = /{\pict[\s\S]+?({\*\blipuid\s?[\da-fA-F]+})+?/;
第四步 (为了兼容wold和pdf的粘贴)
找到plugins\ckeditor5-build-classic\node_modules@ckeditor\ckeditor5-paste-from-office\src\filters\space.js 这个文件 ,然后打开,搜索htmlDocument.querySelectorAll,将这段代码替换如下
htmlDocument.querySelectorAll('span[style*=spacerun]').forEach(el => {
if (/[^\b]/.test(el.innerText.trim()) === false) {
const innerTextLength = el.innerText.length || 0;
el.innerHTML = Array(innerTextLength + 1).join('\u00A0 ').substr(0, innerTextLength);
}
});
第五步 在api文件下新建upload.js
import { getToken } from "@/utils/auth";
// upload.js中
class MyUploadAdapter {
constructor(loader) {
// 要在上载期间使用的文件加载器实例
this.loader = loader;
}
// 启动上载过程
upload() {
return this.loader.file.then(
(file) =>
new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject, file);
this._sendRequest(file);
})
);
}
// 中止上载过程
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
// 使用传递给构造函数的URL初始化XMLHttpRequest对象.
_initRequest() {
const xhr = (this.xhr = new XMLHttpRequest());
// 后端上传图片接口 `${process.env.VUE_APP_BASE_API}/common/upload`,
xhr.open(
"POST",
`${process.env.VUE_APP_BASE_API}/common/upload`,
true
);
xhr.responseType = "json";
}
// 初始化 XMLHttpRequest 监听.
_initListeners(resolve, reject, file) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = `无法上传文件: ${file.name}.`;
xhr.addEventListener("error", () => reject(genericErrorText));
xhr.addEventListener("abort", () => reject());
xhr.addEventListener("load", () => {
//这个例子假设XHR服务器的"response"对象将附带
//一个"error",它有自己的"message",可以传递给reject()
//你的集成可能以不同的方式处理上传错误,所以请确保
//当上传失败时,必须调用reject()函数。
const response = xhr.response;
// 当上传失败时,必须调用reject()函数。
if (!response || response.error) {
// reject方法会调用浏览器的alert事件 并清除页面上的图片展示
return reject(
response && response.error
? response.error.message
: genericErrorText
);
}
// 上传成功,从后台获取图片的url地址
// resolve方法会将default中的值插入到页面中img标签的src中
resolve({
default: response.url,
});
});
// 支持时上传进度。文件加载器有#uploadTotal和#upload属性,用于在编辑器用户界面中显示上载进度栏。
if (xhr.upload) {
xhr.upload.addEventListener("progress", (evt) => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
// 准备数据并发送请求
_sendRequest(file) {
// 通过FormData构造函数创建一个空对象
const data = new FormData();
// 通过append()方法在末尾追加key为files值为file的数据
data.append("file", file); // 上传的参数data
/**
* 重要提示:这是实现诸如身份验证和CSRF保护等安全机制的正确位置。
* 例如,可以使用XMLHttpRequest.setRequestHeader()设置包含应用程序先前生成的CSRF令牌的请求头。
*/
this.xhr.setRequestHeader("Authorization", getToken());
this.xhr.send(data);
}
}
function MyCustomUploadAdapterPlugin(editor) {
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
// 在这里将URL配置为后端上载脚本
return new MyUploadAdapter(loader)
}
}
export {
MyUploadAdapter,
MyCustomUploadAdapterPlugin
}
第六步
组件页面 components/CkEditor/index.vue
<template>
<div>
<div :id="editorID"></div>
</div>
</template>
<script>
import {
MyCustomUploadAdapterPlugin,
MyUploadAdapter
} from "@/api/upload";
import ClassicEditor from "@/plugins/ckeditor5-build-classic";
import "@/plugins/ckeditor5-build-classic/build/translations/zh-cn.js";
export default {
props: {
/* 编辑器的内容 */
ckEditorValue: {
type: String,
default: "",
},
/* 只读模式 */
readOnly: {
type: Boolean,
default: false,
},
/* 控制什么时候显示工具条 */
inEditorShow: {
type: Boolean,
default: true,
},
/* editorID */
editorID: {
type: String,
default: 'editor',
}
},
data() {
return {
editor: ''
}
},
watch: {
ckEditorValue: {
handler(newVal) {
var that = this;
setTimeout(() => {
if (that.editor != '' && that.editor != null && that.editor != undefined)
that.editor.setData(that.ckEditorValue);
}, 100)
},
immediate: true,
deep: true
},
readOnly: {
handler(newVal) {
var that = this;
setTimeout(() => {
if (newVal) {
if (that.editor != '' && that.editor != null && that.editor != undefined)
that.editor.enableReadOnlyMode('editor');
} else {
if (that.editor != '' && that.editor != null && that.editor != undefined)
that.editor.disableReadOnlyMode('editor');
}
}, 100)
},
immediate: true,
deep: true
}
},
mounted() {
this.initEditor();
},
methods: {
initEditor() {
let that = this;
ClassicEditor.create(document.querySelector(`#${that.editorID}`), {
language: 'zh-cn',
extraPlugins: [MyCustomUploadAdapterPlugin],
placeholder: '请输入内容....',
autosave: {
waitingTime: 100,
save(editor) {
// return that.saveData(editor.getData());
sessionStorage.setItem("saveEditorData", editor.getData());
},
}
})
.then((editor) => {
// 设置富文本高度
editor.editing.view.change(writer => {
writer.setStyle('min-height', '500px', editor.editing.view.document.getRoot());
});
// 上传文件
editor.plugins.get("FileRepository").createUploadAdapter = (
loader
) => {
return new MyUploadAdapter(loader);
};
//富文本是否只读
if (this.readOnly) {
editor.enableReadOnlyMode('editor');
} else {
editor.disableReadOnlyMode('editor');
}
//根据父组件判断是否显示工具栏
if (!this.inEditorShow) {
var toolbar = document.getElementsByClassName('ck-toolbar');
var border = document.getElementsByClassName('ck-editor__editable_inline');
toolbar[0].style.display = 'none';
border[0].style.border = 'none';
}
editor.setData(that.ckEditorValue);
that.editor = editor;
})
.catch((error) => console.error(error));
},
clear() {
this.editor.setData('');
}
}
}
</script>
<style>
.ck-body-wrapper {
position: absolute;
z-index: 3000;
}
.ck a {
color: rgb(24, 144, 255);
}
</style>
<style lang="scss" scoped>
</style>
第七步 页面引用
<template>
<CkEditor :ckEditorValue="dataForm.storageAndUse" @saveEditorData="saveEditorData"></CkEditor>
</template>
<script>
import CkEditor from "../components/CkEditor/index.vue";
export default {
components:{CkEditor},
data() {
return {
dataForm: {
id: null,
storageAndUse: null //试剂的保存与使用
},
}
}