当前位置: 首页 > article >正文

vue2 quill 视频上传 ,基于ruoyi vue,oss

包含两种上传方式,第一种点开弹新页面可选url和点击上传。本文中是第二种,自己拼的。像点击上传图片一样,直接传video文件,原创不易,纯纯踩坑;

因为现阶段能搜索到的内容,99.5%都是一样的内容,无法满足需求。

我的需求是在ruoyi-vue里面用quill富文本,但里面一部分功能没有。

但作者给了自定义插件通道。

如果根据我的代码去写,上传完视频 就拿不到光标,只能拿到最后一个字的长度,因此上传视频被提成bug。

自猜:点击完tag的上传视频 是打开个子页面,父子页面传值可能存在问题。

无奈之下只能自己手拼。看代码吧。如有问题请及时指正。

所有代码在同级目录下

贴代码

index.vue

<template>
  <div>
 
    <el-upload
      :action="uploadUrl"
      :before-upload="handleBeforeUpload"
      :http-request="uploadURL"
      name="file"
      :show-file-list="false"
      :headers="headers"
      style="display: none"
      ref="upload"
      v-if="this.type == 'url'"
    >
    </el-upload>

    <div class="editor" ref="editor" :style="styles"></div>
  
    <!--视频上传 begin -->

     <!-- <el-tab-pane label="添加视频链接" name="second">
                <el-input v-model="videoDialog.videoLink" placeholder="请输入视频链接" clearable></el-input>
                <el-button type="primary" size="small" style="margin: 20px 0px 0px 0px" @click="addVideoLink(videoDialog.videoLink)">添加 </el-button>
            </el-tab-pane> -->

 <!--<div>
    <el-dialog :close-on-click-modal="false" width="50%" style="margin-top: 1px" title="视频上传" :visible.sync="videoDialog.show" append-to-body   ref="dialogRef">
        <el-tabs v-model="videoDialog.activeName">
           
            <el-tab-pane label="本地视频上传"  >
              
                <el-upload
                v-loading="uploading"
                        style="text-align: center"
                        drag
                        action=""
                        accept="video/*"
  :http-request="uploadVideoURL"
  :multiple="false"
  :before-upload="videoHandleBeforeUpload"
  v-if="this.type == 'video'"
>

                    <i class="el-icon-upload"></i>
                    <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                </el-upload>
            </el-tab-pane>
        </el-tabs>
    </el-dialog>
</div> -->

<!-- :http-request="uploadVideoURL"
  :multiple="false"
  :before-upload="videoHandleBeforeUpload"
  style="display: none" -->
<el-upload


      name="file"
      :show-file-list="false"
      :headers="headers"
      style="display: none"
      ref="uploadVideo"

      :http-request="uploadVideoURL"
      :before-upload="videoHandleBeforeUpload"
      action="uploadVideoURL"
      :multiple="false"
      v-if="this.video == 'r'"
>

                    <i class="el-icon-upload"></i>
                    <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                </el-upload>


    <!--视频上传 end -->
  </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 {client,	getFileNameUUID} from '@/utils/alioss';

let fontFamily = ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial', 'Times-New-Roman', 'sans-serif'];
Quill.imports['formats/font'].whitelist = fontFamily;
Quill.register(Quill.imports['formats/font']);
let fontSize = ['10px', '11px','12px', '14px', '15px', '16px', '17px', '18px', '20px', '24px', '36px']
Quill.imports['attributors/style/size'].whitelist = fontSize;
Quill.register(Quill.imports['attributors/style/size']);

import Video from "./video.js";
// import Buffer from "vue-buffer";


Quill.register(Video, true);



// 行高
import { lineHeightStyle } from './lineHeight.js';
Quill.register({ 'formats/lineHeight': lineHeightStyle }, true);

 // 行间距
import { letterSpacingStyle } from './letterSpacing.js';
Quill.register({ 'formats/letterSpacing': letterSpacingStyle }, true);

