vue2使用wangeditor5及word导入解析的实现与问题

安装

wangeditor5

        官网:https://www.wangeditor.com/v5/

yarn add @wangeditor/editor
# 或者 npm install @wangeditor/editor --saveyarn add @wangeditor/editor-for-vue
# 或者 npm install @wangeditor/editor-for-vue --save

mammoth.js

        官网:https://github.com/mwilliamson/mammoth.js

npm install mammoth

        若出现依赖包下载失败的情况,可能是镜像问题,可选择使用国内镜像,参考文档:https://blog.csdn.net/hyk521/article/details/140706064

使用

        editor.vue:

<template><div style="border: 1px solid #ccc;"><input type="file" id="weWordBtn" style="display:none;"accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document"/><Toolbarstyle="border-bottom: 1px solid #ccc":editor="editor":defaultConfig="toolbarConfig":mode="mode"/><Editor:style="editorStyle"v-model="html":defaultConfig="editorConfig":mode="mode"@onCreated="onCreated"@onChange="onChange"@customPaste="customPaste"/></div>
</template><script>import Vue from 'vue';import {Boot, DomEditor} from '@wangeditor/editor';import {Editor, Toolbar} from '@wangeditor/editor-for-vue';import '@wangeditor/editor/dist/css/style.css';import {uploadPic} from "@/api/fileUpload/upload";import mammoth from "mammoth";import {Loading} from "element-ui";//自定义新菜单class wordImportMenu {constructor() {this.title = 'word导入';this.iconSvg = '<svg t="1721893685983" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12124" width="16" height="16"><path d="M563.2 1006.933333s-3.413333 0 0 0l-549.546667-102.4c-6.826667-3.413333-13.653333-10.24-13.653333-17.066666V170.666667c0-6.826667 6.826667-13.653333 13.653333-17.066667l546.133334-136.533333c3.413333 0 10.24 0 13.653333 3.413333s6.826667 6.826667 6.826667 13.653333v955.733334c0 3.413333-3.413333 10.24-6.826667 13.653333-3.413333 3.413333-6.826667 3.413333-10.24 3.413333zM34.133333 873.813333l512 95.573334V54.613333L34.133333 184.32v689.493333z" fill="" p-id="12125"></path><path d="M1006.933333 938.666667h-443.733333c-10.24 0-17.066667-6.826667-17.066667-17.066667s6.826667-17.066667 17.066667-17.066667H989.866667v-785.066666H563.2c-10.24 0-17.066667-6.826667-17.066667-17.066667s6.826667-17.066667 17.066667-17.066667h443.733333c10.24 0 17.066667 6.826667 17.066667 17.066667v819.2c0 10.24-6.826667 17.066667-17.066667 17.066667zM358.4 699.733333c-6.826667 0-13.653333-6.826667-17.066667-13.653333l-68.266666-249.173333-68.266667 249.173333c-3.413333 6.826667-6.826667 13.653333-17.066667 13.653333-6.826667 0-13.653333-3.413333-17.066666-10.24l-102.4-307.2c-3.413333-10.24 3.413333-17.066667 10.24-20.48 10.24-3.413333 17.066667 3.413333 20.48 10.24l85.333333 252.586667 71.68-252.586667c3.413333-13.653333 27.306667-13.653333 34.133333 0l71.68 252.586667 85.333334-252.586667c3.413333-10.24 13.653333-13.653333 20.48-10.24 10.24 3.413333 13.653333 13.653333 10.24 20.48l-102.4 307.2c-3.413333 6.826667-10.24 10.24-17.066667 10.24z" fill="" p-id="12126"></path><path d="M904.533333 256h-341.333333c-10.24 0-17.066667-6.826667-17.066667-17.066667s6.826667-17.066667 17.066667-17.066666h341.333333c10.24 0 17.066667 6.826667 17.066667 17.066666s-6.826667 17.066667-17.066667 17.066667zM904.533333 392.533333h-334.506666c-10.24 0-17.066667-6.826667-17.066667-17.066666s6.826667-17.066667 17.066667-17.066667h334.506666c10.24 0 17.066667 6.826667 17.066667 17.066667s-6.826667 17.066667-17.066667 17.066666zM904.533333 529.066667h-341.333333c-10.24 0-17.066667-6.826667-17.066667-17.066667s6.826667-17.066667 17.066667-17.066667h341.333333c10.24 0 17.066667 6.826667 17.066667 17.066667s-6.826667 17.066667-17.066667 17.066667zM904.533333 665.6h-341.333333c-10.24 0-17.066667-6.826667-17.066667-17.066667s6.826667-17.066667 17.066667-17.066666h341.333333c10.24 0 17.066667 6.826667 17.066667 17.066666s-6.826667 17.066667-17.066667 17.066667zM904.533333 802.133333H580.266667c-10.24 0-17.066667-6.826667-17.066667-17.066666s6.826667-17.066667 17.066667-17.066667h324.266666c10.24 0 17.066667 6.826667 17.066667 17.066667s-6.826667 17.066667-17.066667 17.066666z" fill="" p-id="12127"></path></svg>';this.tag = 'button';}//菜单是否需要激活(如选中加粗文本,“加粗”菜单会激活),用不到则返回 falseisActive(editor) {return false;}//获取菜单执行时的 value,用不到则返回空字符串或 falsegetValue(editor) {return '';}//菜单是否需要禁用(如选中 H1 ,“引用”菜单被禁用),用不到则返回 falseisDisabled(editor) {return false; // or true}//点击菜单时触发的函数exec(editor, value) {document.getElementById('weWordBtn').click();}}const wordImportConf = {key: 'wordImport',factory() {return new wordImportMenu();}};Boot.registerMenu(wordImportConf);export default Vue.extend({components: {Editor, Toolbar},props: {/* 编辑器的内容 */value: {type: String,default: "",},/* 高度 */height: {type: Number,default: 500,},/* 是否只读 */readOnly: {type: Boolean,default: false},/* 编辑器内提示语 */placeholder: {type: String,default: '请输入内容...'}},data() {return {editor: null,html: '',toolbarConfig: {modalAppendToBody: false,toolbarKeys: ['headerSelect', 'blockquote', '|', 'bold', 'underline', 'italic', 'through', 'code', 'sup', 'sub','clearStyle', '|', 'color', 'bgColor', 'fontSize', 'lineHeight', '|', 'bulletedList', 'numberedList', 'todo',{'key': 'group-justify','title': '对齐','iconSvg': '<svg viewBox=\"0 0 1024 1024\"><path d=\"M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z\"></path></svg>','menuKeys': ['justifyLeft', 'justifyRight', 'justifyCenter', 'justifyJustify']},{'key': 'group-indent','title': '缩进','iconSvg': '<svg viewBox=\"0 0 1024 1024\"><path d=\"M0 64h1024v128H0z m384 192h640v128H384z m0 192h640v128H384z m0 192h640v128H384zM0 832h1024v128H0z m0-128V320l256 192z\"></path></svg>','menuKeys': ['indent', 'delIndent']},'|', 'insertLink', 'uploadImage', 'insertTable', 'codeBlock', 'divider', '|', 'undo', 'redo', '|', '|', 'fullScreen'],// excludeKeys: ['fontFamily', 'emotion', 'group-video']insertKeys: {index: 32,keys: ['wordImport']}},editorConfig: {placeholder: this.placeholder,readOnly: this.readOnly,autoFocus: true,MENU_CONF: {'uploadImage': {timeout: 300000,fieldName: 'files',maxNumberOfFiles: 10,allowedFileTypes: ['image/jpeg', 'image/png'],// allowedFileTypes: ['image/*'],maxFileSize: 1024 * 1024 * 5,server: process.env.VUE_APP_BASE_API + '/system/fileStorage/uploadPic',onError: (e, t, n) => {this.$message.error('图片上传失败:' + t);},onFailed: (e, t) => {this.$message.error('图片上传失败:未知错误');},onSuccess: (e, t) => {this.$message.success('图片上传成功');},customInsert(resp, insertFn) {insertFn(process.env.VUE_APP_BASE_API + resp.data.url, '', '');}}}},mode: 'default'}},computed: {editorStyle() {return 'overflow-y: hidden;height: ' + this.height + 'px;';}},watch: {value: {handler(val) {if (val !== this.html) {this.html = val === null ? "" : val;}},immediate: true,},readOnly: {handler(flag) {if (this.editor !== null) {if (flag) {this.editor.disable();} else {this.editor.enable();}}}}},methods: {onCreated(editor) {this.editor = Object.seal(editor);console.log('editor.getConfig()', editor.getConfig())console.log('editor.getAllMenuKeys()', editor.getAllMenuKeys())console.log('editor.getConfig().hoverbarKeys', editor.getConfig().hoverbarKeys)console.log('editor.getMenuConfig(uploadImage)', editor.getMenuConfig('uploadImage'))},onChange(editor) {console.log('toolbar.getConfig().toolbarKeys', DomEditor.getToolbar(editor).getConfig().toolbarKeys)console.log('editor.children ', editor.children)this.$emit('onChange', {editor: editor, html: editor.getHtml(), text: editor.getText()});},customPaste(editor, event, callback) {console.log('ClipboardEvent 粘贴事件对象', event)// const html = event.clipboardData.getData('text/html') // 获取粘贴的 html// const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本// const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)// 自定义插入内容// editor.insertText('xxx')// 返回 false ,阻止默认粘贴行为// event.preventDefault()// callback(false) // 返回值(注意,vue 事件的返回值,不能用 return)// 返回 true ,继续默认的粘贴行为// callback(true)},base64ToBlob(imageType, imageBuffer) {let byteCharacters = atob(imageBuffer);let byteNumbers = new Array(byteCharacters.length);for (let i = 0; i < byteCharacters.length; i++) {byteNumbers[i] = byteCharacters.charCodeAt(i);}let byteArray = new Uint8Array(byteNumbers);let blob = new Blob([byteArray], {type: imageType});let imageName = 'e' + new Date().getTime();return new File([blob], imageName, {type: imageType});}},mounted() {document.getElementById("weWordBtn").addEventListener("change", (event) => {let requestLoading = Loading.service({fullscreen: true,text: 'word解析中......',spinner: 'el-icon-loading',background: 'rgba(217,217,217,0.2)'});let editorObj = this.editor;let _this = this;if (event.target.files && event.target.files.length > 0) {let file = event.target.files[0];mammoth.convertToHtml({arrayBuffer: file.arrayBuffer()}, {ignoreEmptyParagraphs: true,transformDocument: mammoth.transforms.paragraph((element) => {console.log('element', element)if (element.styleName === null) {if (element.children && element.children.length > 0) {for (let i = 0; i < element.children.length; i++) {let secondChild = element.children[i];if (secondChild.type === 'hyperlink') {secondChild.targetFrame = '_blank';} else if (secondChild.type === 'run') {if (secondChild.children && secondChild.children.length > 0) {if (i === 0 && secondChild.children[0].type === 'text') {let originVal = secondChild.children[0].value;secondChild.children[0].value = '        ' + originVal;}if (secondChild.highlight !== null) {secondChild.style = 'background-color: ' + secondChild.highlight + ';';for (let j = 0; j < secondChild.children.length; j++) {let thirdChild = secondChild.children[j];thirdChild.style = 'background-color: ' + secondChild.highlight + ';';}}}} else {}}}}return element;}),styleMap: ["u => u"],convertImage: mammoth.images.imgElement(function (image) {return image.read('base64').then(async (imageBuffer) => {//本地图片上传至服务器let result = '';let imgFile = _this.base64ToBlob(image.contentType, imageBuffer);let formData = new FormData();formData.append('files', imgFile);await uploadPic(formData).then(resp => {if (resp.code === '200') {result = process.env.VUE_APP_BASE_API + resp.data.url;}}).catch(e => {console.error('uploadPic-error : ', e)});return {src: result}});})}).then(function (result) {console.log('result', result)if (result.messages.length > 0) {_this.$message.warning('发生错误:' + result.messages[0].message);} else {if (editorObj !== null) {editorObj.clear();editorObj.dangerouslyInsertHtml(result.value);}}requestLoading.close();}).catch(function (error) {console.error(error);requestLoading.close()});}});},beforeDestroy() {if (this.editor !== null) {this.editor.destroy();}}});
</script><style scoped>
</style>

        Test.vue:

