vue-quill-editor富文本编辑器(图片使用base64或自定义上传,最大字数限制等功能)

本文介绍了如何在Vue项目中使用vue-quill-editor富文本编辑器,包括图片处理(base64或自定义上传)以及设置最大字数限制等功能。首先,通过下载并配置vue.config.js文件进行初始设置,然后详细展示了代码实现过程。

前置操作:

1.下载:

    "quill": "^1.3.7",
    "quill-image-resize-module": "^3.0.0",
    "vue-quill-editor": "^3.0.6",

2.config配置— vue.config.js (脚手架3.x版本)

 chainWebpack (config) {
 	...
 	config.plugin('provide').use(webpack.ProvidePlugin, [
      {
        'window.Quill': 'quill'
      }
    ])
    ...
 }

3.具体代码实现

<template>
  <div>
    <quill-editor ref="myQuillEditor" v-model="content" class="ql-editor c333" :options="editorOption" @change="onEditorChange" />
    <div class="length_wr"> {{ contentLength }}/{{ maxLength }} </div>

    <el-upload class="none" :action="'/fhris/file/upload?t='+(+new Date())" accept="image/jpg, image/jpeg, image/png, image/gif" :show-file-list="false" :before-upload="ImgBeforeUpload" :on-success="ImgUploadSuccess" :on-error="ImgUploadError">
      <i class="el-icon-plus avatar-uploader-icon upload-editor-el" />
    </el-upload>
  </div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor' // 调用编辑器
import 'quill/dist/quill.snow.css' // 富文本编辑器外部引用样式
import { upload } from '@/utils/request'

// 扩展部分-调整图片大小
import * as Quill from 'quill' // 富文本基于quill
import resizeImage from 'quill-image-resize-module'
Quill.register('modules/imageResize', resizeImage)

