美文网首页Vue前端后端砖头
vue-quill-editor 富文本编辑器封装,可上传图片,

vue-quill-editor 富文本编辑器封装,可上传图片,

作者: 阿巳交不起水电费 | 来源:发表于2022-01-30 14:45 被阅读0次

    组件说明:
    (1)支持图片上传到服务器,也可使用base64的方式
    (2)支持视频上传,或者插入视频链接
    【其实无论是上传图片到服务器还是上传视频到服务器,其实本质上都是上传文件然后后端返回一个地址插入到编辑器中】
    (3)当前版本为vue 2.x + element ui,暂不支持vue 3.0

    1. 安装
    npm install vue-quill-editor --save
    

    2.引入下面我封装的组件

    <!--富文本编辑器-->
    <template>
      <div class="RichTextEditor-Wrap" v-loading="loading">
    
        <quill-editor :content="content"
                      :options="editorOption"
                      class="ql-editor"
                      ref="myQuillEditor"
                      @change="onEditorChange($event)">
        </quill-editor>
    
        <!-- 图片上传组件辅助-->
        <el-upload
          v-show="false"
          :show-file-list="false"
          :name="uploadImgConfig.name"
          :multiple="false"
          :action="uploadImgConfig.uploadUrl"
          :before-upload="onBeforeUpload"
          :on-success="onSuccess"
          :on-error="onError"
          :file-list="fileList">
          <!--<i class="el-icon-upload"></i>-->
          <!--<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>-->
          <!--<div class="el-upload__tip" slot="tip">最多只能上传两个附件</div>-->
          <button ref="myinput">上传文件</button>
        </el-upload>
    
        <!--视频上传-->
        <div>
          <el-dialog
            :close-on-click-modal="false"
            width="50%"
            style="margin-top: 1px"
            title="视频上传"
            :visible.sync="videoDialog.show"
            append-to-body>
            <el-tabs v-model="videoDialog.activeName">
              <el-tab-pane label="添加视频链接" name="first">
                <el-input v-model="videoDialog.videoLink" placeholder="请输入视频链接" clearable></el-input>
                <el-button type="primary" size="small" style="margin: 20px 0px 0px 0px "
                           @click="addVideoLink(videoDialog.videoLink)">添加
                </el-button>
              </el-tab-pane>
              <el-tab-pane label="本地视频上传" name="second">
                <el-upload
                  v-loading="loading"
                  style="text-align: center;"
                  drag
                  :action="uploadVideoConfig.uploadUrl"
                  accept="video/*"
                  :name="uploadVideoConfig.name"
                  :before-upload="onBeforeUploadVideo"
                  :on-success="onSuccessVideo"
                  :on-error="onErrorVideo"
                  :multiple="false">
                  <i class="el-icon-upload"></i>
                  <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                  <div class="el-upload__tip" slot="tip">只能上传MP4文件,且不超过{{uploadVideoConfig.maxSize}}M</div>
                </el-upload>
    
              </el-tab-pane>
            </el-tabs>
          </el-dialog>
        </div>
      </div>
    </template>
    <script>
      // require styles
      import 'quill/dist/quill.core.css'
      import 'quill/dist/quill.snow.css'
      import 'quill/dist/quill.bubble.css'
      import {quillEditor} from 'vue-quill-editor'
      // 设置title
      import {addQuillTitle} from './quill-title.js'
    
      // 工具栏
      const toolbarOptions = [
        ['bold', 'italic', 'underline', 'strike'], // toggled buttons
        ['blockquote', 'code-block'],
        [{'header': 1}, {'header': 2}],
        [{'list': 'ordered'}, {'list': 'bullet'}],
        [{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
        [{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
        [{'direction': 'rtl'}],
        [{'size': ['small', false, 'large', 'huge']}],
        [{'header': [1, 2, 3, 4, 5, 6, false]}],
        [{'font': []}],
        [{'color': []}, {'background': []}], // dropdown with defaults from theme
        [{'align': []}],
        [{'clean': '清除'}], // remove formatting button
        // ['link', 'image', 'video']
        ['image', 'video']
      ]
      export default {
        name: 'RichTextEditor',
        model: {
          prop: 'content',
          event: 'change'
        },
        components: {
          quillEditor
        },
        props: {
          content: { // 返回的html片段
            type: String,
            default: ''
          },
          uploadImgConfig: { // 图片上传配置 - 若不配置则使用quillEditor默认方式,即base64方式
            type: Object,
            default(){
              return {
                uploadUrl: '', // 图片上传地址
                maxSize: 2, // 图片上传大小限制,默认不超过2M
                name: 'Filedata' // 图片上传字段
              }
            }
          },
          uploadVideoConfig: { // 视频上传配置
            type: Object,
            default(){
              return {
                uploadUrl: '', // 上传地址
                maxSize: 10, // 图片上传大小限制,默认不超过2M
                name: 'Filedata' // 图片上传字段
              }
            }
          }
        },
        data() {
          let _self = this;
          return {
            loading: false, // 加载loading
            editorOption: {
              placeholder: '',
              theme: 'snow', // or 'bubble'
              modules: {
                toolbar: {
                  container: toolbarOptions, // 工具栏
                  handlers: {
                    'video': function (value) {
                      _self.videoDialog.show = true;
                    }
                  }
                }
              }
            },
    
            // 图片上传变量
            fileList: [],
    
            // 视频上传变量
            videoDialog: {
              show: false,
              videoLink: '',
              activeName: 'first'
            }
          }
        },
        mounted () {
          // 初始给编辑器设置title
          addQuillTitle()
    
          let toolbar = this.$refs['myQuillEditor'].quill.getModule('toolbar');
          // 是否开启图片上传到服务器功能
          if (this.uploadImgConfig.uploadUrl) {
            toolbar.addHandler('image', this.addImageHandler);
          }
    
        },
        methods: {
          // 文本编辑
          onEditorChange ({quill, html, text}) {
            // console.log('editor change!', quill, html, text)
            // console.log(html.replace(/<[^>]*>|/g, ''), 33333333)
            this.$emit('update:content', html)
            this.$emit('change', html)
          },
          hideLoading(){
            this.loading = false
          },
          // --------- 图片上传相关 start ---------
    
          addImageHandler(value){
            if (value) {
              // 触发input框选择图片文件
              this.$refs['myinput'].click();
            } else {
              this.quill.format('image', false)
            }
          },
          // 把已经上传的图片显示回富文本编辑框中
          uploadSuccess (imgurl) {
            let quill = this.$refs['myQuillEditor'].quill
            let range = quill.getSelection()
            let index = 0;
            if (range == null) {
              index = 0;
            } else {
              index = range.index; // 获取光标所在位置
            }
            // 插入
            quill.insertEmbed(index, 'image', imgurl) // imgurl是服务器返回的图片链接地址
            // 调整光标到最后
            quill.setSelection(index + 1)
          },
          // el-文件上传组件
          onBeforeUpload (file) {
            this.loading = true
            let acceptArr = ['image/jpg', 'image/jpeg', 'image/gif', 'image/png']
            const isIMAGE = acceptArr.includes(file.type)
            const isLt1M = file.size / 1024 / 1024 < this.uploadImgConfig.maxSize
            if (!isIMAGE) {
              this.hideLoading()
              this.$message.error('只能插入图片格式!')
            }
            if (!isLt1M) {
              this.hideLoading()
              this.$message.error(`上传文件大小不能超过 ${this.uploadImgConfig.maxSize}MB!`)
            }
            return isLt1M && isIMAGE
          },
          // 文件上传成功时的钩子
          onSuccess (response, file, fileList) { // ---- 注意这部分需要改为对应的返回格式
            this.hideLoading()
            if (response.retCode === '00') {
              this.uploadSuccess(response.url)
            } else {
              this.$message.error('上传失败')
            }
          },
          // 文件上传失败时的钩子
          onError (file, fileList) {
            this.hideLoading()
            this.$message.error('上传失败')
          },
    
          // --------- 图片上传相关 end ---------
    
          // --------- 视频上传相关 start ---------
    
          addVideoLink(videoLink) {
            if (!videoLink) return this.$message.error('请输入视频地址')
            this.videoDialog.show = false
            let quill = this.$refs['myQuillEditor'].quill
            let range = quill.getSelection()
            let index = 0;
            if (range == null) {
              index = 0;
            } else {
              index = range.index;
            }
            // 插入
            quill.insertEmbed(index, 'video', videoLink)
            // 调整光标到最后
            quill.setSelection(index + 1)
          },
    
          // el-文件上传组件
          onBeforeUploadVideo (file) {
            this.loading = true
            let acceptArr = ['video/mp4']
            const isVideo = acceptArr.includes(file.type)
            const isLt1M = file.size / 1024 / 1024 < this.uploadVideoConfig.maxSize
            if (!isVideo) {
              this.hideLoading()
              this.$message.error('只能上传mp4格式文件!')
            }
            if (!isLt1M) {
              this.hideLoading()
              this.$message.error(`上传文件大小不能超过 ${this.uploadVideoConfig.maxSize}MB!`)
            }
            return isLt1M && isVideo
          },
          // 文件上传成功时的钩子
          onSuccessVideo (response, file, fileList) { // ---- 注意这部分需要改为对应的返回格式
            this.hideLoading()
            if (response.retCode === '00') {
              this.addVideoLink(response.url)
            } else {
              this.$message.error('上传失败')
            }
          },
          // 文件上传失败时的钩子
          onErrorVideo (file, fileList) {
            this.hideLoading()
            this.$message.error('上传失败')
          },
    
          // --------- 视频上传相关 end ---------
        }
      }
    </script>
    <style>
      .RichTextEditor-Wrap .ql-container {
        height: 300px;
      }
    
      .RichTextEditor-Wrap .ql-editor {
        padding: 0;
      }
    
      .RichTextEditor-Wrap .ql-tooltip {
        left: 5px !important;
      }
    </style>
    
    
    

    quill-title.js这个文件是拿来给工具栏设置title的,代码如下:

    const titleConfig = {
      'ql-bold': '加粗',
      'ql-font': '字体',
      'ql-code': '插入代码',
      'ql-italic': '斜体',
      'ql-link': '添加链接',
      'ql-color': '字体颜色',
      'ql-background': '背景颜色',
      'ql-size': '字体大小',
      'ql-strike': '删除线',
      'ql-script': '上标/下标',
      'ql-underline': '下划线',
      'ql-blockquote': '引用',
      'ql-header': '标题',
      'ql-indent': '缩进',
      'ql-list': '列表',
      'ql-align': '文本对齐',
      'ql-direction': '文本方向',
      'ql-code-block': '代码块',
      'ql-formula': '公式',
      'ql-image': '图片',
      'ql-video': '视频',
      'ql-clean': '清除字体样式'
    }
    
    export function addQuillTitle () {
      const oToolBar = document.querySelector('.ql-toolbar')
      const aButton = oToolBar.querySelectorAll('button')
      const aSelect = oToolBar.querySelectorAll('select')
      aButton.forEach(function (item) {
        if (item.className === 'ql-script') {
          item.value === 'sub' ? item.title = '下标' : item.title = '上标'
        } else if (item.className === 'ql-indent') {
          item.value === '+1' ? item.title = '向右缩进' : item.title = '向左缩进'
        } else {
          item.title = titleConfig[item.className]
        }
      })
      // 字体颜色和字体背景特殊处理,两个在相同的盒子
      aSelect.forEach(function (item) {
        if (item.className.indexOf('ql-background') > -1) {
          item.previousSibling.title = titleConfig['ql-background']
        } else if (item.className.indexOf('ql-color') > -1) {
          item.previousSibling.title = titleConfig['ql-color']
        } else {
          item.parentNode.title = titleConfig[item.className]
        }
      })
    }
    
    
    title.gif

    组件使用: 注意props,其中 uploadImgConfig 是针对上传图片的配置,如果不传则默认使用原始的base64方式,视频上传必须要有上传服务器的地址

    image.png
    组件复制过去后,还需更改下下面这部分代码,根据自己的接口返回值类型更改:
    image.png

    组件调用方式
    支持v-model、.sync方式。

    上传图片效果图:

    image.png

    上传视频效果图:

    image.png
    image.png

    最后: 对于组件中上传图片、视频的格式判断可自行更改,也可提取到props中,整个组件代码逻辑比较简单,大家自由发挥吧。

    组件中视频上传封装参考了这篇文章:
    https://zhuanlan.zhihu.com/p/108705388

    如果对你有帮助,点个赞支持下吧。

    相关文章

      网友评论

        本文标题:vue-quill-editor 富文本编辑器封装,可上传图片,

        本文链接:https://www.haomeiwen.com/subject/shbqkrtx.html