vue後臺管理系統富文本組件(一)tinymce

vue後臺管理系統富文本組件(一)tinymce

簡介

富文本組件作爲後臺管理系統的最重要的基礎組件之一,一定要選擇坑比較少的富文本廠家。這裏使用的是好看又坑少的tinymce。

主要依賴說明 (先安裝,步驟略)

 {
    "axios": "^0.18.0",
    "element-ui": "2.11.1",  
    "vue": "^2.6.10",    
    "vue-router": "^3.0.1"   
 }

tinymce:5.0.8 這裏直接採用下載源碼的方式,npm下載極大的影響打包速度,也可以使用cdn

tinymce官網下載地址

正文

1.下載tinymce源碼放在vue-cli 3生成的 public文件夾下的static文件夾下 ,如圖
在這裏插入圖片描述

需要語言包的話,單獨下載zh_CN.js文件放在tinymce_5.0.8文件夾下的lang文件夾下

zh_CN.js官網下載地址

2.在入口html文件中導入


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="renderer" content="webkit" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
    />
    <meta name="description" content="<%= webpackConfig.name %>" />
    <meta name="robots" content="noindex,nofollow,noarchive" /> 
    <title>title</title>
  </head>
  <body>
    <script src="<%= BASE_URL %>static/tinymce_5.0.8/tinymce.min.js"></script>  
    <div id="app"></div> 
  </body>
</html>

3.組件Tinymce.vue

文件目錄

在這裏插入圖片描述

src/components/Tinymce/index.vue

<template>
  <div class="tinymce-container editor-container" :class="{fullscreen:fullscreen}">
    <textarea :id="tinymceId" class="tinymce-textarea" />
    <div class="editor-custom-btn-container">
      <multiple-upload class="editor-upload-btn" @success="imageSuccess" />
    </div>
  </div>
</template>

<script>
import axios from 'axios'

 // MultipleUpload組件見 https://blog.csdn.net/qq_39953537/article/details/100039094

import MultipleUpload from '@/components/MultipleUpload'
import plugins from './plugins' // 見下文
import toolbar from './toolbar' // 見下文

// 上傳html片段接口根據自己項目更換
import { uploadHtml } from '@/api/upload'
export default {
  name: 'Tinymce',
  components: {
    MultipleUpload
  },
  props: {
    // 默認填充到富文本的html文件
    html: {
      type: String,
      default: ''
    },
    toolbar: {
      type: Array,
      default() {
        return []
      }
    },
    menubar: {
      type: Boolean,
      default: false
    },
    height: {
      type: Number,
      default: 400
    }
  },
  data() {
    return {
      hasChange: false,
      hasInit: false,
      tinymceId: 'vue-tinymce-' + +new Date(),
      fullscreen: false,
      value: '',
      editorContent: ''
    }
  },
  watch: {
    value(val) {
      this.$nextTick(() =>
        window.tinymce.get(this.tinymceId).setContent(val || '')
      )
    },
    html(val) {
      if (this.isUrl) {
        this.loadUrl(val)
      }
    }
  },
  created() {
    if (this.html && this.html.startsWith('http')) {
      this.loadUrl(this.html)
    } else {
      this.value = this.html + ''
      this.editorContent = this.html + ''
    }
  },
  mounted() {
    this.initTinymce()
  },
  activated() {
    this.initTinymce()
  },
  deactivated() {
    this.destroyTinymce()
  },
  destroyed() {
    this.destroyTinymce()
  },
  methods: {
    initTinymce() {
      window.tinymce.init({
        fontsize_formats: '12px 14px 16px 18px 20px 24px 36px',
        language: 'zh_CN',
        language_url: '/static/tinymce_5.0.8/langs/zh_CN.js',
        selector: `#${this.tinymceId}`,
        height: this.height,
        body_class: 'panel-body ',
        object_resizing: true,
        toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
        menubar: this.menubar,
        plugins: plugins,
        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',
        default_link_target: '_blank',
        link_title: false,
        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())
            this.editorContent = editor.getContent()
          })
        },
        setup(editor) {
          editor.on('FullscreenStateChanged', e => {
            this.fullscreen = e.state
          })
        }
      })
    },
    destroyTinymce() {
      if (window.tinymce.get(this.tinymceId)) {
        window.tinymce.get(this.tinymceId).destroy()
      }
    },
    loadUrl(url) {
      if (url && url.length > 0) {
        axios
          .get(url)
          .then(response => {
            // 處理HTML顯示
            this.value = response.data
            this.editorContent = response.data
            this.$emit('subLoadUrlToHtml', response.data)
            this.$emit('input', response.data)
          })
          .catch(() => {
            this.value = '服務器數據加載失敗,請重試!'
          })
      }
    },
    // 設置編輯器內容
    setContent(value) {
      window.tinymce.get(this.tinymceId).setContent(value)
    },
    // 獲取編輯器內容
    getContent() {
      window.tinymce.get(this.tinymceId).getContent()
    },
    // 圖片上傳成功後填充到富文本編輯器
    async imageSuccess(urlList) {
      try {
        let imageTemplateList = ''
        urlList.forEach(item => {
          const image = `<img style="max-width:100%;" src="${item}">`
          imageTemplateList = imageTemplateList + image
        })
        window.tinymce.get(this.tinymceId).insertContent(imageTemplateList)
        this.$message({
          message: '上傳成功!',
          type: 'success'
        })
      } catch (error) {
        console.log(error)
        this.$message({
          message: error,
          type: 'error'
        })
      }
    },
    // 編輯器內容上傳到cos,調用返回url
    async content2Url() {
      try {
        const res = await uploadHtml(this.editorContent)
        return res
      } catch (error) {
        this.$message({
          message: error.data.message,
          type: 'error'
        })
      }
    }
  }
}
</script>

