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-uploadv-loading="uploading"style="text-align: center"dragaction=""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-uploadname="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 beginconsole.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 endthis.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;
相关文章:

vue2 quill 视频上传 ,基于ruoyi vue,oss
包含两种上传方式,第一种点开弹新页面可选url和点击上传。本文中是第二种,自己拼的。像点击上传图片一样,直接传video文件,原创不易,纯纯踩坑; 因为现阶段能搜索到的内容,99.5%都是一样的内容&…...

YOLOv8改进实战 | 更换损失函数之MPDIOU(2023最新IOU)篇
前言 YOLOv8官方默认损失函数采用的是CIoU。本章节主要介绍如何将MPDIoU损失函数应用于目标检测YOLOv8模型。 目录 一、MPDIoU二、代码实现添加损失函数更换损失函数一、MPDIoU 论文链接:MPDIoU: A Loss for Efficient and Accurate Bounding Box Regression MPDIoU是一种基于…...

图的应用1.0-----最小生成树问题
目录 前言 生成树 1.基本概念 2.生成树的特点 3.生成树形成过程 最小生成树(Minimum Spanning Tree) 1.基本概念 2.构造方法(MST) 普里姆(Prime)算法 1.算法思想 2.代码实现 克鲁斯卡尔(Kruskal)算法 1.算法思想 2.代码…...

【计算机网络笔记】网络应用对传输服务的需求
系列文章目录 什么是计算机网络? 什么是网络协议? 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能(1)——速率、带宽、延迟 计算机网络性能(2)…...

IDEA启动报错:Command line is too long的解决办法
文章目录 1.报错现象2.解决办法验证3.最佳实践4.问题原因5.参考文献1.报错现象 在idea中启动一个spring cloud项目时,编译完成后直接报错,报错内容如下: Error running XXXApplication:Command line is too long. Shorten command line for XXXApplication or also for Sp…...

Android 中的 本地广播LocalBroadcastManager
Android 中的 本地广播LocalBroadcastManager 文章目录 Android 中的 本地广播LocalBroadcastManager一、LocalBroadcastManager 的基本作用二 、LocalBroadcastManager 的基本使用1、包的导入(1)Android 源码中bp文件的导入:(2&a…...

题目 1120: C语言训练-“水仙花数“问题2python详解)——练气三层后期
✨博主:命运之光 🦄专栏:算法修炼之练气篇(C\C版) 🍓专栏:算法修炼之筑基篇(C\C版) 🍒专栏:算法修炼之练气篇(Python版) ✨…...

sheng的学习笔记-【中】【吴恩达课后测验】Course 3 - 结构化机器学习项目 - 第二周测验
课程3_第2周_测验题 目录:目录 要解决的问题 ① 为了帮助你练习机器学习的策略,本周我们将介绍另一个场景,并询问你将如何行动。 ② 我们认为这个在机器学习项目中工作的“模拟器”将给出一个任务,即领导一个机器学习项目可能…...

基于Pytorch的驾驶员分心行为实时检测
本文使用深度学习和Pytorch(PyTorch 2.0.1\Torchvision 0.15.2)实时检测驾驶员的分心行为,并附录完整代码。 检测分心驾驶是现代汽车中最重要的功能之一。无论是自动驾驶汽车还是其它高端汽车,都配备了驾驶员监控系统,以持续跟踪驾驶员的行为。这对确保驾驶员保持目光在道路…...

【uniapp】小程序开发7:自定义组件、自动注册组件
一、自定义轮播图组件、自动注册 以首页轮播图组件为例。 1、创建组件文件src/components/my-swipper.vue 代码如下: <template><view><view class"uni-margin-wrap"><swiper class"swiper" circular :indicator-dots…...

Modbus转MQTT以太网网关MQT-802主要特点和典型应用
随着社会的快速发展,物联网已经潜移默化地深入工控行业的各个领域,其高效的资源整合和强大的数据采集能力,深受客户的喜爱。上海泗博为实现客户在云端平台接收处理世界万物的信息以及实现远程控制,精心打造一款全新物联网产品&…...

Go学习第五章——函数与包
Go学习第五章——函数与包 1 函数1.1 基本语法1.2 函数多返回值1.3 函数的可见性和包级函数1.4 函数调用机制底层原理1.5 值类型和引用类型1.6 注意事项和细节1.7 逃逸机制(补,可不看) 2 包2.1 快速入门2.2 包的使用细节 3 函数详细讲解3.1 递…...

【Python 常用脚本及命令系列 5 -- 如何使用 BeautifulSoup 解析CSDN网页表格中的数据】
文章目录 Python BeautifulSoup 介绍CSDN 网页表格解析开发问题总结 Python BeautifulSoup 介绍 BeautifulSoup是一个Python库,用于解析HTML和XML文档。它常常用于网络爬虫来提取网页中的信息。 以下是BeautifulSoup的一些主要特性: 解析HTMLÿ…...

OpenFeign实现分析、源码解析
什么是openfeign? 是springcloud全家桶的组件之一,其核心作用是为Rest API提供高效简洁的rpc调用方式。 为什么只定义接口而没有实现类? 源码解读(省略) 总结: 源码分析:如何发送http请求? …...

