vue富文本,支持粘贴图片、上传附件功能

<richText details="标记" :value="ruleForm.data" @getInputValue="getInputValue" />
 // 监听并且给输入框复制
    getInputValue(val, key) {
      switch (key) {
        case '标记':
          this.ruleForm.data= val
          break
    }
  }
const toolbarOptions = [
  [],
  ['blockquote', 'code-block'],
  [{ header: [1, 2, 3, 4, 5, 6, false] }],
  [{ script: 'sub' }, { script: 'super' }],
  [{ indent: '-1' }, { indent: '+1' }],
  [{ direction: 'rtl' }],
  [{ 'color': [] }, { 'background': [] }],
  ['link'],
  ['upload']
]
export default toolbarOptions
<template>
  <div style="width:100%">
    <div class="avatar">
      <el-upload id="quill-upload" ref="annexUpload" action="上传的路径" name="content" :limit="1" list-type="picture" :on-exceed="handleExceed" :on-error="handleError" :file-list="uploadList" :with-credentials="true" :auto-upload="true" />
    </div>
    <quill-editor ref="myQuillEditor" v-model="content" v-viewer :options="editorOption" @change="onEditorChange($event)" @focus="onEditorClick($event)" />

    <el-dialog
      title="附件上传"
      :visible.sync="dialogVisible"
      width="28%"
      :modal="false"
      :before-close="handleClose"
    >
      <div class="demo">
        <el-upload
          ref="upload"
          class="upload-demo"
          action="/api/security_audit_tools/hole_planet_file"
          :on-change="handleProgress"
          :on-preview="handlePreview"
          :on-remove="handleRemove"
          :file-list="fileList"
          :auto-upload="false"
          :on-success="handleSuccess"
          :on-error="handleFileError"
          :before-upload="beforeAvatarUpload"
          :on-exceed="handleExceed1"
          multiple
          drag
          :limit="5"
        >
          <i class="el-icon-upload" />
          <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
          <div slot="tip" class="el-upload__tip">一次只能上传5个文件,且每个文件不超过50Mb</div>
        </el-upload>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" :disabled="uploadAble" @click="insertData">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor, Quill } from 'vue-quill-editor'
