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文件夹下 ,如图
vue后台管理系统富文本组件(一)tinymce_第1张图片

需要语言包的话,单独下载zh_CN.js文件放在tinymce_5.0.8文件夹下的lang文件夹下

zh_CN.js官网下载地址

2.在入口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>titletitle>
  head>
  <body>
    <script src="<%= BASE_URL %>static/tinymce_5.0.8/tinymce.min.js">script>  
    <div id="app">div> 
  body>
html>

3.组件Tinymce

文件目录

vue后台管理系统富文本组件(一)tinymce_第2张图片

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 = `${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.使用效果
vue后台管理系统富文本组件(一)tinymce_第3张图片

参考链接

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

2.tinymce中文文档

你可能感兴趣的:(vue,element-ui,那些年写过的企业级中后台vue,react组件)