vue2 tinymce实现导入word功能

前言

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" />
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359