富文本编辑器 可全屏可粘贴(只能单独粘贴图片或文字)

vue-quill-editor 全屏设置

components/Editor/fullScreen.js


import Vue from "vue";

const domList = [
  {
    dom: `<svg  t="1637824425355" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10463"><path d="M243.2 780.8v-179.2H153.6v179.2c0 49.28 40.32 89.6 89.6 89.6h179.2v-89.6H243.2zM780.8 153.6h-179.2v89.6h179.2v179.2h89.6V243.2c0-49.28-40.32-89.6-89.6-89.6zM243.2 243.2h179.2V153.6H243.2c-49.28 0-89.6 40.32-89.6 89.6v179.2h89.6V243.2z m537.6 537.6h-179.2v89.6h179.2c49.28 0 89.6-40.32 89.6-89.6v-179.2h-89.6v179.2z" p-id="10464" fill="#000000"></path></svg>`,
    tit: "最大化",
    id: "maxId",
    display: "block",
  },
  {
    dom: `<svg t="1637824296192" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6336"><path d="M341.065143 910.189714v-146.285714c0-53.686857-43.885714-97.572571-97.572572-97.572571h-146.285714a48.786286 48.786286 0 0 0 0 97.499428h146.285714v146.285714a48.786286 48.786286 0 1 0 97.499429 0z m-292.571429-617.910857c0 26.916571 21.796571 48.786286 48.713143 48.786286h146.285714c53.686857 0 97.572571-43.885714 97.572572-97.572572v-146.285714a48.786286 48.786286 0 0 0-97.499429 0v146.285714h-146.285714a48.786286 48.786286 0 0 0-48.786286 48.786286z m910.409143 0a48.786286 48.786286 0 0 0-48.713143-48.786286h-146.285714v-146.285714a48.786286 48.786286 0 1 0-97.499429 0v146.285714c0 53.686857 43.885714 97.572571 97.499429 97.572572h146.285714a48.786286 48.786286 0 0 0 48.713143-48.786286z m0 422.765714a48.786286 48.786286 0 0 0-48.713143-48.713142h-146.285714c-53.686857 0-97.572571 43.885714-97.572571 97.572571v146.285714a48.786286 48.786286 0 1 0 97.499428 0v-146.285714h146.285714a48.786286 48.786286 0 0 0 48.786286-48.786286z" fill="#000000" p-id="6337"></path></svg>`,
    tit: "还原",
    id: "minId",
    display: "none",
  },
];

/**@ bing
 *2021-11-25 15:58:21
 * v-maxWindow: 只针对 vue-quill-editor组件  最大化最小化
 */
Vue.directive("maxWindow", {
  //属性名称maxWindow,前面加v- 使用
  bind(el, binding, vnode, oldVnode) {
    setTimeout(() => {
      let dialogHeaderEl = el.querySelector(".ql-toolbar");

      domList.map((item) => {
        let dom = document.createElement("span");

        dom.className = "ql-formats";

        dom.innerHTML = `<button title="${item.tit}" style="display:${item.display}" id="${item.id}"  type="button" class="ql-max">${item.dom}</button>`;

        dialogHeaderEl.appendChild(dom);
      });

      //最大化
      document.getElementById("maxId").onclick = () => {
        el.style.width = 100 + "vw";
        el.style.height = 100 + "vh";
        el.style.position = "fixed";
        el.style.top = 0;
        el.style.left = 0;
        el.style.zIndex = 9999;
        el.style.background = "white";

        document.getElementById("maxId").style.display = "none";
        document.getElementById("minId").style.display = "block";
      };
      //最小化
      document.getElementById("minId").onclick = () => {
        el.style.width = "auto";
        el.style.height = "auto";
        el.style.position = "inherit";

        document.getElementById("maxId").style.display = "block";
        document.getElementById("minId").style.display = "none";
      };
    }, 0);
  },
});

组件

components/Editor/newEditor.vue