Quill.register({ 'formats/lineHeight': lineHeightStyle }, true);

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",
    },
    video: {
      type: String,
      default: "r",
    }
  },
  data() {
    return {
      aliyun :{},
      uploading : false,
      videoDialog: {show: false,activeName: null,videoLink:null},
      uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
      headers: {
        Authorization: "Bearer " + getToken()
      },
      Quill: null,
      currentValue: "",
      options: {
        theme: "snow",
        bounds: document.body,
        debug: "warn",
        modules: {
          // 工具栏配置
          toolbar: {
            handlers: {
        // lineHeight: (value) => {
        //     if (value) {
        //         let quill = this.$refs.myQuillEditor.quill;
        //         quill.format("lineHeight", value);
        //     }
        // },
        // lineHeight :['initial', '1', '1.5', '1.75', '2', '3', '4', '5'],
        video: (value) => {
            this.videoDialog.show = true;
        },
      },
      container : 
      [
            ["bold", "italic", "underline", "strike"],       // 加粗 斜体 下划线 删除线
            ["blockquote", "code-block"],                    // 引用  代码块
            [{ list: "ordered" }, { list: "bullet" }],       // 有序、无序列表
            [{ indent: "-1" }, { indent: "+1" }],            // 缩进
            // [{ size: ["small", false, "large", "huge"] }],   // 字体大小
            [{ size:fontSize }],   // 字体大小

            // [{ size: [ "ten",false,"eleven", "twelve", "thirteen",
            //     "fourteen", "fifteen", "sixteen", "seventeen",
            //     "eighteen", "nineteen", "twenty"]}],

            [{ header: [1, 2, 3, 4, 5, 6, false] }],         // 标题
            [{ color: ['aqua', 'black', 'blue', 'brown', 'cyan', 'gold', 'gray', 'green', 'indigo', 'lavender', 'lime', 'magenta', 'maroon', 'navy', 'olive', 'orange', 'pink', 'purple', 'red', 'silver', 'teal', 'violet', 'white', 'yellow'] },
             { background: ['aqua', 'black', 'blue', 'brown', 'cyan', 'gold', 'gray', 'green', 'indigo', 'lavender', 'lime', 'magenta', 'maroon', 'navy', 'olive', 'orange', 'pink', 'purple', 'red', 'silver', 'teal', 'violet', 'white', 'yellow']
             }],             // 字体颜色、字体背景颜色
            [{ align: [] }],                                 // 对齐方式
            ["clean"],                                       // 清除文本格式
            ["image", "video"],                       // 链接、图片、视频
             [{lineheight: ['initial', '1', '2', '3', '4', '5','20'] }],
            [{ letterSpacing: [ 'initial','2px','4px', '6px','8px','10px', '12px', '14px', '16px'],},], //行间距
            // ["link", "image", "video"]                       // 链接、图片、视频
            
          ],
          }
        },
      
        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();

  },
  methods: {


        // 上传文件之前
			videoHandleBeforeUpload(file) {
				const isJpgOrPng =
    file.type === 'video/ogg' ||
    file.type === 'video/flv' ||
    file.type === 'video/avi' ||
    file.type === 'video/wmv' ||
    file.type === 'video/mov' ||
    file.type === 'video/mp4'
  if (!isJpgOrPng) {
    this.$message.error('只能上传图片/视频!')
    return
  }
			},
			uploadURL(file) {
				//注意哦,这里指定文件夹'image/',尝试过写在配置文件,但是各种不行,写在这里就可以
				var fileName = 'imgs/'+'editor' + getFileNameUUID() + "." + file.file.name.replace(/.+\./, "");//'.jpg';
				client().multipartUpload(fileName, file.file,{
                progress: function(percentage, cpt) {
                  console.log('打印进度',percentage)
                }
              }).then((res)=>{
          //此处赋值,是相当于上传成功之后,手动拼接服务器地址和文件名
          let quill = this.Quill;
          let length = quill.getSelection().index;
          console.log("quill.getSelection().index===",length);
          console.log(quill.getSelection());
        // 插入图片  res.url为服务器返回的图片地址
        quill.insertEmbed(length, "image", 'https://oss-cn-beijing.aliyuncs.com/' + fileName);
        // 调整光标到最后
        quill.setSelection(length + 1);
        })
      },
      
    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);
          }
        });
      }
      //todo begin
      console.log("this.video",this.video);
      if (this.video == 'r') {
        let toolbar = this.Quill.getModule("toolbar");
        toolbar.addHandler("video", (value) => {
          this.uploadType = "video";
          console.log("value",value);
          if (value) {
            this.$refs.uploadVideo.$children[0].$refs.input.click();
          } else {
            this.quill.format("video", false);
          }
        });
      }
      //todo end
      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) {
				const isJPEG = file.name.split('.')[1] === 'jpeg';
				const isJPG = file.name.split('.')[1] === 'jpg';
				const isPNG = file.name.split('.')[1] === 'png';
				const isWEBP = file.name.split('.')[1] === 'webp';
				const isGIF = file.name.split('.')[1] === 'gif';
				const isLt500K = file.size / 1024 / 1024 / 1024 / 1024 < 4;
				if (!isJPG && !isJPEG && !isPNG && !isWEBP && !isGIF) {
					this.$message.error('上传图片只能是 JPEG/JPG/PNG 格式!');
				}
				if (!isLt500K) {
					this.$message.error('单张图片大小不能超过 4mb!');
				}
				return (isJPEG || isJPG || isPNG || isWEBP || isGIF) && isLt500K;
			},

      uploadVideoURL(file) {
				//注意哦,这里指定文件夹'image/',尝试过写在配置文件,但是各种不行,写在这里就可以
				var fileName = 'imgs/'+'editor' + getFileNameUUID() + "." + file.file.name.replace(/.+\./, "");//'.jpg';
				client().multipartUpload(fileName, file.file,{
                progress: function(percentage, cpt) {
                  // console.log('打印进度',percentage)
                }
              }).then((res)=>{
          //此处赋值,是相当于上传成功之后,手动拼接服务器地址和文件名

          this.videoDialog.show = false;
        // console.log("this.$parent.editor",this.$parent.editor);
        

        // let editor = this.$refs['editor'];
        // this.init();
        let quill = this.Quill;
        let lengthSelection = quill.getSelection().index;
        console.log("quill.getSelection().index===",lengthSelection);

        // console.log("this.$refs.editer.quill.selection.savedRange.index",this.$refs.editer.quill.selection.savedRange.index);
       
        console.log("quill.getLength()",quill.getLength());
      // 获取富文本
      // let range = quill.getSelection().index;
      // 获取光标位置:当编辑器中没有输入文本时,这里获取到的 range 为 null
      // let index = range ? range.index : 0

        // let quill = this.Quill;

        var length = quill.getLength();
        if(quill.getSelection()){
          length = quill.getSelection().index;
          // console.log("quill.getSelection().index===",length);
        }
        // console.log(quill.getSelection());
        let resp = 'https://oss-cn-beijing.aliyuncs.com/' + fileName;


    // 插入
    quill.insertEmbed(length, "video", resp);
    // 调整光标到最后
    quill.setSelection(length + 1);




    this.$forceUpdate();
        })
      },

  },
};

