在 vue.js + element-ui 中安装 tinymce富文本编辑器

官网:http://tinymce.ax-z.cn/

一、简介

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。

二、TinyMCE的优势:

  • 开源可商用,基于LGPL2.1
  • 插件丰富,自带插件基本涵盖日常所需功能(示例看下面的Demo-2)
  • 接口丰富,可扩展性强,有能力可以无限拓展功能
  • 界面好看,符合现代审美
  • 提供经典、内联、沉浸无干扰三种模式(详见“介绍与入门”)
  • 对标准支持优秀(自v5开始)
  • 多语言支持,官网可下载几十种语言。

三、安装组件

第三方集成专区 -- Vue
点击进去
进去后的文档
执行命令:npm install @packy-tang/vue-tinymce 或者 E:/HBuilderX/plugins/npm/npm.cmd install @packy-tang/vue-tinymce,最后在node_modules文件夹 生成 @packy-tang文件夹/vue-tinymce文件夹

四、官网的引用

<template>
    <!-- 全局引入TinyMCE -->
    <script src="//unpkg.com/tinymce@5.1.5/tinymce.min.js"></script>
    <!-- App -->
    <div id="app">
        <vue-tinymce v-model="content" :setup="setup" :setting="setting" />
    </div>
</template>
<script>
    import Vue from "vue"
    import VueTinymce from "@packy-tang/vue-tinymce"

    //安装组件
    Vue.use(VueTinymce)

    new Vue({
        el: "#app",
        data: function() {
            return {
                content: "<p>html content</p>",
                setting: {
                    height: 500
                }
            }
        },
        methods: {
            setup(editor) {
                console.log(editor)
            }
        }
    })
</script>

五、实际项目

1、在node_modules文件夹 生成的 @packy-tang/vue-tinymce文件夹,将vue-tinymce文件夹 复制到 某个components文件夹里面

2、将tinymce.min.js放于 public/static/js文件夹

下载地址:https://unpkg.com/tinymce@5.1.5/tinymce.min.js

tinymce.min.js的存放位置

3、问题:

3-1、tinymce.min.js的引用问题

可以直接引用线上的路径,本人建议将tinymce.min.js下载,引用自己项目的路径

  • A、代码如下:
<template>
  <el-container>
    <el-header>
      <div><span>校园头条</span><span class="commapp_arrow">></span><span>24小时</span></div>
    </el-header>
    <el-main class="comm-HeadlinesDiv">
      <div class="el-col-md-16 el-col-offset-4">
        <div class="card updaterCard">
          <div class="myUpdateItem ThemeBorderColor4">
            <div class="myUpdateItem_Content">
              <div class="msgContainer msg_textarea_Updater">
                <div class="mentions-input-box">

                  <script src="https://unpkg.com/tinymce@5.1.5/tinymce.min.js"></script>
                  <VueTinymce v-model="h_editHtml" />

                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-main>
  </el-container>
</template>

<script>
  import { mapActions } from 'vuex'
  import VueTinymce from '../components/vue-tinymce/src/vue-tinymce.vue'

  export default {
    name: 'Hours',
    components: {
      VueTinymce
    },
    data() {
      return {
        h_editHtml: '<h1>11111</h1>'
      }
    },
    methods: {
      
    }
  }
</script>

<style>
</style>
  • B、报错:
报错.png
  • C、解决:
我们项目<script></script>的引用要放到public/index.html

3-2、vue-tinymce.vue里面的render位置问题

  • A、代码如下:
<script>
  /**
   * 注:编辑器二次刷新处理
   * 编辑器二次刷新具体效果为输入光标重置到第一行第一个字前。
   * 这种效果根本无法正常录入,其原因是双向绑定数据导致编辑器数据更新所致。
   * 根据编辑器的不同状态做标记,当标记为`INPUT`录入时,数据将不会更新至编辑器,
   * 从而避免二次更新的情况,具体请看`content`部分和`editor event`部分的代码。
   * */

  const INIT = 0;
  const INPUT = 1;
  const CHANGED = 2;

  const status = ['INIT', 'INPUT', 'CHANGED']
  const changedLog = debug => {
    if (!debug) return () => false
    console.warn("`@packy-tang/vue-tinymce`进入debug模式");
    return (e, _status, val, oldVal) => console.log(`来自:%s | 状态:%s \n %s \n %s`, e.type, status[_status], val, oldVal)
  }

  export default {
    name: "VueTinymce",
    model: {
      prop: "content",
      event: "change"
    },
    props: {
      content: {
        type: [String, Object],
        default: ''
      },
      setup: {
        type: Function,
        default: function() {}
      },
      disabled: {
        type: Boolean,
        default: false
      },
      setting: {
        type: Object,
        default: function() {
          return {};
        }
      },
      debug: Boolean
    },
    render(createElement) {
      if (typeof tinymce === "undefined") {
        return createElement('div', "tinymce is undefined");
      }
      return createElement('div', {
        attrs: {
          id: this.id
        }
      });
    },
    data() {
      return {
        id: 'vue-tinymce-' + Date.now() + Math.floor(Math.random() * 1000),
        editor: null,
        status: INIT,
        bookmark: null
      }
    },
    watch: {
      content(val, oldVal) {
        this.changedLog({
          type: "propsChanged"
        }, this.status, `${val} | ${oldVal}`, "--")
        if (this.status === INPUT || oldVal === val) return;
        if (!this.editor || !this.editor.initialized)
      return; // fix editor plugin is loading and set content will throw error.
        if (val === null) return this.resetContent("");
        this.setContent(val);
      },
      disabled(val) {
        this.editor.setMode(val ? "readonly" : "design")
      }
    },
    created() {
      this.changedLog = changedLog(this.debug)
      if (typeof tinymce === "undefined") throw new Error('tinymce undefined');
    },
    beforeMount() {
      const setting = Object.assign({},
        this.setting, {
          selector: '#' + this.id,
          setup: (editor) => {
            this.setup(editor);
            // console.log('setup');
            editor.on('init', () => {
              // console.log('init', this.content);
              this.setContent(this.content, editor)
              editor.on('keyup input', e => { //只在编辑器中打字才会触发
                this.status = INPUT //编辑器录入文字时标记为`INPUT`状态
              })
              editor.on('SetContent', e => { //编辑器在插入图片和撤销/重做时触发,组件content更新数据也会导致触发
                this.changedLog(e, this.status, editor.getContent(), "--")
              })
              editor.on('Blur', e => {
                this.status = INIT
                this.changedLog(e, this.status, editor.getContent(), "--")
              })
              editor.on('input keyup Change Undo Redo ExecCommand NodeChange', e => {
                this.onChanged(e, editor)
              })
            });
          }
        }
      );
      this.editor = tinymce.createEditor(setting.selector, setting)
    },
    mounted() {
      this.editor.targetElm = this.$el
      this.editor.render()
    },
    updated() {
      this.editor.render()
    },
    beforeDestroy: function() {
      this.editor.remove();
    },
    methods: {
      setContent(val, editor) {
        if (!editor) editor = this.editor
        editor.setContent(val)
        editor.selection.moveToBookmark(this.bookmark)
      },
      resetContent(val, editor) {
        if (!editor) editor = this.editor
        if (!!editor.resetContent) return editor.resetContent(val)
        editor.setContent(val)
        editor.setDirty(false)
        editor.undoManager.clear()
      },
      onChanged(e, editor) {
        if (!editor) editor = this.editor
        if (e.type === 'change') this.bookmark = e.level.bookmark
        const content = editor.getContent()
        this.changedLog(e, this.status, content, "--")
        this.$emit('change', content);
      }
    }
  }
</script>
  • B、报错:
报错.png
  • C、解决:


    将render方法 放于 所有方法的最后

3-3、CHANGELOG.md

  • A、报错:
报错.png
  • B、解决:
直接删除CHANGELOG.md

3-4、README.md

  • A、报错:
报错.png
  • B、解决:
直接删除README.md

3-5、LICENSE

  • A、报错:
报错.png
  • B、解决:
直接删除LICENSE

3-6、vue-tinymce.cjs.js.map缺少module.exports =

  • A、代码如下:
代码.png
  • B、报错:
报错.png
  • C、解决:
解决.png

3-7、vue-tinymce.esm.js.map缺少module.exports =

  • A、代码如下:
代码.png
  • B、报错:
报错.png
  • C、解决:
解决.png

3-8、vue-tinymce.umd.js.map缺少module.exports =

  • A、代码如下:
代码.png
  • B、报错:
报错.png
  • C、解决:
解决.png

3-9、Uncaught SyntaxError: Unexpected token '<'

  • A、代码如下:
代码.png
  • B、报错:
报错.png
  • C、解决:
不知道为什么引用线上的tinymce.min.js路径,就不会报错

四、效果图

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

推荐阅读更多精彩内容