<template>
  <!--富文本编辑器-->
  <div class="app-container customEditor">
    <div class="avatar">
      <!-- 图片上传组件辅助-->
      <el-upload id="quill-upload" class="avatar-uploader" :action="serverUrl" ref="annexUpload" name="file" :limit="2"
        list-type="picture" :on-exceed="handleExceed" :on-error="handleError" :on-success="uploadAttachment"
        :file-list="uploadList" :with-credentials="true" :auto-upload="true" :headers="headers">
        <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
      </el-upload>
      <!-- 视频上传组件辅助-->
      <el-upload class="video-uploader" :action="serverUrl" name="video" accept=".mp4,.3gp" :headers="headers"
        :show-file-list="false" :on-success="uploadSuccessVideo" :on-error="handleError" :before-upload="beforeUpload">
      </el-upload>
    </div>
    <el-row v-loading="quillUpdateImg">
      <div v-if="!disableFlag">
        <quill-editor v-if="loaded" :disabled="disableFlag" v-model="detailContent" ref="myQuillEditor"
          :options="editorOption" @focus="onEditorFocus($event)" @change="onEditorChange($event)" v-maxWindow>
        </quill-editor>
      </div>
      <div v-else>
        <div style="display: flex;">
          <span @click="showViewer = !showViewer" style="float: right;cursor: pointer;margin-right: 20px;">
            <i :class="!showViewer ? 'el-icon-arrow-up' : 'el-icon-arrow-down'" class="arrow"></i>
          </span>
        </div>
        <viewer v-if="showViewer" :images="[]" class="viewer" ref="viewer">
          <div v-html="detailContent"></div>
        </viewer>
      </div>
    </el-row>
  </div>

</template>
<script>
//需要传真的token 不需要传值可以删除
  import {
    getToken
  } from '@/utils/auth'
  import 'viewerjs/dist/viewer.css'
  import Viewer from 'v-viewer'
  import 'quill/dist/quill.core.css'
  import 'quill/dist/quill.snow.css'
  import 'quill/dist/quill.bubble.css'
  //npm install quill
  import Quill from 'quill'
  //npm install vue-quill-editor
  import {
    quillEditor
  } from 'vue-quill-editor'
  //npm install quill-image-resize-module quill-image-drop-module --save 调节图片大小
  //点击预览图片npm install v-viewer --save
  //点击预览图片 在main.js中需要设置
  //点击预览图片 import Viewer from 'v-viewer'
  //点击预览图片 import 'viewerjs/dist/viewer.css'
