• Quill富文本的使用以及自定义图片和视频处理事件


    Quill富文本的使用    官网 https://quilljs.com/docs/quickstart/

    1、安装quill

    使用  mpn i quill -S 

    2、新建myquil.vue文件,内容如下

    <template>
      <div class="quill-editor">
        <slot name="toolbar"></slot>
        <div ref="editor"></div>
        <div id="editor"></div>
        <!-- 测试video标签 -->
        <!-- <video src="https://www.w3school.com.cn/i/movie.mp4" controls="controls" width="100%" height="100%" webkit-playsinline="true" playsinline="true" x5-playsinline="true"></video> -->
        <input id="uploadImg" ref="uploadImg" type="file" style="display:none" accept="image/jpeg, image/png"
          @change="uploadImage">
        <input id="uploadVideo" ref="uploadVideo" type="file" style="display:none" accept="video/*" @change="uploadVideo">
      </div>
    </template>
    
    <script>
    // require sources
    import _Quill from 'quill'
    import defaultOptions from './options'
    const Quill = window.Quill || _Quill
    
    // pollfill
    if (typeof Object.assign != 'function') {
      Object.defineProperty(Object, 'assign', {
        value(target, varArgs) {
          if (target == null) {
            throw new TypeError('Cannot convert undefined or null to object')
          }
          const to = Object(target)
          for (let index = 1; index < arguments.length; index++) {
            const nextSource = arguments[index]
            if (nextSource != null) {
              for (const nextKey in nextSource) {
                if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                  to[nextKey] = nextSource[nextKey]
                }
              }
            }
          }
          return to
        },
        writable: true,
        configurable: true
      })
    }
    //视频处理
    const BlockEmbed = Quill.import('blots/block/embed')
    class VideoBlot extends BlockEmbed {
      static create(value) {
        let node = super.create()
        node.setAttribute('src', value.url)
        node.setAttribute('controls', value.controls)
        node.setAttribute('width', value.width)
        node.setAttribute('height', value.height)
        node.setAttribute('webkit-playsinline', true)
        node.setAttribute('playsinline', true)
        node.setAttribute('x5-playsinline', true)
        return node
      }
    
      static value(node) {
        return {
          url: node.getAttribute('src'),
          controls: node.getAttribute('controls'),
           node.getAttribute('width'),
          height: node.getAttribute('height')
        }
      }
    }
    
    VideoBlot.blotName = 'simpleVideo'
    VideoBlot.tagName = 'video'
    Quill.register(VideoBlot)
    
    // export
    export default {
      name: 'myquill',
      data() {
        return {
          _options: {},
          _content: '',
          defaultOptions
        }
      },
      props: {
        content: String,
        value: String,
        disabled: {
          type: Boolean,
          default: false
        },
        options: {
          type: Object,
          required: false,
          default: () => ({})
        },
        globalOptions: {
          type: Object,
          required: false,
          default: () => ({})
        }, //文件大小阈值,单位字节B,大于1M=1024B
        threshold: {
          type: Number,
          default: 1025
        },
        //宽度
         {
          type: Number,
          default: 1080
        },
        //高度
        height: {
          type: Number,
          default: 100
        },
        //高度
        quality: {
          type: Number,
          default: 0.2
        }
      },
      mounted() {
        this.initialize()
      },
      beforeDestroy() {
        this.quill = null
        delete this.quill
      },
      methods: {
        // Init Quill instance
        initialize() {
          if (this.$el) {
            // Options
            this._options = Object.assign({}, this.defaultOptions, this.globalOptions, this.options)
    
            // Instance
            this.quill = new Quill(this.$refs.editor, this._options)
    
            this.quill.getModule('toolbar').addHandler('image', this.uploadImageHandler)
    
            this.quill.getModule('toolbar').addHandler('video', this.uploadVideoHandler)
    
            this.quill.enable(false)
    
            // Set editor content
            if (this.value || this.content) {
              this.quill.pasteHTML(this.value || this.content)
            }
    
            // Disabled editor
            if (!this.disabled) {
              this.quill.enable(true)
            }
    
            // Mark model as touched if editor lost focus
            this.quill.on('selection-change', (range) => {
              if (!range) {
                this.$emit('blur', this.quill)
              } else {
                this.$emit('focus', this.quill)
              }
            })
    
            // Update model if text changes
            this.quill.on('text-change', (delta, oldDelta, source) => {
              let html = this.$refs.editor.children[0].innerHTML
              const quill = this.quill
              const text = this.quill.getText()
              if (html === '<p><br></p>') html = ''
              this._content = html
              this.$emit('input', this._content)
              this.$emit('change', { html, text, quill })
            })
    
            // Emit ready event
            this.$emit('ready', this.quill)
          }
        },
        uploadImageHandler() {
          const input = document.querySelector('#uploadImg')
          input.value = ''
          input.click()
          // this.$refs.uploadImage.value = "";
          // this.$refs.uploadImage.click();
        },
    
        //el-upload文件上传的处理逻辑
        beforeUpload(file) {
          const isJPGorPNG = file.type === 'image/jpeg' || file.type === 'image/png'
          const isLessthan2M = file.size / 1024 / 1024 < 2 //最大限制2M
    
          if (!isJPGorPNG) {
            this.$message.error('上传图片只能是 JPG,PNG 格式!')
          }
          if (!isLessthan2M) {
            this.$message.error('上传图片大小不能超过 2MB!')
          }
          return isJPGorPNG && isLessthan2M
        },
        uploadImage(event) {
          var file = event.target.files[0]
          const that = this
          if (that.beforeUpload(file)) {
            //上传图片大于1M进行压缩
            if (file.size / 1024 > that.threshold) {
              this.condenseFile(file, function(base64Codes) {
                that.uploadImageSucess(1, base64Codes)
              })
            } else {
              this.convertBase64Url(file)
            }
            this.uploadImageSucess(1, this.condenseBase64)
          }
        },
    
        uploadVideoHandler() {
          const input = document.querySelector('#uploadVideo')
          input.value = ''
          input.click()
          // this.$refs.uploadVideo.value = "";
          // this.$refs.uploadVideo.click();
        },
    
        uploadVideo(event) {
          if (typeof XMLHttpRequest === 'undefined') {
            return
          }
          var xhr = new XMLHttpRequest()
          const formData = new FormData()
          //   formData.append('upload_file', event.target.files[0])
          var text = '成功'
          const that = this
          xhr.onload = function onload() {
            if (xhr.status < 200 || xhr.status >= 300) {
              // return option.onError(this.getError(action, option, xhr));
              text = xhr.responseText || xhr.response
              alert('失败' + text)
            }
            // console.log(text)
            that.uploadImageSucess(2, 'https://www.w3school.com.cn/i/movie.mp4')
          }
    
          xhr.open('post', 'http://localhost:8088/test', true)
          xhr.send(formData)
        },
    
        uploadImageSucess(type, url) {
          //   const addImageRange = this.quill.getSelection()
          //   const newRange = 0 + (addImageRange !== null ? addImageRange.index : 0)
          let newRange = this.quill.selection.savedRange.index
    
          if (type === 1) {
            // const url =
            //   'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100'
            this.quill.insertEmbed(newRange, 'image', url)
            this.quill.setSelection(1 + newRange)
            console.log(
              '$ uploadImageSucess 7777 his.quill.selection.savedRange.index:{this.quill.selection.savedRange.index}'
            )
          } else {
            // const url = 'https://www.w3school.com.cn/i/movie.mp4'
            this.quill.insertEmbed(newRange, 'simpleVideo', {
              url,
              controls: 'controls',
               '320',
              height: '240',
              autoplay: 'autoplay'
            })
            this.quill.setSelection(1 + newRange)
          }
          //获取内容
          //   console.log(this.quill.getContents())
          //   console.log(this.quill.getText())
          //   var html = this.quill.root.innerHTML
          // alert(html);
          //   console.log(html)
          //   var xx = this.quillGetHTML(this.quill.getContents())
          //  alert(xx);
          //   console.log(xx)
        },
    
        //获取富文本信息
        quillGetHTML(inputDelta) {
          //护球编辑器的内容
          var tempCont = document.createElement('div')
          new Quill(tempCont).setContents(inputDelta)
          return tempCont.getElementsByClassName('ql-editor')[0].innerHTML
        },
    
        //文件转换成base64字符串
        convertBase64Url(file) {
          var ready = new FileReader()
          ready.readAsDataURL(file)
          const that = this
          ready.onload = function() {
            var fileResult = this.result
            that.uploadImageSucess(1, fileResult)
          }
        },
    
        //压缩文件第一步
        condenseFile(file, callback) {
          var ready = new FileReader()
          ready.readAsDataURL(file)
          const that = this
          ready.onload = function() {
            var fileResult = this.result
            that.condenseCanvasDataURL(fileResult, callback)
          }
        },
    
        //压缩文件第二步,重新绘制图片,并返回压缩以后的文件的base64的值,可以把base64的值作为参数传给回调接口
        condenseCanvasDataURL(path, callback) {
          var img = new Image()
          img.src = path
          const that1 = this
          img.onload = function() {
            var that = this
            //默认压缩后图片规格
            var quality = 0.5
            var w = that.width
            var h = that.height
            var scale = w / h
            //计算图片的实际大小,设置宽高比例
            w = w > that1.width ? that1.width : w
            h = w / scale
            if (that1.quality && that1.quality > 0 && that1.quality <= 1) {
              quality = that1.quality
            }
    
            //生成canvas
            var canvas = document.createElement('canvas')
            var ctx = canvas.getContext('2d')
            // 创建属性节点
            var anw = document.createAttribute('width')
            anw.nodeValue = w
            var anh = document.createAttribute('height')
            anh.nodeValue = h
            canvas.setAttributeNode(anw)
            canvas.setAttributeNode(anh)
            ctx.drawImage(that, 0, 0, w, h)
    
            var base64 = canvas.toDataURL('image/jpeg', quality)
            // 回调函数返回压缩以后的文件的base64的值
            callback(base64)
          }
        },
    
        condenseconvertBase64UrlToBlob(urlData) {
          var arr = urlData.split(','),
            mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]),
            n = bstr.length,
            u8arr = new Uint8Array(n)
          while (n--) {
            u8arr[n] = bstr.charCodeAt(n)
          }
          return new Blob([u8arr], { type: mime })
        },
    
        //压缩文件第一步
        uploadFile(event) {
          if (typeof XMLHttpRequest === 'undefined') {
            return
          }
          var xhr = new XMLHttpRequest()
          if (xhr.upload) {
            xhr.upload.onprogress = function progress(e) {
              if (e.total > 0) {
                e.percent = (e.loaded / e.total) * 100
              }
              //   option.onProgress(e);吧上传进度回调给其他的接口
            }
          }
          const formData = new FormData()
          //   formData.append('upload_file', event.target.files[0]);
          var file = event.target.files[0]
          const that = this
          //上传图片大于1M进行压缩
          if (file.size / 1024 > 1025) {
            that.photoCompress(file, { quality: 0.2 }, function(base64Codes) {
              var bl = that.convertBase64UrlToBlob(base64Codes)
              formData.append('files', bl, file.name)
            })
          } else {
            formData.append('files', file, file.name)
          }
          xhr.onerror = function error(e) {
            alert('失败' + e)
          }
          var text = '成功'
          xhr.onload = function onload() {
            if (xhr.status < 200 || xhr.status >= 300) {
              text = xhr.responseText || xhr.response
              alert('失败' + text)
            }
            // console.log(text)
            //上传成功,根据xhr.response的返回信息中的url,将url插入到编辑区
            that.uploadImageSucess(1)
          }
    
          xhr.open('post', 'http://localhost:8084/test', true)
          xhr.send(formData)
        },
        //图片压缩处理 压缩文件第一步
        photoCompress(file, objCompressed, objDiv) {
          var ready = new FileReader()
          ready.readAsDataURL(file)
          const that = this
          ready.onload = function() {
            var fileResult = this.result
            that.canvasDataURL(fileResult, objCompressed, objDiv)
          }
        },
        //压缩文件第二步
        canvasDataURL(path, objCompressed, callback) {
          var img = new Image()
          img.src = path
          img.onload = function() {
            var that = this
            //默认压缩后图片规格
            var quality = 0.5
            var w = that.width
            var h = that.height
            var scale = w / h
            //实际要求
            w = objCompressed.width || w
            h = objCompressed.height || w / scale
            if (objCompressed.quality && objCompressed.quality > 0 && objCompressed.quality <= 1) {
              quality = objCompressed.quality
            }
    
            //生成canvas
            var canvas = document.createElement('canvas')
            var ctx = canvas.getContext('2d')
            // 创建属性节点
            var anw = document.createAttribute('width')
            anw.nodeValue = w
            var anh = document.createAttribute('height')
            anh.nodeValue = h
            canvas.setAttributeNode(anw)
            canvas.setAttributeNode(anh)
            ctx.drawImage(that, 0, 0, w, h)
    
            var base64 = canvas.toDataURL('image/jpeg', quality)
            // 回调函数返回base64的值
            callback(base64)
          }
        },
        //上传文件压缩的处理完成之后,回调方法 压缩文件第三步
        convertBase64UrlToBlob(urlData) {
          var arr = urlData.split(','),
            mime = arr[0].match(/:(.*?);/)[1],
            bstr = atob(arr[1]),
            n = bstr.length,
            u8arr = new Uint8Array(n)
          while (n--) {
            u8arr[n] = bstr.charCodeAt(n)
          }
          return new Blob([u8arr], { type: mime })
        }
      },
      watch: {
        // Watch content change
        content(newVal, oldVal) {
          if (this.quill) {
            if (newVal && newVal !== this._content) {
              this._content = newVal
              this.quill.pasteHTML(newVal)
            } else if (!newVal) {
              this.quill.setText('')
            }
          }
        },
        // Watch content change
        value(newVal, oldVal) {
          if (this.quill) {
            if (newVal && newVal !== this._content) {
              this._content = newVal
              this.quill.pasteHTML(newVal)
            } else if (!newVal) {
              this.quill.setText('')
            }
          }
        },
        // Watch disabled change
        disabled(newVal, oldVal) {
          if (this.quill) {
            this.quill.enable(!newVal)
          }
        }
      }
    }
    </script>

    options.js文件内容如下

    export default {
      theme: 'snow',
      boundary: document.body,
      modules: {
        toolbar: [
          ['bold', 'italic', 'underline', 'strike'],
          ['blockquote', 'code-block'],
          [{ header: 1 }, { header: 2 }],
          [{ list: 'ordered' }, { list: 'bullet' }],
          [{ script: 'sub' }, { script: 'super' }],
          [{ indent: '-1' }, { indent: '+1' }],
          [{ direction: 'rtl' }],
          [{ size: ['small', false, 'large', 'huge'] }],
          [{ header: [1, 2, 3, 4, 5, 6, false] }],
          [{ color: [] }, { background: [] }],
          [{ font: [] }],
          [{ align: [] }],
          ['link', 'image', 'video', 'formula'],
          ['clean']
        ]
      },
      // placeholder: 'Insert text here ...',
      placeholder: '我的编辑器占位符...',
      readOnly: false
    }

    3、再其他的vue文件中引用,内容如下

    <template>
      <!-- 使用 quill富文本编辑器组件-->
      <myquill v-model="content" ref="myQuillEditor"></myquill>
    </template>
    
    <script>
    import myquill from "./myquil.vue"; //导入定义的myquil.vue文件
    export default {
      name: "App",
      components: {
        myquill //注册quill富文本编辑器组件
      },
      data() {
        return {
          content: "quill富文本编辑器初始值"
        };
      }
    };
    </script>
    
    <style scoped>
    .ql-container.ql-snow {
      margin-bottom: 22px;
    }
    .ql-editor {
      height: 500px;
    }
    </style>
  • 相关阅读:
    Eureka源码分析
    从零搭建一个SpringCloud项目之Sleuth+Zipkin(六)
    从零搭建一个SpringCloud项目之Config(五)
    spring-boot-lll-starter自动化框架介绍
    大数据量分批执行封装
    itext7史上最全实战总结
    springboot-lll-starter限流说明
    研究javax.validation.constraints.NotNull运行原理
    Nacos入门
    错误日志告警实战
  • 原文地址:https://www.cnblogs.com/1175429393wljblog/p/12748681.html
Copyright © 2020-2023  润新知