TinyMce-Vue rich text integration to realize picture and video upload function

The rich text editor of the system was Baidu's UEditor before , but the plug-in has been out of maintenance for many years, and there are development risks, so TinyMce is used to replace and expand business functions.

1. Demo demonstration

2. Packaging steps

1. Install tinymce-vue

Note: Vue 2.x downloads the 3.x version of tinymce-vue, Vue 3 downloads the 4.x version of tinymce-vue, this article is a Vue 2.x project

yarn add @tinymce/[email protected]

2. Package font type configuration

const fontFormats = `
  微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;
  宋体=simsun,serif;
  苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;
  Arial=arial,helvetica,sans-serif;
  Arial Black=arial black,avant garde;
  Book Antiqua=book antiqua,palatino;
  Comic Sans MS=comic sans ms,sans-serif;
  Courier New=courier new,courier;
  Georgia=georgia,palatino;
  Helvetica=helvetica;
  Symbol=symbol;
  Tahoma=tahoma,arial,helvetica,sans-serif;
  Terminal=terminal,monaco;
  Times New Roman=times new roman,times;
  Verdana=verdana,geneva;
  `

export default fontFormats

3. Encapsulation plug-in configuration

// Any plugins you want to use has to be imported
// Deatil plugins list see https://www.tiny.cloud/docs/plugins/

const plugins = [
  `advlist autolink lists link image charmap print preview anchor,
  searchreplace visualblocks code fullscreen,
  insertdatetime media table paste help wordcount image`
]

export default plugins

4. Package toolbar configuration

// Here is a list of the toolbar
// Detail list see https://www.tiny.cloud/docs/general-configuration-guide/basic-setup/#toolbarconfiguration

const toolbars = `code | fontselect fontsizeselect | undo redo | cut copy paste | bold italic forecolor backcolor |
                  alignleft aligncenter alignright alignjustify | formatselect |
                  bullist numlist outdent indent | removeformat | image media | fullscreen preview | help`

export default toolbars

5. Apply for Tiny API Key

Enter the official website , register and log in , and obtain the API Key according to the prompts

6. Encapsulate and upload file method

/**
 * @description 上传文件
 * @param {File} file - 要上传的文件
 * @param {string} folder - 所存放的文件夹
 * @returns {Object}
 */
async uploadFile (file, folder = 'images') {
    
    
  const formData = new FormData()
  formData.append('file', file)
  formData.append('folder', folder)

  // 注:此为调用后端上传接口,需根据实际情况进行调整
  const {
    
     data } = await axios({
    
    
    method: 'POST',
    url: '/api/v1/upload',
    data: formData,
    headers: {
    
     'Content-Type': 'multipart/form-data' }
  })

  return {
    
    
    url: data.url,
    name: file.name
  }
}

7. Custom image upload function

images_upload_handler: async (blobInfo, successFun) => {
    
    
  const file = blobInfo.blob()
  const {
    
     url } = await this.uploadFile(file, 'image')
  successFun(url)
}

8. Custom file upload function

file_picker_callback: (callback, value, meta) => {
    
    
  if (meta.filetype === 'media') {
    
    
    const input = document.createElement('input')
    input.setAttribute('type', 'file')
    const that = this // 为 Vue 构造函数中的 this,指向 Vue 实例对象
    input.onchange = async function () {
    
    
      const file = this.files[0] // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
      const isValid = await that.validateVideo(file)

      if (isValid) {
    
    
        const {
    
     url } = await that.uploadFile(file, 'video')
        callback(url)
      } else {
    
    
        callback()
      }
    }

    input.click()
  }
}

9. Customize and insert video code

video_template_callback: data => {
    
    
  return `<video width="745" height="420" controls="controls" src=${
      
      data.source} />`
}

3. Complete package code

<template>
  <editor
    id="editor"
    v-model="content"
    :api-key="apiKey"
    :init="initConfig"
  />
</template>

<script>
import axios from 'axios'
import plugins from './plugins'
import toolbar from './toolbar'
import fontFormats from './fontFormats'
import Editor from '@tinymce/tinymce-vue'