Vue.use(Viewer)
  import {
    ImageDrop
  } from 'quill-image-drop-module'
  import ImageResize from 'quill-image-resize-module'
  Quill.register('modules/imageDrop', ImageDrop)
  Quill.register('modules/imageResize', ImageResize)
 //全屏设置
  import maxWindow from './fullscreen.js'
  // 标题
  const titleConfig = {
    "ql-bold": "加粗",
    "ql-color": "颜色",
    "ql-font": "字体",
    "ql-code": "插入代码",
    "ql-italic": "斜体",
    "ql-link": "添加链接",
    "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": "清除字体样式",
    "ql-upload": "文件",
    "ql-max": "窗口设置"
  };
  //获取富文本所以图片的id和被删除图片id
  // const getRemovImgId = (ids, html) => {
  //   let dom = document.createElement('DIV')
  //   dom.innerHTML = html
  //   const imgDom = dom.getElementsByTagName('img')
  //   const url = window.location.host
  //   const arr=[]
  //   for (let i = 0; i < imgDom.length; i++) {
  //     if (imgDom[i].src.indexOf(url) > -1) {
  //       let reg = new RegExp('(^|&)id=([^&]*)(&|$)')
  //       let r = imgDom[i].src.split('?')[1].match(reg)
  //       let id=unescape(r[2])
  //       ids.splice(ids.indexOf(id),1)
  //       arr.push(id)
  //     }
  //   }
  //   return {
  //     removeIds:ids,
  //     imgIds:arr
  //   }
  // }
  export default {
    name: 'QuillEditorForm',
    components: {
      quillEditor,
      Viewer
    },
    props: ['entity', 'disableFlag', 'value'],
    data() {
      return {
         imgIds: [],//所有图片id
                  removeIds:[],//被删除图片id
        quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
        serverUrl: window.SITE_CONFIG['apiURL'] + "/common/upload", // 上传的图片服务器地址,  // 这里写你要上传的图片服务器地址
        headers: {
          Authorization: 'Bearer ' + getToken()
        }, // //需要传真的token 不需要传值可以删除 有的图片服务器要求请求头需要有token之类的参数,写在这里
        uploadList: [],
        activeNames: ['1'],
        detailContent: '', // 富文本内容
        initFlag: true,
        // 富文本编辑器配置
        editorOption: {
          placeholder: '',
          theme: 'snow', // or 'bubble'
          modules: {
            //粘贴
            clipboard: {
              matchers: []
            },
            imageDrop: {
              loading: true,
              name: 'file',
              action: window.SITE_CONFIG['apiURL'] + "/common/upload",
              response: (res) => {
                console.log('拖拽', res)
                return res.info
              }
            },
            imageResize: { //调节图片大小
              displayStyles: {
                backgroundColor: 'black',
                border: 'none',
                color: 'white'
              },
              modules: ['Resize', 'DisplaySize', 'Toolbar']
            },
            toolbar: {
              container: [
                [], // 预留图片上传
                ['blockquote', 'code-block'], // 引用,代码块
                [{
                  'list': 'ordered'
                }, {
                  'list': 'bullet'
                }], // 列表
                [{
                  'header': [1, 2, 3,4,5,6, false]
                }], // 几级标题(1~6)
                [{
                  'header': 1
                }, {
                  'header': 2
                }], // 标题,键值对的形式;1、2表示字体大小
                [{
                  'size': ['small', false, 'large']
                }], // 字体大小('small', false, 'large', 'huge')
                [{
                  'color': []
                }, {
                  'background': []
                }],
                ['bold', 'italic', 'underline'], // 加粗,斜体,下划线,删除线('strike')
                [{
                  'script': 'sub'
                }, {
                  'script': 'super'
                }], // 上下标
                [{
                  'indent': '-1'
                }, {
                  'indent': '+1'
                }], // 缩进
                [{
                  'direction': 'rtl'
                }], // 文本方向
                [{ 'font': [] }], // 字体
                // [{ 'align': [] }], // 对齐方式
                ['clean'], // 清除字体样式
                ['link', 'video']
              ], // 工具栏
              handlers: {
                image: (value) => {
                  if (value) {
                    this.$refs.annexUpload.$el.querySelector('#quill-upload input').click()
                  } else {
                    this.Quill.format('image', false)
                  }
                },
                video: function(value) {
                  //当点击视频上传时,value会变为true
                  if (value) {
                    // 触发上传
                    document.querySelector(".video-uploader input").click();
                  } else {
                    this.quill.format("video", false);
                  }
                },

              }
            }
          }
        },
        quillInstance: null,
        loaded: true,
        showViewer: true
      }
    },
    watch: {
      value: {
        handler(val) {
          this.detailContent = val
          this.$forceUpdate()
        },
        deep: true,
        immediate: true
      },
      detailContent: {
        handler(val) {
          if (val && this.initFlag && this.$refs.myQuillEditor && this.$refs.myQuillEditor.quill) {
            // 初始化页面,取消focus状态
            this.$refs.myQuillEditor.quill.enable(false)
            this.detailContent = val
            this.$emit('input', val)
            setTimeout(() => {
              this.$refs.myQuillEditor.quill.enable(true)
            })
            this.initFlag = false
          } else {
            this.detailContent = val
            this.$emit('input', val)
          }
        },
        deep: true,
        immediate: true
      },
      disableFlag: {
        handler(val) {
          if (!val) {
            this.editorOption.modules.toolbar.container[0] = ['image']
          }
          this.loaded = false
          this.$nextTick(() => {
            this.loaded = true
            this.$nextTick(() => {
              if (!this.$refs.myQuillEditor) {
                return
              }
              //  自定义粘贴图片功能
              this.quillInstance = this.$refs.myQuillEditor.quill
              this.quillInstance.root.removeEventListener('paste', this.handlePaste)
              this.quillInstance.root.addEventListener('paste', this.handlePaste, false)
            })
          })
        },
        immediate: true
      }
    },
    mounted() {
      //设置文字提示
      this.setTitleText()
    },
    methods: {
      //设置文字提示
      setTitleText() {
        setTimeout(function() {
          const oToolBar = document.querySelector(".ql-toolbar"),
            aButton = oToolBar.querySelectorAll("button"),
            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.classList[0]];
            }
          });
          aSelect.forEach(function(item) {
            item.parentNode.title = titleConfig[item.classList[0]];
          });
        }, 500)
      },
      // 用户自动获取焦点,不设置focus状态
      onEditorFocus(e) {
        this.initFlag = false
      },
      //图片粘贴
      handlePaste(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
            }
            this.uploadToServer(file, (res) => {
              // 获取光标内容
              var range = this.quillInstance.getSelection()
              if (range) {
                this.uploadAttachment(res, file, null)
                //  在当前光标位置插入图片
                // this.$refs.myQuillEditor.quill.insertEmbed(range.index, 'image', res.url)
                //  将光标移动到图片后面
                this.$refs.myQuillEditor.quill.setSelection(range.index + 1)
              }
            })
          })
        }
      },
      //上传图片
      uploadToServer(file, callback) {
        var xhr = new XMLHttpRequest()
        var formData = new FormData()
        formData.append('file', file)
        console.log(this.serverUrl)
        // headers //需要传真的token 不需要传值可以删除
        let str = 'Bearer ' + getToken()
        xhr.open('post', this.serverUrl)
        xhr.setRequestHeader('Authorization', str)
        xhr.withCredentials = true
        xhr.responseType = 'json'
        xhr.send(formData)
        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4 && xhr.status === 200) {
            callback(xhr.response)
          }
        }
      },
      //上传视频
      uploadSuccessVideo(res, file) {
        // res为文件服务器返回的数据
        // 获取富文本组件实例
        let quill = this.$refs.myQuillEditor.quill;
        // 如果上传成功
        if (res.code === 200) {
          // 使用getSelection来获取光标所在位置
          let length = quill.getSelection().index;
          // 插入“video”或者“image”  第三个参数为服务器端返回的地址
          quill.insertEmbed(length, "video", res.data);
          // 调整光标到最后
          quill.setSelection(length + 1);
          console.log(this.content)
        } else {
          this.$message.error("视频插入失败");
        }
        // loading动画消失
        this.quillUpdateImg = false;
      },
      //上传判断
      handleExceed(files, fileList) {
        this.$message.warning(`当前限制一次性上传最多 2 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)
      },
      // 富文本图片上传前
      beforeUpload() {
        // 显示loading动画
        this.quillUpdateImg = true;
      },
      //上传失败
      handleError(err, file, fileList) {
        this.$message.warning(`文件上传失败,请尝试重新上传,文件失败原因为: ${err}`)
      },
      //上传成功
      uploadAttachment(response, file, fileList) {
        // 保存文件信息
        if (response.code === 200) {
          // 获取富文本组件实例
          let quill = this.$refs.myQuillEditor.quill
          // 获取光标所在位置
          let length = quill.getSelection().index
          // 插入图片  res.info为服务器返回的图片地址
          let imgurl = window.SITE_CONFIG['apiURL'] + response.fileName
          quill.insertEmbed(length, 'image', imgurl)
          quill.setSelection(length + 1)
          let fileType = null
          if (file.raw && file.raw.type) {
            fileType = file.raw.type
          } else {
            fileType = file.type
          }
          let params = {}
          params = {
            // 保存文件上传的参数
          }
          this.$message.success('上传成功')
        } else {
          this.$message.error(response.message)
        }
        // 清空文件列表
        this.uploadList = []
      },
      //改变时传值
      onEditorChange(e) {
        this.$emit('change', e.html)
        //删除多余图片
        //  const ids=getRemovImgId(this.imgIds,e.html)
        // this.imgIds=ids.imgIds
        // this.removeIds=[...this.removeIds,...ids.removeIds]
        // console.log(this.imgIds)
        // console.log(this.removeIds)
      }
    }
  }
</script>

<style lang="scss">
  .customEditor .quill-editor {

    .ql-toolbar.ql-snow .ql-picker-label {
      display: flex !important;
    }
  }

  .avatar {
    display: none;
  }

  ::v-deep .ql-disabled {
    background-color: #F5F7FA;
    border-color: #E4E7ED;
    color: #7f8185;
    cursor: not-allowed;
  }

  .viewer {
    border: 1px solid #ebeef5;
    padding: 12px 15px;
    min-width: 657px;
    max-width: 700px;
    max-height: 600px;
    overflow: auto;
  }

  .arrow {
    margin: 0 8px 0 auto;
    transition: transform .3s;
    font-weight: 300;
  }
</style>

在页面中引用

最后需要在vue.config.js chainWebpack里面设置

chainWebpack(config) {

   
    //富文本可编辑 配置
    config.plugin('provide').use(webpack.ProvidePlugin, [{
          'window.Quill': 'quill'
        }])
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值