第一步在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 //试剂的保存与使用},}}