export default {
  name: 'BaseQuillEditor',
  components: {
    quillEditor
  },
  props: {
    propsContent: { // 父组件传值回显
      type: String,
      default: ''
    },
    maxLength: { // 字数限制
      type: Number,
      default: 2000
    },
    placeholder: {
      type: String,
      default: '请填写相关说明、内容、原因等...'
    },
    isImgBase64: { // 图片是否采用base64
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      content: '',
      contentLength: 0, // 内容字数
      editorOption: {
        modules: {
          clipboard: this.isImgBase64 ? {} : {
            // 粘贴版,处理粘贴时候的自带样式
            matchers: [[Node.ELEMENT_NODE, this.HandleCustomMatcher]]
          },
          imageResize: { // 调整大小组件
            displayStyles: {
              backgroundColor: 'black',
              border: 'none',
              color: 'white'
            },
            modules: ['Resize', 'DisplaySize']
          },
          toolbar: {
            container: [ // 工具栏
              ['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
              [{ 'size': ['small', false, 'large', 'huge'] }], // 字体大小
              [{ 'list': 'ordered' }, { 'list': 'bullet' }], // 列表
              [{ 'indent': '-1' }, { 'indent': '+1' }], // 缩进
              [{ 'color': [] }, { 'background': [] }], // 字体颜色,字体背景颜色
              [{ 'align': [] }], // 对齐方式
              ['image'],
              ['clean'] // 清除字体样式
            ],
            handlers: this.isImgBase64 ? {} : {
              'image': (value) => {
                if (value) document.querySelector('.upload-editor-el').click() // 点击图片改: 调用图片上传
                else this.quill.format('image', false)
              }
            }
          }
        },
        theme: 'snow',
        placeholder: this.placeholder
      }
    }
  },
  watch: {
    propsContent (v, o) {
      this.content = v
      const length = this.$refs.myQuillEditor.quill.getLength() - 1 // 字符数
      if (this.$route.name.includes('Record') && !o && length !== 1) this.handleCursor() // 解决回显时光标位置错乱问题
    }
  },
  methods: {
    handleCursor () {
      this.$refs.myQuillEditor.quill.enable(false)
      setTimeout(() => {
        this.$refs.myQuillEditor.quill.enable(true)
      }, 0)
    },
    onEditorChange (e) {
      // 字数限制
      this.contentLength <= this.maxLength && this.$emit('getQuillEditorContent', e)
      e.quill.deleteText(this.maxLength, 1)
      this.contentLength = !this.content ? 0 : this.$refs.myQuillEditor.quill.getLength() - 1 // 默认getLength返回1,因为富文本默认有一个由\n表示的空白行
    },
    // 粘贴事件
    HandleCustomMatcher (node, Delta) {
      // 如果是粘贴图片 手动上传 (同步操作插件不支持,粘贴完成后再替换图片)
      if (node.src && node.src.includes('data:image/png;base64,')) {
        const file = this.dataURLtoBlob(node.src)
        const formData = new FormData()
        formData.append('file', file, new Date().getTime() + '.png')
        this.handleUploadIntoField(formData) // 异步 加入上传图片
        Delta.ops = [] // 清除掉粘贴内容
      }
      const length = Delta?.ops?.[0]?.insert?.length || 0
      if (length > 200) Delta.ops[0].insert = Delta?.ops?.[0]?.insert.substring(0, 2000) // 解决粘贴时字数超过限制的问题
      return Delta
    },
    // 粘贴上传图片
    handleUploadIntoField (file) {
      const options = { url: '/file/upload', method: 'post', data: file }
      const header = { 'Content-Type': undefined }
      upload(options, header).then(res => {
        if (!res.data) {
          this.ImgUploadError()
        } else {
          const quill = this.$refs.myQuillEditor.quill
          const length = quill.getSelection().index
          const imgUrl = window.location.origin + '/fhris/file/filePreview?id=' + res.data
          quill.insertEmbed(length, 'image', imgUrl)
          quill.setSelection(length + 1)
        }
      }, () => {
      })
    },
    dataURLtoBlob (dataurl) {
      const arr = dataurl.split(',')
      // 注意base64的最后面中括号和引号是不转译的
      const _arr = arr[1].substring(0, arr[1].length - 2)
      const mime = arr[0].match(/:(.*?);/)[1]
      const bstr = atob(_arr)
      let n = bstr.length
      const u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new Blob([u8arr], { type: mime })
    },
    // 编辑器内 图片上传前
    ImgBeforeUpload (file) {
      const isJPG = file.type === 'image/jpg' || file.type === 'image/png' || file.type === 'image/jpeg'
      const isLt1M = file.size / 1024 / 1024 < 10
      if (!isJPG) this.$message({ type: 'error', message: '支持JPG、PNG格式的图片,大小不得超过10M', showClose: true })
      if (!isLt1M) this.$message({ type: 'error', message: '文件最大不得超过10M', showClose: true })
      return isJPG && isLt1M
    },
    // 编辑器内 上传图片成功
    ImgUploadSuccess (res, file, list) {
      if (res.code + '' !== '200') {
        this.ImgUploadError()
      } else {
        const quill = this.$refs.myQuillEditor.quill
        const length = quill.getSelection().index
        const imgUrl = window.location.origin + '/fhris/file/filePreview?id=' + res.data
        quill.insertEmbed(length, 'image', imgUrl)
        quill.setSelection(length + 1)
      }
    },
    // 编辑器内 上传图片失败
    ImgUploadError (res, file, list) {
      this.$message({ type: 'error', message: '图片插入失败', showClose: true })
    }
  }
}
</script>
<style lang="scss" scoped>
.ql-editor {
  padding: 0;
  white-space: normal;
  >>> .ql-editor {
    min-height: 200px;
    padding-bottom: 30px;
  }
  >>> .ql-container {
    font-size: 14px;
    min-height: 210px;
    border-radius: 0 0 4px 4px;
    border-color: #dcdfe6;
  }
  >>> .ql-toolbar {
    border-radius: 4px 4px 0 0;
    border-color: #dcdfe6;
  }
  >>> .ql-blank::before {
    font-style: normal;
    color: #999;
  }
}
.length_wr {
  position: absolute;
  bottom: 0;
  right: 10px;
}
.none {
  display: none;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值