2023 10月最新Vmd 下载安装教程,WindowsLinux
文章目录 下载Vmdwindows版本安装LINUX版本安装 下载Vmd 谷歌搜索VMD 点击左下角download VMD 可选择对应版本 注:点击后会出现输入用户名和密码,由于我已注册,界面不见了,所以直接描述一下。 输入用户名和密码然后会出现让登记…...

Photoshop(PS)安装教程(图文教程超详细)
目录 一.简介 二.安装步骤 软件:PS版本:2023语言:简体中文大小:3.20G系统要求:Win10(1903)及以上版本,64位操作系统硬件要求:CPU2.0GHz 内存8G(或更高,不支…...

C++模版进阶
一、非类型模版参数 之前学习的模版,参数一般是某种类型,但其实非类型的参数也可以定义在模版里面,但也有一定的限制,只可以定义整形家族的参数,而且具有常量性 注意: 1. 浮点数、类对象以及字符串是不允…...

CloudCompare
CloudCompare 源码编译Windows 功能格式转换 源码编译 Windows 源码编译出来的默认基本不带几个插件,包括保存为 .las 的功能 可以直接从 https://www.danielgm.net/cc/ 下载编译好的版本,插件比较多。也有免安装版本 cmake -B build -S . -G "Vi…...

【算法小课堂】深入理解前缀和算法
前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。 我们通过一个例子来理解前缀和算法的优势: 一维前缀和: ww…...

元对象系统功能
元对象系统功能 建立工程 布局页面 布局页面 修改原件名称 建立元对象 函数作为接口 增加一些固定的属性 #------------------------------------------------- # # Project created by QtCreator 2023-10-24T21:54:44 # #----------------------------…...

【2024秋招】小米中间件后端开发一面2023-9-13-base武汉
1 自我介绍 2 快手实习 2.1 讲讲你写的curd启动器,做了哪些工作呢 答: 2.2 网上也有一些开源的curd代码生成器,你为什么需要自研呢(重要) 答: (1)这个必须得自研,因…...

SpringMVC Day 01:入门案例
前言 在我们的日常工作和学习中,Web 开发是一个无法回避的重要环节。而在 Java Web 开发领域,SpringMVC 无疑是一个重量级选手。它以其灵活性、强大功能和清晰的 MVC 结构,赢得了大量开发者的青睐。但是,对于初学者来说ÿ…...

docker、docker-compose安装教程,很详细
docker、docker-compose安装教程,很详细 一、卸载旧版1、查看有没有安装过旧版2、停止docker3、删除安装过docker的相关包4、删除docker相关的镜像和容器 二、docker安装1、设置阿里云镜像2、查看所有docker3、安装最新版本4、安装指定版本 三、使用前准备1、启动do…...

源代码转换:Tangible Software Solutions 23.10 Crack
Tangible Software Solutions The Most Accurate and Reliable Source Code Converters Convert between C#, Java, C, Python, & VB, while saving countless hours of painstaking work and valuable time.源代码转换 Key Benefits Saves valuable time Accurate and com…...

SAD notes
ESKF 总结 prediction 更新误差先验 F F F通过3.42来算 得到 这里有点绕的一点是: 误差状态的 F F F牵涉到名义状态, 而名义状态又需要在时间上推进更新 其中, F中的名义状态的推进通过公式3.41得到, (名义状态不考虑误差, 这一点从3.41d, 3.41e可以看出, 误差状态只考虑…...

[SQL开发笔记]BETWEEN操作符:选取介于两个值之间的数据范围内的值
一、功能描述: BETWEEN操作符:选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。 二、BETWEEN操作符语法详解: BETWEEN操作符语法: SELECT column1, column2,…FROM table_nameWHERE column BETWEEN val…...

Babylonjs学习笔记(三)——创建天空盒
书接上回,这里讨论创建天空盒!!! // 天空盒const envTex CubeTexture.CreateFromPrefilteredData(./env/environmentSpecular.env,scene)scene.environmentTexture envTex;scene.createDefaultSkybox(envTex,true)scene.environ…...

【计算机网络】文件传输协议FTP和SFTP
1. 介绍 SFTP(SSH文件传输协议)和FTP(文件传输协议)都是用于在计算机之间传输文件的网络协议。FTP和SFTP都位于OSI模型中的应用层。这两种协议用于文件传输和管理,是应用层协议,因此它们工作在OSI模型的最…...

Python 编程语言的介绍
Python 是一种高级、动态类型的解释型语言。由 Guido van Rossum 于1989年底发明,并在1991年首次发布。Python 的设计哲学强调代码的可读性和简洁的语法,特别是使用缩进来表示代码块,这使得开发者能够用更少的代码表达想法。 基础概念: 语法…...

centos服务器搭建安装Gitlab教程使用教程
1、更新服务器: sudo yum update -y && sudo yum upgrade -y 2、下载Gitlab的RPM包 https://packages.gitlab.com/gitlab/gitlab-cece表示开源el表示centos 选64位el8对应CentOS8 本教程以centos8为例,在服务器中,下载centos8的…...