<template><div><h1 style="text-align: center">editor测试</h1><div style="width: 80%;margin: 0 auto;"><editor :value="editorHtml" :height="450" :readOnly="readOnly" @onChange="onChange"/><div class="test_count"><span>{{editorCount}}&nbsp;字</span></div></div><div style="text-align: center;margin-top: 25px;"><el-button type="primary" @click="control">{{controlText}}</el-button><el-button type="primary" @click="submit">提交</el-button></div></div>
</template><script>import Editor from './editor';export default {name: "Test",components: {Editor},data() {return {readOnly: false,controlText: '禁用',editorHtml: '',editorText: ''}},computed: {editorCount() {return this.editorText.replace(/\s*/g, "").replace(/\n/g, "").length;}},mounted() {},methods: {onChange(data) {if (data.html !== this.editorHtml) {this.editorHtml = data.html;this.editorText = data.text;}},control() {this.readOnly = !this.readOnly;if (this.readOnly) {this.controlText = '启用';} else {this.controlText = '禁用';}},submit() {console.log('editorHtml', this.editorHtml)console.log('editorText', this.editorText)}},};
</script>
<style scoped>.test_count {height: 40px;line-height: 40px;text-align: right;padding-right: 20px;border: 1px solid #ccc;border-top: none;}
</style>

        页面效果:

word导入问题与解决方案

        问题:

                mammoth 仅支持简单的样式,对于背景色、颜色字体等高级样式无法支持。

        解决方案:

                1、修改 mammoth.js 的源码,参考文档:https://blog.csdn.net/Jioho_chen/article/details/124699665

                2、前端加一个按钮或触发器,后端 Java 使用 poi 解析 word 内容,具体参考:https://www.cnblogs.com/ismallboy/p/12584761.html

        若有其他方法,欢迎留言探讨。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/391062.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一款功能强大的屏幕演示工具,免费版足够使用!

鼠标换肤 | 屏幕画笔 | 放大镜 | 聚光灯 | 屏幕放大 | 倒计时&#xff0c;功能强大的屏幕演示工具 屏幕演示工具适用于Windows平台&#xff0c;特别是Windows 10及以上版本。该软件提供了多种实用功能&#xff0c;包括鼠标换肤、屏幕画笔、放大镜、聚光灯、屏幕放大和倒计时等…

MyBatis的example.createCriteria()方法学习记录

目录 一、mapper的crud方法:1. insert方法insert(User user)insertSelective(User user) 2. select方法selectByPrimaryKey(id)selectByExample(example)selectCountByExample(example) 3. update方法updateByPrimaryKey(User user)updateByPrimaryKeySelective(User user)upda…

el-table 表格序号列前端实现递增,切换分页不从头开始

<el-table-column type"index" width"55" label"序号" :index"hIndex"> </el-table-column> 分页 <el-pagination size-change"handleSizeChange" current-change"handleCurrentChange"> <…

Day29 | 动态规划 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

语言 Java 509. 斐波那契数 斐波那契数 题目 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n -…

Java 中的缓冲流

字符流 前面学习的字节流和字符流都是基本流&#xff0c;其中字符流的底层其实已经在内存中创建了一个长度为8192的字节数组作为缓存区。而字节流中则是没有的。 在内存中增加缓冲区的目的是为了减少内存与硬盘的交互的次数&#xff0c;因为这一操作比较耗时。 下面是一个图…

Pr2024苹果(mac)版剪辑软件安装下载(附下载链接)

Adobe Premiere Pro 2024&#xff08;简称PR 2024&#xff09;是一款由Adobe公司开发的专业视频编辑软件&#xff0c;被广泛应用于电影、电视、广告和社交媒体视频的制作。以下是对PR 2024的详细简介&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rN-3kB3KQgn0JswDa…

基于PFC和ECN搭建无损RoCE网络的工作流程分析

无损RoCE网络概念 RDMA&#xff08;Remote Direct Memory Access&#xff0c;远程直接内存访问&#xff09;是一种为了解决网络传输中服务器端数据处理延迟而产生的技术。RDMA 将用户应用中的数据直接传入服务器的存储区&#xff0c;通过网络将数据从一个系统快速传输到远程系…

Leetcode第136场双周赛题解(c++)

题外话 也是好久没有更新力扣比赛的题解了&#xff0c;前段时间也是比较忙&#xff08;说的好像现在不忙一样哈哈&#xff09;&#xff0c;像我等菜鸟&#xff0c;一般都是保二进三四不写的&#xff0c;笑死。 题目一.求出胜利玩家的数目 给你一个整数 n &#xff0c;表示在…

MySQL·事务

目录 事务是用来做什么的&#xff1f; 什么是事务&#xff1f; 为什么会出现事务 事务提交方式 事务操作前提了解 事务常见操作方式 读未提交(RU) 读提交(RC) 可重复读(RR) 串行化 一致性(Consistency) 事务是用来做什么的&#xff1f; CURD不加控制&#xff0c;会…

Phalco安装过程以及踩的一些坑(mac环境)

一 背景 公司用Phalcon框架好长时间了,中途发现了一些Phalcon使用的上的问题,于是想在本地搭建一套Phalcon的环境,方便排查问题使用。 二 Mac系统下的安装 看了很多说法,最终发现还是官网给力,安装Phalcon使用下列命令即可(前提条件是PHP已安装好,工具pecl也安装好了):…

现代C++多线程编程:C++20 jthread全面解析

目录 一、引言 二、jthread 的基本特点和优势 自动 join 机制 线程取消功能 相对于 std::thread 的改进和便利 三、jthread 的工作原理 线程的生命周期管理 线程取消机制的实现和使用方法 四、jthread 的实际应用案例 服务器编程中的应用 高性能计算中的应用 实时系…

JVM知识总结(内存结构)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 内存模型 内存结构 堆&#xff1a; 存放对象实例, 几乎所有的对象…

GUI:Tkinter(一)

Tkinter文档 一&#xff0c;Tkinter基本流程 1. 创建应用程序主窗口对象 from tkinter import * window Tk() window.mainloop()#开启主循环 2. 在主窗口中&#xff0c;添加各种可视化组件&#xff0c;比如&#xff1a;按钮&#xff08;Button&#xff09;、文本框&#x…

[CP_AUTOSAR]_系统服务_DEM模块(三)功能规范之诊断事件定义

目录 1、诊断事件定义1.1、Event priority&#xff08;事件优先级&#xff09;1.2、Event occurrence&#xff08;事件发生计数器&#xff09;1.3、Event kind&#xff08;事件类别&#xff09;1.4、Event destination&#xff08;故障内存&#xff09;1.5、Diagnostic monitor…

springboot系列教程(三十):springboot整合Zookeeper组件,管理架构中服务协调

一、Zookeeper基础简介 1、概念简介 Zookeeper是一个Apache开源的分布式的应用&#xff0c;为系统架构提供协调服务。从设计模式角度来审视&#xff1a;该组件是一个基于观察者模式设计的框架&#xff0c;负责存储和管理数据&#xff0c;接受观察者的注册&#xff0c;一旦数据…

【数据结构】链表篇

文章目录 1.链表的概念以及结构2.链表的分类2.1 单向或者双向2.2 带头或者不带头2.3 循环或者不循环2.4 无头单向非循环链表和带头双向循环链表 3.单链表的实现3.1 准备工作3.2 节点的创建3.3 单链表的释放3.4 打印链表3.5 单链表的尾插3.6 单链表的尾删3.7 单链表头删3.8 单链…

TiDB系列之:TiCDC同步TiDB数据库数据到Kafka集群Topic

TiDB系列之&#xff1a;TiCDC同步TiDB数据库数据到Kafka集群Topic 一、Changefeed 概述Changefeed 状态流转操作 Changefeed 二、同步数据到Kafka创建同步任务&#xff0c;复制增量数据 KafkaSink URI 配置 kafka最佳实践TiCDC 使用 Kafka 的认证与授权TiCDC 集成 Kafka Connec…

搭建高可用OpenStack(Queen版)集群(一)之架构环境准备

一、搭建高可用OpenStack&#xff08;Queen版&#xff09;集群之架构环境准备 一、架构设计 二、初始化基础环境 1、管理节点创建密钥对&#xff08;方便传输数据&#xff09; 所有控制节点操作 # ssh-keygen #一路回车即可 Generating public/private rsa key pair. Enter f…

算法通关:016:设计循环双端队列

文章目录 题目思路代码运行结果问题为什么能直接调用方法名 题目 leetcode641 设计循环双端队列 思路 代码 import java.util.Deque; import java.util.LinkedList;/*** Author: ggdpzhk* CreateTime: 2024-08-03* 641 双端队列&#xff1a;利用双向链表和动态数组实现*/ pu…

C#和S7-1200PLC S7.NET通信

1、一步步建立一个C#项目 一步步建立一个C#项目(连续读取S7-1200PLC数据)_s7协议批量读取-CSDN博客文章浏览阅读1.7k次,点赞2次,收藏4次。这篇博客作为C#的基础系列,和大家分享如何一步步建立一个C#项目完成对S7-1200PLC数据的连续读取。首先创建一个窗体应用。_s7协议批量…