<style lang='scss' >
#tinymce {
  background-color: blue;
  p {
    margin: 0;
  }
}
.tinymce-container {
  position: relative;
}
.tinymce-container >>> .mce-fullscreen {
  z-index: 10000;
}
.tinymce-textarea {
  visibility: hidden;
  z-index: -1;
}
.editor-custom-btn-container {
  position: absolute;
  right: 4px;
  top: 4px;
  /*z-index: 2005;*/
}
.fullscreen .editor-custom-btn-container {
  z-index: 10000;
  position: fixed;
}
.editor-upload-btn {
  display: inline-block;
}
// 隱藏底部logo欄
.mce-edit-area + .mce-statusbar {
  opacity: 0;
  height: 0;
}
</style>



src/components/Tinymce/plugins.js

const plugins = [
  'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak  preview print save searchreplace spellchecker tabfocus table template  textpattern visualblocks visualchars wordcount paste'
]

export default plugins

src/components/Tinymce/toolbar.js

const toolbar = ['formatselect fontsizeselect forecolor backcolor bold italic underline strikethrough alignleft aligncenter alignright outdent indent    removeformat  hr undo redo']

export default toolbar

4.使用

<template>
    <div>
        <tinymce
        ref="tinymce"
        :height="500"
        :html="html"
        @input="getContent"
        />
    </div>
</template>

<script>
import AppCropper from '@/components/Cropper'
export default {
  name: 'GoodsForm',
  components: {
    AppCropper
  },
  data() {
    return {
      html: 'https://ebusiness-1255313385.cosbj.myqcloud.com/image/20190823/center2019082304054532.html',
      content:''
    }
  },
  methods: {
      // 獲取編輯器內容
    getContent(content) {
      this.content = content
    },
    // 編輯器內容轉換成在線url
    async getcontent2Url() {
      try {
        const htmlUrl =  await this.$refs.tinymce.content2Url()
        return htmlUrl
      } catch (error) {
        console.log(error)
      }
    }

  }
}
</script>
 

5.使用效果
在這裏插入圖片描述

參考鏈接

1.https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/Tinymce/index.vue

2.tinymce中文文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章