</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-tooltip[data-mode="video"]::before {
  content: "请输入视频地址:";
}

.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";
}
/*todo begin*/

.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='10px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='10px']::before {
  content: '10px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='11px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='11px']::before {
  content: '11px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='12px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='12px']::before {
  content: '12px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='14px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='14px']::before {
  content: '14px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='15px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='15px']::before {
  content: '15px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='16px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='16px']::before {
  content: '16px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='17px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='17px']::before {
  content: '17px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='18px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='18px']::before {
  content: '18px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='20px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='20px']::before {
  content: '20px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='24px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='24px']::before {
  content: '24px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='36px']::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='36px']::before {
  content: '36px';
}







  /*todo end*/
.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: "等宽字体";
}
</style>


<style>
/* .ql-snow .ql-picker.ql-lineheight .ql-picker-label::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item::before {
  content: '行高';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="行高"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='initial']::before {
  content: '默认';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='1']::before {
  content: '1px';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="1.5"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='1.5']::before {
  content: '1.5px';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="1.75"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='1.75']::before {
  content: '1.75px';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='2']::before {
  content: '2px';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='3']::before {
  content: '3px';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='4']::before {
  content: '4px';
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value='5']::before {
  content: '5px';
} 
.ql-snow .ql-picker.ql-lineheight {
  width: 70px;
}*/
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item::before {
  content: '字符间距';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="字符间距"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='initial']::before {
  content: '默认';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="2px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='2px']::before {
  content: '2px';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="4px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='4px']::before {
  content: '4px';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="6px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='6px']::before {
  content: '6px';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="8px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='8px']::before {
  content: '8px';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="10px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='10px']::before {
  content: '10px';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="12px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='12px']::before {
  content: '12px';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="14px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='14px']::before {
  content: '14px';
}
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-label[data-value="16px"]::before,
.ql-snow .ql-picker.ql-letterSpacing .ql-picker-item[data-value='16px']::before {
  content: '16px';
}
.ql-snow .ql-picker.ql-letterSpacing {
  width: 90px;
}

</style>




 <style>
.ql-snow .ql-picker.ql-lineheight .ql-picker-label::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item::before {
  content: '行高';
}

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

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

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

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

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

.ql-snow .ql-picker.ql-lineheight .ql-picker-label[data-value="20"]::before,
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value="20"]::before {
  content: "20";
}

.ql-snow .ql-picker.ql-lineheight {
  width: 70px;
}





</style> 

 













letterSpacing.js

