Quill文件上传图标增加及功能实现

增加上图上载文件图标,并实现文件上传功能。

video.js

import Quill from 'quill'

const BlockEmbed = Quill.import('blots/block/embed')
// const Link = Quill.import('formats/link')

class Video extends BlockEmbed {
  static create(value) {
    // console.log(value,'value')
    const node = super.create(value);
    node.setAttribute('src', value.url);
    node.setAttribute('controls', true);
    node.setAttribute('name', value.name);
    node.setAttribute('controlsList', 'nodownload');
    // nofullscreen 不要全屏按钮
    // nodownload 不要下载按钮
    // noremoteplayback 不要远程回放
    node.setAttribute('controlsList', 'nodownload noremoteplayback') // 控制删除
    node.setAttribute('type', 'video')
    node.setAttribute('style', 'object-fit:fill;width: 100%;')
    node.setAttribute('preload', 'auto')    // auto - 当页面加载后载入整个视频  meta - 当页面加载后只载入元数据  none - 当页面加载后不载入视频
    node.setAttribute('playsinline', 'true')
    node.setAttribute('x-webkit-airplay', 'allow')
    // node.setAttribute('x5-video-player-type', 'h5') // 启用H5播放器,是wechat安卓版特性
    node.setAttribute('x5-video-orientation', 'portraint') // 竖屏播放 声明了h5才能使用  播放器支付的方向,landscape横屏,portraint竖屏,默认值为竖屏
    node.setAttribute('x5-playsinline', 'true') // 兼容安卓 不全屏播放
    node.setAttribute('x5-video-player-fullscreen', 'true')
    return node;
  }
  // 添加value获取当前的audio元素。拿到audio元素的属性。
  static value(domNode) {
    // console.log(domNode,'domNode')
    const value = {
      url: '',
      name: '',
    };
    // 这里要加判断。不然会显示undefined
    if (domNode.getAttribute('src')) {
      value.url = domNode.getAttribute('src');
      value.name = domNode.getAttribute('name');
    }
    return value;
  }

}
Video.blotName = 'video'
Video.className = 'ql-video'
Video.tagName = 'video'

export default Video

upload.svg

<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1693300673548" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5709" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20"><path d="M224 601.6v185.6a12.8 12.8 0 0 0 12.8 12.8h550.4a12.8 12.8 0 0 0 12.8-12.8V601.6a25.6 25.6 0 0 1 25.6-25.6h12.8a25.6 25.6 0 0 1 25.6 25.6v224a38.4 38.4 0 0 1-38.4 38.4H198.4a38.4 38.4 0 0 1-38.4-38.4v-224a25.6 25.6 0 0 1 25.6-25.6h12.8a25.6 25.6 0 0 1 25.6 25.6z m315.152-427.696l160 160a25.6 25.6 0 0 1 0 36.192l-9.056 9.056a25.6 25.6 0 0 1-36.192 0l-104.448-104.416a3.2 3.2 0 0 0-5.456 2.256V614.4a25.6 25.6 0 0 1-25.6 25.6h-12.8a25.6 25.6 0 0 1-25.6-25.6V276.992a3.2 3.2 0 0 0-5.456-2.256l-104.448 104.416a25.6 25.6 0 0 1-36.192 0l-9.056-9.056a25.6 25.6 0 0 1 0-36.192l160-160a38.4 38.4 0 0 1 54.304 0z" fill="#000000" p-id="5710"></path></svg>

完整代码

index.vue

<template>
  <div>
    <el-upload
      :action="uploadUrl"
      :before-upload="handleBeforeUpload"
      :on-success="handleUploadSuccess"
      :on-error="handleUploadError"
      name="file"
      :show-file-list="false"
      :headers="headers"
      style="display: none"
      ref="upload"
      :data="{'bussType':'editor','bussNo':'1','userId':'editor'}"
      v-if="this.type == 'url'"
    >
    </el-upload>
    <div class="editor" ref="editor" :style="styles"></div>
  </div>
</template>

