vue+ckEditor5 复制粘贴wold文字+图片并保存格式

第一步在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  //试剂的保存与使用
				},
			}
		}
相关推荐
崔庆才丨静觅1 天前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 天前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 天前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 天前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 天前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 天前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 天前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅1 天前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊1 天前
jwt介绍
前端
爱敲代码的小鱼1 天前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax