关于tinymce富文本组件的一些总结

简介: 项目中初次用tinymce的时候遇到一大推问题,感觉这个组件太庞大,太笨重,尤其是网站反应那个慢啊,转半天反应不过来,api多的类找都找不到,现在都觉得很难驾驭它,下面是项目中所用到的api的整理: 项目目录: index.
项目中初次用tinymce的时候遇到一大推问题,感觉这个组件太庞大,太笨重,尤其是网站反应那个慢啊,转半天反应不过来,api多的类找都找不到,现在都觉得很难驾驭它,下面是项目中所用到的api的整理:

项目目录:
image

index.vue 对原来的Tinymce组件做了稍许修改


<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\'>请输入内容(限2000个字符)!</p>')
          }
        })
        editor.on('focus', function () {
          editor.setContent(_this.value)
        })
      }
      // 整合七牛上传
      // 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('出现未知问题,刷新页面,或者联系程序员')
      //     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 和 toolbar.js里面放的是插件,可以对里面的插件增加和删除不必要的插件;editorImage.vue是上传本地图片插件,但是不能上传黏贴过来的图片

editorImage.vue


<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary"
           @click=" dialogVisible=true">上传图片
</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">点击上传</el-button>
  </el-upload>
  <el-button @click="dialogVisible = false">取 消</el-button>
  <el-button type="primary" @click="handleSubmit">确 定</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('请耐心等待所有图片上传成功!')
      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
      }
    }
  },
  /**上传文件之前的钩子(参数为上传的文件)**/
  beforeUpload (file) {
    const _URL = window.URL || window.webkitURL
    const fileName = file.uid
    this.listObj[fileName] = {}
    //图片类型和大小判断
    let reg = new RegExp('[.jpg|.png]$')
    if (Math.floor(file.size / 1024 / 1024) < 2) {
      if (file && reg.test(file.name)) {
      } else {
        this.$message.error('图片格式不正确,请上传JPG或PNG格式的图片!')
        return false
      }
    } else {
      this.$message.error('只能上传小于2MB的图片!')
      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) {}//文件超出个数限制时的钩子
}

}

项目中遇到的问题:

1、无法黏贴截图过来的图片

2、无法在富文本内部添加placeholder占位符

第一个问题:

解决第一个问题主要用到的是tinymce组件的paste_data_images属性,Boolean类型,为true时允许黏贴图片,false不允许黏贴图片

第二个问题:

解决第二个问题用到的就是setup方法,再利用编辑器事件的init事件给文本框设置占位符,就是在富文本编辑器初始渲染的时候添加placeholder,即:

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

 if (editor.getContent() === '') {
      editor.setContent('<p id=\'#imThePlaceholder\'>请输入内容(限2000个字符)!</p>')
 }

})
再利用当focus事件时清除初始化时的placeholder的内容

editor.on('focus', function () {
          editor.setContent(_this.value)
        })
目录
相关文章
|
6月前
|
JavaScript
安装富文本组件
安装富文本组件
|
6月前
|
JavaScript
Vue项目中使用wangEditor富文本输入框(推荐)
Vue项目中使用wangEditor富文本输入框(推荐)
vue3 wangEditor富文本自定义上传本地图片
Vue3和WangEditor都提供了上传本地图片的功能,可以结合使用实现自定义上传本地图片。
1230 0
|
存储 JavaScript 前端开发
【前端系列】- 富文本组件(mavon-editor)
mavon-editor是一款基于vue的markdown编辑器,可以用来做文本的编辑,比如是某种业务需要发送公告、个人博客等,都可以用到,操作也十分简单。
4028 0
【前端系列】- 富文本组件(mavon-editor)
|
JavaScript
VUE~富文本简单使用~tinymce
VUE~富文本简单使用~tinymce
865 0
VUE~富文本简单使用~tinymce
|
6月前
|
JavaScript
Vue在Element UI下使用富文本框插件quill-editor(我个人不推荐用这个复杂的富文本插件)
Vue在Element UI下使用富文本框插件quill-editor(我个人不推荐用这个复杂的富文本插件)
|
4月前
|
JavaScript 前端开发
vue 富文本编辑器 quill (含代码高亮、自定义字体、汉化、鼠标悬浮提示、组件封装等)
vue 富文本编辑器 quill (含代码高亮、自定义字体、汉化、鼠标悬浮提示、组件封装等)
232 0
|
6月前
|
对象存储
wangEditor 富文本详解(中)
wangEditor 富文本详解(中)
107 1
|
6月前
|
前端开发 数据可视化 API
前端react 18.2整合ckeditor富文本编辑器——配置插件、自定义toolbar工具栏(二)
前端react 18.2整合ckeditor富文本编辑器——配置插件、自定义toolbar工具栏
421 0
前端react 18.2整合ckeditor富文本编辑器——配置插件、自定义toolbar工具栏(二)