<script>
  import Quill from "quill";
  import "quill/dist/quill.core.css";
  import "quill/dist/quill.snow.css";
  import "quill/dist/quill.bubble.css";
  import {getToken} from "@/utils/auth";
  //增加视频
  import Video from "./video";
  Quill.register(Video, true);
  const Parchment = Quill.import('parchment');

  // 这里第二个参数中的'upload-file'其实在这个例子中并不重要
  const UploadStyle = new Parchment.Attributor.Style('upload-file', 'upload-file', {
    scope: Parchment.Scope.BLOCK,
  });
  Quill.register(UploadStyle, true);

  // 源码中是import直接倒入,这里要用Quill.import引入
  const Link = Quill.import("formats/link");
  // 自定义a链接
  class FileBlot extends Link {
    // 继承Link Blot
    static create (value) {
      let node = undefined;
      if (value && !value.href) {
        // 适应原本的Link Blot
        node = super.create(value)
      } else {
        // 自定义Link Blot
        node = super.create(value.href)
        node.href = value.href
        node.innerText = value.innerText
        // node.setAttribute('download', value.innerText);  // 左键点击即下载
      }
      return node;
    }
  }
  FileBlot.blotName = "link" // 这里不用改,如果需要也可以保留原来的,这里用个新的blot
  FileBlot.tagName = "A"
  Quill.register(FileBlot) // 注册link

  export default {
    name: "Editor",
    props: {
      /* 编辑器的内容 */
      value: {
        type: String,
        default: "",
      },
      /* 高度 */
      height: {
        type: Number,
        default: null,
      },
      /* 最小高度 */
      minHeight: {
        type: Number,
        default: null,
      },
      /* 只读 */
      readOnly: {
        type: Boolean,
        default: false,
      },
      // 上传文件大小限制(MB)
      fileSize: {
        type: Number,
        default: 5,
      },
      /* 类型(base64格式、url格式) */
      type: {
        type: String,
        default: "url",
      }
    },
    data() {
      return {
        uploadUrl: process.env.VUE_APP_BASE_EDITOR + "minio/minio/uploadFile", // 上传的图片服务器地址
        headers: {
          Authorization: "Bearer " + getToken()
        },
        Quill: null,
        currentValue: "",
        options: {
          theme: "snow",
          bounds: document.body,
          debug: "warn",
          modules: {
            // 工具栏配置
            toolbar: [
              ["bold", "italic", "underline", "strike"],       // 加粗 斜体 下划线 删除线
              ["blockquote", "code-block"],                    // 引用  代码块
              [{list: "ordered"}, {list: "bullet"}],       // 有序、无序列表
              [{indent: "-1"}, {indent: "+1"}],            // 缩进
              [{size: ["small", false, "large", "huge"]}],   // 字体大小
              [{header: [1, 2, 3, 4, 5, 6, false]}],         // 标题
              [{color: []}, {background: []}],             // 字体颜色、字体背景颜色
              [{align: []}],                                 // 对齐方式
              ["clean"],                                       // 清除文本格式
              ["link", "image", "video", "upload-file"]             // 链接、附件、图片、视频
            ],
          },
          placeholder: "请输入内容",
          readOnly: this.readOnly,
        },
      };
    },
    computed: {
      styles() {
        let style = {};
        if (this.minHeight) {
          style.minHeight = `${this.minHeight}px`;
        }
        if (this.height) {
          style.height = `${this.height}px`;
        }
        return style;
      },
    },
    watch: {
      value: {
        handler(val) {
          if (val !== this.currentValue) {
            this.currentValue = val === null ? "" : val;
            if (this.Quill) {
              this.Quill.pasteHTML(this.currentValue);
            }
          }
        },
        immediate: true,
      },
    },
    mounted() {
      this.init();
    },
    beforeDestroy() {
      this.Quill = null;
    },
    created() {
      console.log(process.env.VUE_APP_BASE_EDITOR);
    },
    methods: {
      init() {
        const editor = this.$refs.editor;
        this.Quill = new Quill(editor, this.options);
        // 如果设置了上传地址则自定义图片上传事件
        if (this.type == 'url') {
          let toolbar = this.Quill.getModule("toolbar");
          toolbar.addHandler("image", (value) => {
            this.uploadType = "image";
            if (value) {
              this.$refs.upload.$children[0].$refs.input.click();
            } else {
              this.quill.format("image", false);
            }
          });
          toolbar.addHandler("video", (value) => {
            this.uploadType = "video";
            if (value) {
              this.$refs.upload.$children[0].$refs.input.click();
            } else {
              this.quill.format("video", false);
            }
          });

          toolbar.addHandler("upload-file", (value) => {
            this.uploadType = "upload-file";
            if (value) {
              this.$refs.upload.$children[0].$refs.input.click();
            } else {
              this.quill.format("upload-file", false);
            }
          });

        }
        this.Quill.pasteHTML(this.currentValue);
        this.Quill.on("text-change", (delta, oldDelta, source) => {
          const html = this.$refs.editor.children[0].innerHTML;
          const text = this.Quill.getText();
          const quill = this.Quill;
          this.currentValue = html;
          this.$emit("input", html);
          this.$emit("on-change", {html, text, quill});
        });
        this.Quill.on("text-change", (delta, oldDelta, source) => {
          this.$emit("on-text-change", delta, oldDelta, source);
        });
        this.Quill.on("selection-change", (range, oldRange, source) => {
          this.$emit("on-selection-change", range, oldRange, source);
        });
        this.Quill.on("editor-change", (eventName, ...args) => {
          this.$emit("on-editor-change", eventName, ...args);
        });
      },
      // 上传前校检格式和大小
      handleBeforeUpload(file) {
        // 校检文件大小
        if (this.fileSize) {
          const isLt = file.size / 1024 / 1024 < this.fileSize;
          if (!isLt) {
            this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
            return false;
          }
        }
        return true;
      },
      handleUploadSuccess(res, file) {
        // 获取富文本组件实例
        let quill = this.Quill;
        // 如果上传成功
        if (res.code == 200) {
          // 获取光标所在位置
          let length = quill.getSelection().index;
          if (this.uploadType != "image") {
            // 插入文件,res为服务器返回的文件链接地址
            if (this.uploadType === "upload-file") {
              // 插入文件,res为服务器返回的文件链接地址
              // console.log("minio" + res.data.downloadUrl);
              console.log(file.name);

              quill.insertEmbed(length +1 , "link", { href: "minio" + res.data.downloadUrl, innerText: file.name }, "api");
            } else {
              quill.insertEmbed(
                length + 1,
                this.uploadType,
                {url: "minio" + res.data.downloadUrl, name: file.name},
                "api"
              );
            }

          } else {
            // 插入图片  res.url为服务器返回的图片地址
            quill.insertEmbed(length, this.uploadType, res.data.downloadUrl);

          }
          // 调整光标到最后
          quill.setSelection(length + 1);
        } else {
          this.$message.error("图片插入失败");
        }
      },
      handleUploadError() {
        this.$message.error("图片插入失败");
      },
    },
  };
