前言
VUE后台管理有使用富文本编辑器导入word 文档进行编辑的需求
在使用 tinymce-plugin 库中的importword插件,总有各种报错,现在重新通过Mammoth获取word内容,实现导入word编辑的需求。底部有完整代码。
安装
现在TinMCE已经推到了v6版本,兼容Vue3.0X版本,现在我们要说的是Vue2.0X版本和我们的TinyMEC
npm install tinymce@6.1.2 -S
npm install @tinymce/tinymce-vue@2.0.0 -S
安装好后在node_modules里找到这个文件
image.png
将里面你所需要的文件拉到我们本地文件中,Vue2的版本是public,很多的博客的写法是将 node_modules 里面的skins文件夹复制到public/tinymce目录下,经过尝试是不完善的,需要将整个目录倒入进public里。
image.png
tinymce 默认是英文界面,所以还需要下载一个中文包,复制到 public/tinymce/langs目录下
导入tinymcejs
<div id="app"></div>
<script src="/tinymce/tinymce.js"></script>
封装组件
自己封装一个组件
<template>
<div id="textAreaTemplate">
<!-- //富文本插件 -->
<div class="tinymcebox">
<editor v-model="content" :init="init" :disabled="disabled"></editor>
</div>
</div>
</template>
<!--
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\-/'' | |
\ .-\__ `-` ___/-. /
___`. .' /-.-\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=-='
-->
<script>
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/icons/default/icons";
import "tinymce/themes/silver";
import "tinymce/plugins/image";
import "tinymce/plugins/media";
import "tinymce/plugins/table";
import "tinymce/plugins/lists";
import "tinymce/plugins/wordcount";
import "tinymce/plugins/preview";
import "tinymce/plugins/code";
import "tinymce/plugins/link";
import "tinymce/plugins/advlist";
import "tinymce/plugins/codesample";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/autolink";
import "tinymce/plugins/directionality";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/visualchars";
import "tinymce/plugins/template";
import "tinymce/plugins/charmap";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/autosave";
import "tinymce/plugins/autoresize";
export default {
components: {
Editor,
},
props:{
value:{
type:String,
default:''
}
},
watch:{
value(newValue){
this.content = newValue
},
content(newValue){
this.$emit("input",newValue)
}
},
data() {
return {
disabled: false,
content:'',
//初始化配置
init: {
_target: () => this,
//menubar: true, // 菜单栏显隐
language_url: "/tinymce/langs/zh_CN.js",
//language_url: '../../static/tinymce/langs/zh_CN.js', // vue-cli2.x
language: "zh_CN",
skin_url: "/tinymce/skins/ui/oxide",
//skin_url: '../../static/tinymce/skins/ui/oxide', // vue-cli2.x
//content_css: '../../static/tinymce/skins/content/default/content.css',// vue-cli2.x
height: 770,
min_height: 770,
max_height: 770,
toolbar_mode: "wrap",
plugins:
"preview searchreplace autolink directionality visualblocks visualchars fullscreen image link template code codesample table charmap nonbreaking insertdatetime advlist lists wordcount autosave autoresize",
toolbar:
"code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link codesample | alignleft aligncenter alignright alignjustify outdent indent formatpainter | \
styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
table image charmap pagebreak insertdatetime | fullscreen preview|",
content_style: "p {margin: 5px 0;}",
fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
font_formats:
"微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;",
branding: false,
paste_data_images: true,
// 图片上传转码,图片作为base64代码使用,不使用上传接口,如果需要使用上传,请根据文档中的图片上传进行修改
images_upload_handler: (blobInfo, progress) =>
new Promise((resolve, reject) => {
resolve(
"data:" + blobInfo.blob().type + ";base64," + blobInfo.base64()
);
}),
},
content: this.value,
};
},
mounted() {
},
methods: {
},
};
</script>
<style lang="scss" scoped>
</style>
页面使用
<MyTinymce v-model="formData.content" ref="tinymce" />
到这一步已经完成可以用富文本的功能,接下来实现导入word的功能
首先下载依赖
npm install --save mammoth
在富文本的组件中,创建一个上传的组件,
<div style="text-align: right; margin-bottom: 20px">
<el-upload
class="uploadBtn"
style="display:none"
ref="fileRefs"
action=""
:before-upload="beforeUpload"
:show-file-list="false"
:disabled="disabled"
>
<el-button :disabled="disabled">导入</el-button>
</el-upload>
</div>
引入依赖
import "mammoth/mammoth.browser.js";
import Mammoth from "mammoth";
文件上传完之后,转化为html,并把内容传入编辑器中
beforeUpload(file, fileList) {
const self = this;
var reader = new FileReader();
reader.onloadend = function (event) {
let arrayBuffer = reader.result;
//将word 转换成html
const loading = self.$loading({
lock: true,
text: '文件解析中....',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.3)'
});
Mammoth.convertToHtml({ arrayBuffer: arrayBuffer }).then(function (
resultObject
) {
// 重要!不适用setTimeout,无法使用
setTimeout(() => {
//获取原来编辑器的内容
let content = tinymce.activeEditor.getContent()+resultObject.value
tinymce.activeEditor.setContent(content);
loading.close();
}, 100);
});
};
reader.readAsArrayBuffer(file);
return false;
},
将导入word按钮加入到tinymce的工具栏中,
将以下代码加入到tinymce的init配置信息中
image.png
setup: (editor)=> {
let _this = this
editor.ui.registry.addButton("importbtn", {
text: "导入word",
// icon:'', // 目前使用文字按钮,如果需要图标展示,根据文档中自定义图标中的内容进行配置
onAction: function () {
//触发上传组件
_this.$refs['fileRefs'].$refs['upload-inner'].handleClick()
},
});
},
效果展示
image.png
image.png
完整代码
组件代码
<template>
<div id="textAreaTemplate">
<div style="text-align: right; margin-bottom: 20px">
<el-upload
class="uploadBtn"
style="display:none"
ref="fileRefs"
action=""
:before-upload="beforeUpload"
:show-file-list="false"
:disabled="disabled"
>
<el-button :disabled="disabled">导入</el-button>
</el-upload>
</div>
<!-- //富文本插件 -->
<div class="tinymcebox">
<editor v-model="content" :init="init" :disabled="disabled"></editor>
</div>
</div>
</template>
<!--
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\-/'' | |
\ .-\__ `-` ___/-. /
___`. .' /-.-\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=-='
-->
<script>
import "mammoth/mammoth.browser.js";
import Mammoth from "mammoth";
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/icons/default/icons";
import "tinymce/themes/silver";
import "tinymce/plugins/image";
import "tinymce/plugins/media";
import "tinymce/plugins/table";
import "tinymce/plugins/lists";
import "tinymce/plugins/wordcount";
import "tinymce/plugins/preview";
import "tinymce/plugins/code";
import "tinymce/plugins/link";
import "tinymce/plugins/advlist";
import "tinymce/plugins/codesample";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/autolink";
import "tinymce/plugins/directionality";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/visualchars";
import "tinymce/plugins/template";
import "tinymce/plugins/charmap";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/autosave";
import "tinymce/plugins/autoresize";
export default {
components: {
Editor,
},
props:{
value:{
type:String,
default:''
}
},
watch:{
value(newValue){
this.content = newValue
},
content(newValue){
this.$emit("input",newValue)
}
},
data() {
return {
disabled: false,
content:'',
//初始化配置
init: {
_target: () => this,
//menubar: true, // 菜单栏显隐
language_url: "/tinymce/langs/zh_CN.js",
//language_url: '../../static/tinymce/langs/zh_CN.js', // vue-cli2.x
language: "zh_CN",
skin_url: "/tinymce/skins/ui/oxide",
//skin_url: '../../static/tinymce/skins/ui/oxide', // vue-cli2.x
//content_css: '../../static/tinymce/skins/content/default/content.css',// vue-cli2.x
height: 770,
min_height: 770,
max_height: 770,
toolbar_mode: "wrap",
plugins:
"preview searchreplace autolink directionality visualblocks visualchars fullscreen image link template code codesample table charmap nonbreaking insertdatetime advlist lists wordcount autosave autoresize",
toolbar:
"code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link codesample | alignleft aligncenter alignright alignjustify outdent indent formatpainter | \
styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
table image charmap pagebreak insertdatetime | fullscreen preview| importbtn",
content_style: "p {margin: 5px 0;}",
fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
font_formats:
"微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;",
branding: false,
setup: (editor)=> {
let _this = this
editor.ui.registry.addButton("importbtn", {
text: "导入word",
// icon:'material',
onAction: function () {
_this.$refs['fileRefs'].$refs['upload-inner'].handleClick()
},
});
},
paste_data_images: true,
// 图片上传转码
images_upload_handler: (blobInfo, progress) =>
new Promise((resolve, reject) => {
resolve(
"data:" + blobInfo.blob().type + ";base64," + blobInfo.base64()
);
}),
},
content: this.value,
};
},
mounted() {
},
methods: {
beforeUpload(file, fileList) {
const self = this;
var reader = new FileReader();
reader.onloadend = function (event) {
let arrayBuffer = reader.result;
//将word 转换成html
const loading = self.$loading({
lock: true,
text: '文件解析中....',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.3)'
});
Mammoth.convertToHtml({ arrayBuffer: arrayBuffer }).then(function (
resultObject
) {
setTimeout(() => {
let content = tinymce.activeEditor.getContent()+resultObject.value
tinymce.activeEditor.setContent(content);
loading.close();
}, 100);
});
};
reader.readAsArrayBuffer(file);
return false;
}
},
};
</script>
<style lang="scss" scoped>
#textAreaTemplate {
.uploadBtn {
display: inline-block;
margin: 0 10px;
}
.tabTiyle {
border-left: 8px solid rgba(36, 145, 255, 1);
padding-left: 15px;
font-size: 21px;
text-align: left;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: rgba(0, 0, 0, 0.87);
}
}
</style>
<style>
.dialog {
text-align: left;
}
</style>
页面使用
<MyTinymce v-model="formData.content" ref="tinymce" />