import toolbarOptions from './toolbarOptions'
import { uploadFileBug, uploadPicTask } from '@/api/codecheck/bugPlanet/index'
var Link = Quill.import('formats/link')
class FileBlot extends Link { // 继承Link Blot
  static create(value) {
    let node
    if (value && !value.href) { // 适应原本的Link Blot
      node = super.create(value)
    } else { // 自定义Link Blot
      node = super.create(value.href)
      // node.setAttribute('download', value.innerText);  // 左键点击即下载
      node.innerText = value.innerText
      node.download = value.innerText
    }
    return node
  }
}
FileBlot.blotName = 'link'
FileBlot.tagName = 'A'
Quill.register(FileBlot)
export default {
  components: { quillEditor },
  props: {
    details: {
      type: String,
      default: ''
    },
    value: {
      type: String,
      default: ''
    },
    goto: {
      type: Number,
      default: 0
    },
    tabIndex: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      once: false,
      timing: null,
      uploadAble: false,
      sucList: [],
      fileList: [],
      fileExe: [],
      dialogVisible: false,
      content: '',
      editorOption: {
        placeholder: '',
        theme: 'snow',
        modules: {
          toolbar: {
            container: toolbarOptions,
            handlers: {
              image: function(value) {
                if (value) {
                  document.querySelector('.quill-upload input').click()
                } else {
                  this.quill.format('image', false)
                }
              },
              upload: (value) => {
                if (value) {
                  this.dialogVisible = true
                  // document.querySelector('#quill-upload1 input').click()
                }
              }
            }
          }
        }
      },
      uploadList: []
    }
  },
  watch: {
    value(curVal) {
      if (curVal) {
        this.content = curVal
      }
      // if (!this.once) {
      //   this.$nextTick(() => {
      //     this.$refs.myQuillEditor.quill.blur()
      //     window.scrollTo(0, 0)
      //     this.once = true
      //   })
      // }
    },
    goto() {
      setTimeout(() => {
        const quill = this.$refs.myQuillEditor.quill
        quill.setSelection(this.content.length + 1)
      }, 100)
    },
    fileList: {
      handler() {
        let isOk = true
        this.fileList.forEach(item => {
          if (item.size / 1024 / 1024 < 50 && item.status !== 'success') {
            isOk = false
          }
        })
        isOk ? this.uploadAble = false : this.uploadAble = true
      },
      deep: true
    }
  },
  mounted() {
    this.initPic()
    if (this.value) {
      this.content = this.value
      setTimeout(() => {
        const quill = this.$refs.myQuillEditor.quill
        quill.setSelection(this.content.length + 1)
      }, 100)
    }
  },
  methods: {
    insertData() {
      const quill = this.$refs.myQuillEditor.quill
      let length = quill.getSelection(true).index
      this.$refs.myQuillEditor.quill.focus()
      this.sucList.forEach((item, i) => {
        if (i === 0) {
          const title = `\n 上传人:${item.uploader},上传时间:${item.uploadTime} \n`
          quill.insertEmbed(length, title)
          length += title.length
          quill.insertEmbed(length, 'link', { href: item.url, innerText: item.name, download: item.name }, 'api')
          length += item.name.length
        } else {
          const title = `\n`
          quill.insertEmbed(length, title)
          length += title.length
          quill.insertEmbed(length, 'link', { href: item.url, innerText: item.name, download: item.name }, 'api')
          length += item.name.length
        }
        // quill.setSelection(length + 1)
      })
      this.sucList = []
      this.fileList = []
      this.dialogVisible = false
    },
    // 文件上传限制
    handleExceed1(files, fileList) {
      this.$message.warning(`当前限制一次性上传最多 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
    },
    handleProgress(file, fileList) {
      this.fileList = fileList
      if (file.status === 'ready') {
        const isLt2M = file.size / 1024 / 1024 < 50
        if (!isLt2M) {
          if (this.timing) {
            clearTimeout(this.timing)
          }
          this.timing = setTimeout(() => {
            this.$message.error(`上传附件大小不能超过 50MB!`)
          }, 1 * 500)
          this.fileExe.push(file.name)
          let index = -1
          for (let i = 0; i < fileList.length; i++) {
            if (fileList[i].name === file.name) {
              index = i
            }
          }
          this.$nextTick(function() {
            const style = document.getElementsByClassName('el-upload-list__item-name')
            style[index].style.color = 'red'
          })
        } else {
          const params = new FormData()
          params.append('file', file.raw)
          file.status = 'uploading'
          file.percentage = 0
          const onUploadProgress = (progressEvent) => {
            const percent = (progressEvent.loaded / progressEvent.total) * (89 + Math.round(Math.random() * 10)) || 0
            file.percentage = percent
          }
          uploadFileBug({
            data: params
          }, onUploadProgress).then((res) => {
            if (res.code === '0') {
              file.status = 'success'
              this.sucList.push({ url: process.env.VUE_APP_BASE_API + '/api/security_audit_tools/hole_planet_file' + res.data.url, name: res.data.name, uploadTime: res.data.uploadTime, uploader: res.data.uploader })
            } else {
              file.status = 'ready'
            }
          })
        }
      }
    },
    beforeAvatarUpload(file) {
    },
    handleSuccess(response, file, fileList) {
      if (response.code === '0') {
        this.sucList.push({ url: process.env.VUE_APP_BASE_API + '/api/security_audit_tools/hole_planet_file' + response.data.url, name: response.data.name, uploadTime: response.data.uploadTime, uploader: response.data.uploader })
      }
    },
    handleFileError(err, file, fileList) {
      this.$message.error(`文件上传失败,请尝试重新上传,文件失败原因为: ${err}`)
    },
    submitUpload() {
      this.$refs.upload.headers['token'] = localStorage.getItem('panlong_userToken')
      this.$refs.upload.submit()
    },
    handleRemove(file, fileList) {
      for (let i = 0; i < this.fileList.length; i++) {
        if (this.fileList[i].name === file.name) {
          this.fileList.splice(i, 1)
        }
      }
      for (let i = 0; i < this.sucList.length; i++) {
        if (this.sucList[i].name === file.name) {
          this.sucList.splice(i, 1)
        }
      }
      for (let i = 0; i < this.fileExe.length; i++) {
        if (this.fileExe[i] === file.name) {
          this.fileExe.splice(i, 1)
        }
      }
      if (this.fileExe.length === 0) {
        this.uploadAble = false
      }
    },
    handlePreview(file) {
      // console.log(file)
    },
    handleClose() {
      this.dialogVisible = false
    },
    // 监听点击超链接显示弹窗
    onEditorClick(event) {
      const e = document.querySelectorAll('.ql-tooltip,.ql-flip')
      if (e && e[this.tabIndex]) {
        if (e[this.tabIndex].style.left.indexOf('-') === 0) {
          e[this.tabIndex].style.left = 0
        }
        if (e[this.tabIndex].style.top.indexOf('-') === 0) {
          e[this.tabIndex].style.top = 0
        }
      }
    },
    // 监听富文本改变
    onEditorChange(e) {
      this.$emit('getInputValue', e.html, this.details)
    },
    // 文件上传限制
    handleExceed(files, fileList) {
      this.$message.warning(`当前限制一次性上传最多 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
    },
    // 文件上传失败提示
    handleError(err, file, fileList) {
      this.$message.error(`文件上传失败,请尝试重新上传,文件失败原因为: ${err}`)
    },
    initPic() {
      const quill = this.$refs.myQuillEditor.quill
      this.$forceUpdate()
      quill.root.addEventListener(
        'paste',
        (evt) => {
          if (evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length) {
            evt.preventDefault()
            ;[].forEach.call(evt.clipboardData.files, (file) => {
              if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
                return
              }
              var formData = new FormData()
              formData.append('uploadFile', file)
              uploadPicTask({
                data: formData
              }).then((res) => {
                var range = quill.getSelection()
                if (range) {
                  const quill = this.$refs.myQuillEditor.quill
                  const length = quill.getSelection().index
                  quill.insertEmbed(length, 'image', '/api/security_audit_tools/hole_planet_file/image/' + res.data)
                  quill.setSelection(length + 1)
                  // eslint-disable-next-line no-unused-vars
                  let fileType = null
                  if (file.raw && file.raw.type) {
                    fileType = file.raw.type
                  } else {
                    fileType = file.type
                  }
                  this.uploadList = []
                  this.$refs.myQuillEditor.quill.setSelection(range.index + 1)
                }
              })
            })
          }
        },
        false
      )
    }
  }

}
</script>

<style lang="scss" scoped>

.avatar {
  display: none;
}
::v-deep .el-dialog__header{
  padding: 20px 20px 10px;
}
.demo{
.upload-demo{
  border: 1px solid #eee;
  min-height: 200px;
  padding: 10px;
}
}
::v-deep .el-dialog__footer{
 display: flex;
 justify-content: center;
}
::v-deep .ql-container {
    /* overflow-y: auto; */
    height: 300px !important;
    line-height: 25px;
    // padding-bottom: 25px;
}
::v-deep .el-upload--text{
  width: 100%;
}
::v-deep .el-upload-dragger {
    background-color: #fff;
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    width: 100%;
    height: 180px;
    text-align: center;
    position: relative;
    overflow: hidden;
}
</style>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值