</script>

<style>
  .editor, .ql-toolbar {
    white-space: pre-wrap !important;
    line-height: normal !important;
  }

  .quill-img {
    display: none;
  }

  .ql-snow .ql-tooltip[data-mode="link"]::before {
    content: "请输入链接地址:";
  }

  .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
    border-right: 0px;
    content: "保存";
    padding-right: 0px;
  }

  .ql-snow .ql-picker.ql-size .ql-picker-label::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item::before {
    content: "14px";
  }

  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
    content: "10px";
  }

  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
    content: "18px";
  }

  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
    content: "32px";
  }

  .ql-snow .ql-picker.ql-header .ql-picker-label::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item::before {
    content: "文本";
  }

  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
    content: "标题1";
  }

  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
    content: "标题2";
  }

  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
    content: "标题3";
  }

  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
    content: "标题4";
  }

  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
    content: "标题5";
  }

  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
    content: "标题6";
  }

  .ql-snow .ql-picker.ql-font .ql-picker-label::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item::before {
    content: "标准字体";
  }

  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
    content: "衬线字体";
  }

  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
    content: "等宽字体";
  }

  .ql-upload-file {
    background-size: 0px 0px !important;
    background: url('upload.svg') no-repeat center center !important;
  }

</style>

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现音频功能,可以通过自定义 toolbar 的方式来添加一个上按钮,并在点击上按钮时触发文件选择和上操作。下面是一个简单的示例: ```html <template> <div> <vue-quill-editor v-model="content" :options="editorOptions"></vue-quill-editor> <input type="file" ref="fileInput" style="display:none" @change="handleFileChange" accept="audio/*"> </div> </template> <script> import VueQuillEditor from 'vue-quill-editor'; export default { components: { VueQuillEditor, }, data() { return { content: '', editorOptions: { toolbar: { container: [ ['bold', 'italic', 'underline', 'strike'], // 基础样式 ['audio'], // 自定义音频按钮 ], handlers: { audio: this.handleAudioButtonClick, // 自定义音频按钮点击事件 }, }, }, }; }, methods: { handleAudioButtonClick() { this.$refs.fileInput.click(); // 触发文件选择框 }, handleFileChange(event) { const file = event.target.files[0]; // 获取选择的文件 // 进行文件操作,可以使用第三方库或自定义上函数 // 上成功后,将音频地址插入到编辑器中 this.insertAudio(file); }, insertAudio(file) { const reader = new FileReader(); reader.onload = (e) => { const audioDataUrl = e.target.result; const range = this.$refs.editor.quill.getSelection(true); this.$refs.editor.quill.insertEmbed(range.index, 'audio', audioDataUrl, 'user'); }; reader.readAsDataURL(file); }, }, }; </script> ``` 在上述示例中,我们首先在 toolbar 中添加了一个自定义的音频按钮,然后在 `handleAudioButtonClick` 方法中触发了文件选择框。当用户选择了音频文件后,会触发 `handleFileChange` 方法,在该方法中进行文件操作。上成功后,通过 `insertAudio` 方法将音频地址插入到编辑器中。 请注意,上述示例只是一个简单的实现示例,实际的文件操作和插入音频的方式可能会有所不同,你可以根据自己的需求进行相应的修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值