Summary of tinymce rich text component

When using tinymce for the first time in the project, I encountered a big push problem. I felt that this component was too large and cumbersome, especially the slow response of the website. I couldn't respond in half a day, and I couldn't find many classes with APIs. Now I feel it's difficult to control it. Here is the sorting of the APIs used in the project:

Project directory:

index.vue slightly modified the original Tinymce component


<textarea :id="tinymceId" class="tinymce-textarea"/>
<div class="editor-custom-btn-container">
  <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"/>
</div>


import editorImage from './components/editorImage'
import plugins from './plugins'
import toolbar from './toolbar'
import emitter from 'element-ui/src/mixins/emitter'

export default {

name: 'Tinymce',
mixins: [emitter],
components: { editorImage },
props: {
  id: {
    type: String,
    default: function () {
      return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
    }
  },
  value: {
    type: String,
    default: ''
  },
  toolbar: {
    type: Array,
    required: false,
    default () {
      return []
    }
  },
  menubar: {
    type: String,
    default: 'file edit insert view format table'
  },
  height: {
    type: Number,
    required: false,
    default: 50
  },
  pasteImages: {
    type: Boolean,
    default: false
  }
},
data () {
  return {
    hasChange: false,
    hasInit: false,
    tinymceId: this.id,
    fullscreen: false,
    languageTypeList: {
      'en': 'en',
      'zh': 'zh_CN'
    }
  }
},
computed: {
  language () {
    return this.languageTypeList[this.$store.getters.language]
  }
},
watch: {
  value (val) {
    try {
      if (this.value !== this.getContent()) {
        this.$nextTick(() =>
          window.tinymce.get(this.tinymceId).setContent(val || ''))
      }
    } catch (e) {
    }
  },
  language () {
    this.destroyTinymce()
    this.$nextTick(() => this.initTinymce())
  }
},
mounted () {
  this.initTinymce()
},
activated () {
  this.initTinymce()
},
deactivated () {
  this.destroyTinymce()
},
destroyed () {
  this.destroyTinymce()
},
methods: {
  initTinymce () {
    const _this = this
    window.tinymce.init({
      language: this.language,
      selector: `#${this.tinymceId}`,
      height: this.height,
      body_class: 'panel-body ',
      object_resizing: false,
      toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
      menubar: this.menubar,
      plugins: plugins,
      contextmenu: 'link',
      end_container_on_empty_block: true,
      powerpaste_word_import: 'clean',
      code_dialog_height: 450,
      code_dialog_width: 1000,
      advlist_bullet_styles: 'square',
      advlist_number_styles: 'default',
      imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
      default_link_target: '_blank',
      link_title: false,
      paste_data_images: this.pasteImages,
      nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
      init_instance_callback: editor => {
        if (_this.value) {
          editor.setContent(_this.value)
        }
        _this.hasInit = true
        editor.on('NodeChange Change KeyUp SetContent', () => {
          this.hasChange = true
          this.$emit('input', editor.getContent())
        })
        editor.on('blur', () => {
          _this.dispatch('ElFormItem', 'el.form.blur', [editor.getContent()])
        })
      },
      setup (editor) {
        editor.on('FullscreenStateChanged', (e) => {
          _this.fullscreen = e.state
        })
        editor.on('init', function () {
          if (editor.getContent() === '') {
            editor.setContent('<p id=\'#Imtheplaceholder \ '> please input content (limited to 2000 characters)! </p>')
          }
        })
        editor.on('focus', function () {
          editor.setContent(_this.value)
        })
      }
      // Integrate seven cattle upload
      // images_dataimg_filter(img) {
      //   setTimeout(() => {
      //     const $image = $(img);
      //     $image.removeAttr('width');
      //     $image.removeAttr('height');
      //     if ($image[0].height && $image[0].width) {
      //       $image.attr('data-wscntype', 'image');
      //       $image.attr('data-wscnh', $image[0].height);
      //       $image.attr('data-wscnw', $image[0].width);
      //       $image.addClass('wscnph');
      //     }
      //   }, 0);
      //   return img
      // },
      // images_upload_handler(blobInfo, success, failure, progress) {
      //   progress(0);
      //   const token = _this.$store.getters.token;
      //   getToken(token).then(response => {
      //     const url = response.data.qiniu_url;
      //     const formData = new FormData();
      //     formData.append('token', response.data.qiniu_token);
      //     formData.append('key', response.data.qiniu_key);
      //     formData.append('file', blobInfo.blob(), url);
      //     upload(formData).then(() => {
      //       success(url);
      //       progress(100);
      //     })
      //   }).catch(err => {
      //     failure('unknown problem, refresh page, or contact programmer ')
      //     console.log(err);
      //   });
      // },
    })
  },
  destroyTinymce () {
    try {
      if (window.tinymce.get(this.tinymceId) && window.tinymce.get(this.tinymceId) != null) {
        window.tinymce.get(this.tinymceId).destroy()
      }
    } catch (e) {}
  },
  setContent (value) {
    window.tinymce.get(this.tinymceId).setContent(value)
  },
  getContent () {
    return window.tinymce.get(this.tinymceId).getContent()
  },
  imageSuccessCBK (arr) {
    const _this = this
    arr.forEach(v => {
      window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
    })
  }
}

}

plugins.js and toolbar.js contain plug-ins, which can add and delete unnecessary plug-ins; editorImage.vue is a plug-in for uploading local images, but cannot upload pasted images

editorImage.vue


<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary"
           @click=" dialogVisible=true">Upload pictures
</el-button>
<el-dialog :visible.sync="dialogVisible" append-to-body>
  <el-upload
          :multiple="true"
          :file-list="fileList"
          :show-file-list="true"
          :on-remove="handleRemove"
          :on-success="handleSuccess"
          :before-upload="beforeUpload"
          :on-exceed="handleExceed"
          class="editor-slide-upload"
          action="https://httpbin.org/post"
          list-type="picture-card">
    <el-button size="mini" type="primary">Click upload</el-button>
  </el-upload>
  <el-button @click="dialogVisible = false">Cancellation</el-button>
  <el-button type="primary" @click="handleSubmit">Determine</el-button>
</el-dialog>


// import { getToken } from 'api/qiniu'

export default {

name: 'EditorSlideUpload',
props: {
  color: {
    type: String,
    default: '#1890ff'
  }
},
data () {
  return {
    dialogVisible: false,
    listObj: {},
    fileList: []
  }
},
methods: {
  checkAllSuccess () {
    return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
  },
  handleSubmit () {
    const arr = Object.keys(this.listObj).map(v => this.listObj[v])
    if (!this.checkAllSuccess()) {
      this.$message('Please wait patiently for all pictures to be uploaded successfully!')
      return
    }
    this.$emit('successCBK', arr)
    this.listObj = {}
    this.fileList = []
    this.dialogVisible = false
  },
  handleSuccess (response, file) {
    const uid = file.uid
    const objKeyArr = Object.keys(this.listObj)
    for (let i = 0, len = objKeyArr.length; i < len; i++) {
      if (this.listObj[objKeyArr[i]].uid === uid) {
        this.listObj[objKeyArr[i]].url = response.files.file
        this.listObj[objKeyArr[i]].hasSuccess = true
        return
      }
    }
  },
  handleRemove (file) {
    const uid = file.uid
    const objKeyArr = Object.keys(this.listObj)
    for (let i = 0, len = objKeyArr.length; i < len; i++) {
      if (this.listObj[objKeyArr[i]].uid === uid) {
        delete this.listObj[objKeyArr[i]]
        return
      }
    }
  },
  /**Hook before uploading the file (the parameter is the uploaded file)**/
  beforeUpload (file) {
    const _URL = window.URL || window.webkitURL
    const fileName = file.uid
    this.listObj[fileName] = {}
    //Picture type and size judgment
    let reg = new RegExp('[.jpg|.png]$')
    if (Math.floor(file.size / 1024 / 1024) < 2) {
      if (file && reg.test(file.name)) {
      } else {
        this.$message.error('The image format is incorrect, please upload JPG or PNG Format picture!')
        return false
      }
    } else {
      this.$message.error('Only upload less than 2 MB Picture!')
      return false
    }
    return new Promise((resolve, reject) => {
      const img = new Image()
      img.src = _URL.createObjectURL(file)
      if (img) {
        img.onload = () => {
          this.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
        }
      }
      resolve(true)
    })
  },
  handleExceed (files, fileList) {}//Hook when the number of files exceeds the limit
}

}

Problems encountered in the project:

1. Unable to paste the pictures from the screenshot

2. Cannot add placeholder inside rich text

First question:

To solve the first problem, we mainly use the paste ﹣ data ﹣ images attribute of tinymce component, Boolean type. If it is true, it is allowed to paste pictures. If it is false, it is not allowed to paste pictures

Second question:

To solve the second problem, we use the setup method, and then use the init event of the editor event to set placeholders for the text box, that is, when the rich text editor is initially rendered, we add placeholders, that is:

editor.on('init', function () {

 if (editor.getContent() === '') {
      editor.setContent('<p id=\'#Imtheplaceholder \ '> please input content (limited to 2000 characters)! </p>')
 }

})
Reuse the contents of the placeholder when the focus event is cleared

editor.on('focus', function () {
          editor.setContent(_this.value)
        })

Keywords: Front-end Vue less Attribute

Added by noisyscanner on Fri, 08 Nov 2019 20:43:23 +0200