const defaultConfig = {
  width: 1000,
  height: 600,
  menubar: true,
  language: 'zh_CN'
}

const apiKey = 'aaiu8u7yrq204xloul2q92mi0sdaneml86evmnvcrj0e3dqa'

export default {
  name: 'TinyMce',
  components: {
    editor: Editor
  },
  props: {
    value: {
      type: String,
      default: ''
    },
    config: {
      type: Object,
      default: () => {
        return {
          width: 1000,
          height: 600,
          menubar: true,
          language: 'zh_CN'
        }
      }
    }
  },
  data () {
    return {
      apiKey,
      content: '',
      initConfig: {
        plugins,
        toolbar,
        width: Object.assign(defaultConfig, this.config).width,
        height: Object.assign(defaultConfig, this.config).height,
        menubar: Object.assign(defaultConfig, this.config).menubar,
        language: Object.assign(defaultConfig, this.config).language,
        font_formats: fontFormats,
        images_upload_handler: async (blobInfo, successFun) => {
          const file = blobInfo.blob()
          const { url } = await this.uploadFile(file, 'image')
          successFun(url)
        },
        file_picker_types: 'media',
        file_picker_callback: (callback, value, meta) => {
          if (meta.filetype === 'media') {
            const input = document.createElement('input')
            input.setAttribute('type', 'file')
            const that = this // 为 Vue 构造函数中的 this,指向 Vue 实例对象
            input.onchange = async function () {
              const file = this.files[0] // 为 HTMLInputElement 构造函数中的 this,指向 input 实例对象
              const isValid = await that.validateVideo(file)

              if (isValid) {
                const { url } = await that.uploadFile(file, 'video')
                callback(url)
              } else {
                callback()
              }
            }

            input.click()
          }
        },
        video_template_callback: data => {
          return `<video width="745" height="420" controls="controls" src=${data.source} />`
        }
      }
    }
  },
  watch: {
    value: {
      handler (newValue) {
        if (newValue !== '') {
          this.content = newValue
        }
      },
      immediate: true
    }
  },
  methods: {
    /**
     * @description 获取富文本内容(注:供父组件调用)
     * @returns {string}
     */
    getContent () {
      return this.content
    },

    /**
     * @description 校验上传视频
     * @param {File} file - 要上传的文件
     * @returns {boolean}
     */
    async validateVideo (file) {
      const isMP4 = file.type === 'video/mp4'
      const isLt3M = file.size / 1024 / 1024 < 3

      if (!isMP4) {
        this.$message.error('上传视频必须为 MP4 格式!')

        return false
      }

      if (!isLt3M) {
        this.$message.error('上传视频大小限制 3M 以内!')

        return false
      }

      const duration = await this.getVideoDuration(file)
      if (duration > 60) {
        this.$message.error('上传视频时长不能超过 60 秒!')

        return false
      }

      return true
    },

    /**
     * @description 获取视频时长
     * @param {File} file - 要上传的文件
     * @returns {Promise<number>}
     */
    getVideoDuration (file) {
      return new Promise(resolve => {
        const videoElement = document.createElement('video')
        videoElement.src = URL.createObjectURL(file)

        videoElement.addEventListener('loadedmetadata', () => {
          resolve(videoElement.duration)
        })
      })
    },

    /**
     * @description 上传文件
     * @param {File} file - 要上传的文件
     * @param {string} folder - 所存放的文件夹
     * @returns {Object}
     */
    async uploadFile (file, folder = 'images') {
      const formData = new FormData()
      formData.append('file', file)
      formData.append('folder', folder)

      // 注:此为调用后端上传接口,需根据实际情况进行调整
      const { data } = await axios({
        method: 'POST',
        url: '/api/v1/upload',
        data: formData,
        headers: { 'Content-Type': 'multipart/form-data' }
      })

      return {
        url: data.url,
        name: file.name
      }
    }
  }
}
</script>

4. Use components

5. Node upload interface reference

For more details, please refer to my article egg-oss upload pictures

Guess you like

Origin blog.csdn.net/qq_41548644/article/details/121429783