富文本编辑器进阶使用

富文本编辑器进阶使用

最近,自己想用vue 做一个商品详情编辑器, 选择技术方案如下:

样式与UI不符合 并且还要后端配合 加载速度比较慢

文档全英文 (CKEditor4的文档比CKEditor5的文档全面一点) 看了半天也没搞清楚怎么扩展

加载速度快,文档全中文,但是功能不够全面

全英文文档 稍微体验了一下 无法评价

这个就是我最终选中的编辑器,在此 记录下开发流程

1.引入到vue项目中

npm install @tinymce/tinymce-vue --save
npm install tinymce --save

2.文本编辑器本地化 (官网上面的例子都是通过 api-key 的方式去引用js 如果没有网络连接,可能会无法引用,作者使用 vue-cli3 搭建的 如果使用vue-cli2 的 )

tinymce包 在 node_modules/tinymce 拷贝下来的 如下图

拷贝完后 还要到官网上去找语言包 最终目录如下

好了,前期的准备工作完成了,接下该在页面中使用了

代码如下:

<template>
  <div class="editor">
    <editor :init="init" ref="editor" :tinymce-script-src="tinymceScriptSrc" @onInit="onReady"/>
    <!-- <textarea id="tinymceEl" ref="tinymceRef"></textarea> -->