import Quill from 'quill'
let Parchment = Quill.import('parchment')
// 字符间距
class letterSpacingAttributor extends Parchment.Attributor.Style {}
const letterSpacingStyle = new letterSpacingAttributor(
  'letter-spacing',
  'letterSpacing',
  {
    scope: Parchment.Scope.INLINE,
    whitelist: [
      'initial',
      '2px',
      '4px',
      '6px',
      '8px',
      '10px',
      '12px',
      '14px',
      '16px',
    ],
  }
)
export { letterSpacingStyle }

lineHeight.js

import Quill from 'quill'
let Parchment = Quill.import('parchment')

// 行高
class lineHeightAttributor extends Parchment.Attributor.Style {}
const lineHeightStyle = new lineHeightAttributor(
    // 'line-height',  'lineHeight',

    // 'lineheight',  'ql-lineheight',
 'lineheight',  'line-height',


//  'line-height','lineheight', 

     {
  scope: Parchment.Scope.INLINE,
  whitelist: ['initial', '1', '2', '3', '4', '5','20'],
})
export { lineHeightStyle }




quill-title.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-lineheight': '行高',
    'ql-letterSpacing': '字符间距',
  }
  
  export function addQuillTitle() {
    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]]
    })
  }
  
  

video.js

import { Quill } from "vue-quill-editor";
// 源码中是import直接导入,这里要用Quill.import引入
const BlockEmbed = Quill.import("blots/block/embed");
const Link = Quill.import("formats/link");
const ATTRIBUTES = ["height", "width"];
class Video extends BlockEmbed {
    static create(value) {
        const node = super.create(value);
        // 添加video标签所需的属性
        node.setAttribute("controls", "controls");
        node.setAttribute("type", "video/mp4");
        node.setAttribute("src", this.sanitize(value));
        //为了兼容 iOS 设备上,显示海报图(视频封面)
        node.setAttribute("preload", "metadata");

        // node.setAttribute("poster", value.poster);
        node.setAttribute("webkit-playsinline", "true");

        return node;
    }
    static formats(domNode) {
        return ATTRIBUTES.reduce((formats, attribute) => {
            if (domNode.hasAttribute(attribute)) {
                    formats[attribute] = domNode.getAttribute(attribute);
            }
            return formats;
        }, {});
    }
    static sanitize(url) {
        return Link.sanitize(url); // eslint-disable-line import/no-named-as-default-member
    }
    static value(domNode) {
        return domNode.getAttribute("src");
        // return {
        //     url: domNode.getAttribute('src'),
        //     poster: domNode.getAttribute('poster')
        // }
    }
    format(name, value) {
        if (ATTRIBUTES.indexOf(name) > -1) {
            if (value) {
                    this.domNode.setAttribute(name, value);
            } else {
                    this.domNode.removeAttribute(name);
            }
        } else {
            super.format(name, value);
        }
    }
    html() {
        const { video } = this.value();
        return `<a href="${video}">${video}</a>`;
    }
}
Video.blotName = "video";
Video.className = "ql-video";
Video.tagName = "video"; // 用video标签替换iframe
export default Video;


http://www.kler.cn/a/108455.html

相关文章:

  • 百度热力图数据处理,可直接用于论文
  • 【视觉SLAM:八叉树地图(Octree Map)概述】
  • 对于其他管理的理解(中)
  • Pytorch | 利用NI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
  • Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
  • microk8s使用
  • 『阿里云盘 AList Kodi』家庭影院搭建指南
  • 本机spark 通idea连接Oracle的坑
  • Redis原理-IO模型和持久化
  • LeetCode 2656. K 个元素的最大和【数学】简单
  • 基于springboot实现休闲娱乐代理售票平台系统项目【项目源码+论文说明】
  • 数据库MySQL(五):多表查询
  • 【转信创】银河麒麟:系统安全机制
  • 【LeetCode每日一题合集】2023.10.23-2023.10.29(简单的一周)
  • SparkSQL综合案例-省份维度的销售情况统计分析
  • 【深度学习】Python使用指定gpu运行代码
  • 基于 matplotlib 实现的基本排序算法的动态可视化项目源码,通过 pyaudio 增加音效,冒泡、选择、插入、快速等排序
  • RabbitMQ (4)
  • 机器学习之IV编码,分箱WOE编码
  • 云起无垠典型案例入选《2023软件供应链安全洞察》报告
  • MySQL-DQL【数据查询语言】(图码结合)
  • 首次cmake 多目录构建失败
  • 微信小程序 slot 不显示
  • 私有云:【3】NFS存储服务器的安装
  • Linux内核驱动开发的需要掌握的知识点
  • 虚拟化、容器与Docker基本介绍以及安装部署(Docker 基本管理)