</template>
<script>
import Editor from '@tinymce/tinymce-vue'
export default {
  components: { Editor, PhotoAlbum },
  name: 'CkEditor',
  props: {
    baseUrl: {
      type: String,
      default: process.env.BASE_URL
  data () {
    return {
      visible: false,
      editor: null,
      tinymceScriptSrc: '/tinymce/tinymce.min.js',//本地的tinymce文件地址
      init: {
        height: 500,
        menubar: false,
        resize: false, // 调整编辑器大小工具 true(仅允许改变高度), false(完全不让你动), 'both'(宽高都能改变,注意引号)
        statusbar: true, // 显示隐藏状态栏 状态栏指的是编辑器最底下、左侧显示dom信息、右侧显示Tiny版权链接和调整大小的那一条。默认是显示的,设为false可将其隐藏。
        branding: false, // 隐藏右下角技术支持
        toolbar: 'code bullist numlist emoticons charmap hr insertdatetime link | help fullscreen image', 
        language: 'zh_CN',
        language_url: '/tinymce/langs/zh_CN.js',
        theme: 'silver',
        theme_url: '/tinymce/themes/silver/theme.min.js',
        base_url: '/tinymce',
        suffix: '.min',
        plugins: 'image,wordcount,charmap,code,hr,lists,advlist,emoticons,fullscreen,help,insertdatetime,link',
        setup: function (editor) {
  mounted () {
    var that = this
    this.init.clickImage = function (editor) {
      that.visible = true
  methods: {
    onReady (e) {
      this.editor = e.target
</script>

总算成功的引入了,接下来就要开发自己的组件了

功能:

1.导入图片, 作者不想使用自带的图片上传插件 如图:

官网上的例子

比较难用, 自己撸

先阅读一下插件的源码

发现就是ui.registry.addButton 和 .ui.registry.addMenuItem 这两个方法注册插件的

onAction 就是点击事件触发器 怎么触发事件 直接打印 tinymce 全局 发现我们自定义的参数也可以在 tinymce.settings中找到 ojbk 找到解决方案了 直接在onAction中 去调用tinymce.settings中的回调函数 不就行了吗

最终实现效果

弹出窗是我自己用vue写的一个组件(未完成)

点击图片选择弹出自己写的弹窗组件

饶了一大圈 终于可以自由的开发 tinymce 组件, 想咋玩就咋玩呀

最终代码如下

!(function () {
  'use strict'
  var e = tinymce.util.Tools.resolve('tinymce.PluginManager')
  var o = function (o) {
    var e = o.getContent({
      source_view: !0
    o.windowManager.open({
      title: 'Source Code',
      size: 'large',
      body: {
        type: 'panel',
        items: [{
          type: 'textarea',
          name: 'code'
      buttons: [{
        type: 'cancel',
        name: 'cancel',
        text: 'Cancel'
        type: 'submit',
        name: 'save',
        text: 'Save',
        primary: !0
      initialData: {
        code: e
      onSubmit: function (e) {
        var t, n
        t = o,
        n = e.getData().code,
        t.focus(),
        t.undoManager.transact(function () {
          t.setContent(n)
        t.selection.setCursorLocation(),
        t.nodeChanged(),
        e.close()
  !(function t () {
    e.add('imageUpload', function (e) {
      var t, n
      return (t = e).addCommand('InsertImageUpload', function (e) {
        if (Array.isArray(e) && e.length > 0) {
          var html = ''
          for (var i in e) {
            var obj = e[i]
            html += '<img src="' + obj + '" class="image" /><br />'
            if (i < e.length - 1) {
              html += '<br />'
          t.execCommand('mceInsertContent', false, html)
      (n = e).ui.registry.addButton('imageUpload', {
        icon: 'image',
        tooltip: 'Insert/edit image',
        onAction: function () {
          if (typeof n.settings.clickImage === 'function') {
            return n.settings.clickImage(n)
          return null
      n.ui.registry.addMenuItem('imageUpload', {
        icon: 'image',
        text: 'Insert/edit image',
        onAction: function () {
          if (typeof n.settings.clickImage === 'function') {
            return n.settings.clickImage(n)
          return null

插件js /tinymce/plugins/imageUpload.js

<template>
  <div class="editor">
    <editor :init="init" ref="editor" :tinymce-script-src="tinymceScriptSrc" @onInit="onReady"/>
    <!-- <textarea id="tinymceEl" ref="tinymceRef"></textarea> -->
    <!-- 自定义弹窗组件 -->
    <photo-album :visible.sync="visible" @confirm="confirm"></photo-album>
</template>
<script>
import Editor from '@tinymce/tinymce-vue'
import PhotoAlbum from '@/components/photoAlbum/src/album.vue'
export default {
  components: { Editor, PhotoAlbum },
  name: 'CkEditor',
  props: {
    baseUrl: {
      type: String,
      default: process.env.BASE_URL
  data () {
    return {
      visible: false,
      editor: null,
      tinymceScriptSrc: '/tinymce/tinymce.min.js',
      init: {
        height: 500,
        menubar: false,
        resize: false, // 调整编辑器大小工具 true(仅允许改变高度), false(完全不让你动), 'both'(宽高都能改变,注意引号)
        statusbar: true, // 显示隐藏状态栏 状态栏指的是编辑器最底下、左侧显示dom信息、右侧显示Tiny版权链接和调整大小的那一条。默认是显示的,设为false可将其隐藏。
        branding: false, // 隐藏右下角技术支持
        toolbar: 'code bullist numlist emoticons imageUpload charmap hr insertdatetime link | help fullscreen image',
        language: 'zh_CN',
        language_url: '/tinymce/langs/zh_CN.js',
        // skin_url: '/tinymce/skins/lightgray',
        // skin: 'oxide-dark',
        theme: 'silver',
        theme_url: '/tinymce/themes/silver/theme.min.js',
        base_url: '/tinymce',
        suffix: '.min',
        plugins: 'image,wordcount,charmap,code,hr,lists,advlist,emoticons,fullscreen,help,insertdatetime,link,imageUpload',
        setup: function (editor) {
  mounted () {
    var that = this
    this.init.clickImage = function (editor) {
      that.visible = true
  methods: {
    confirm (img) {
      if (Array.isArray(img) && img.length > 0) {
        img.forEach((item, index) => {
          // 光标放最后?
          this.editor.execCommand('selectAll')
          this.editor.selection.getRng().collapse(false)
          this.editor.focus()
          var html = '<div><img src="' + item + '"/></div>'
          this.editor.insertContent(html)
    onReady (e) {