Quill富文本的使用以及自定义图片和视频处理事件

Quill富文本的使用    官网 https://quilljs.com/docs/quickstart/

1、安装quill

使用  mpn i quill -S 

2、新建myquil.vue文件,内容如下

1<template> 2 <div class="quill-editor"> 3 <slot name="toolbar"></slot> 4 <div ref="editor"></div> 5 <div id="editor"></div> 6 <!-- 测试video标签 --> 7 <!-- <video src="https://www.w3school.com.cn/i/movie.mp4" controls="controls" width="100%" height="100%" webkit-playsinline="true" playsinline="true" x5-playsinline="true"></video> --> 8 <input id="uploadImg" ref="uploadImg" type="file" style="display:none" accept="image/jpeg, image/png" 9 @change="uploadImage"> 10 <input id="uploadVideo" ref="uploadVideo" type="file" style="display:none" accept="video/*" @change="uploadVideo"> 11 </div> 12</template> 13 14<script> 15// require sources 16import _Quill from 'quill' 17import defaultOptions from './options' 18const Quill = window.Quill || _Quill 19 20// pollfill 21if (typeof Object.assign != 'function') { 22 Object.defineProperty(Object, 'assign', { 23 value(target, varArgs) { 24 if (target == null) { 25 throw new TypeError('Cannot convert undefined or null to object') 26 } 27 const to = Object(target) 28 for (let index = 1; index < arguments.length; index++) { 29 const nextSource = arguments[index] 30 if (nextSource != null) { 31 for (const nextKey in nextSource) { 32 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 33 to[nextKey] = nextSource[nextKey] 34 } 35 } 36 } 37 } 38 return to 39 }, 40 writable: true, 41 configurable: true 42 }) 43} 44//视频处理 45const BlockEmbed = Quill.import('blots/block/embed') 46class VideoBlot extends BlockEmbed { 47 static create(value) { 48 let node = super.create() 49 node.setAttribute('src', value.url) 50 node.setAttribute('controls', value.controls) 51 node.setAttribute('width', value.width) 52 node.setAttribute('height', value.height) 53 node.setAttribute('webkit-playsinline', true) 54 node.setAttribute('playsinline', true) 55 node.setAttribute('x5-playsinline', true) 56 return node 57 } 58 59 static value(node) { 60 return { 61 url: node.getAttribute('src'), 62 controls: node.getAttribute('controls'), 63 width: node.getAttribute('width'), 64 height: node.getAttribute('height') 65 } 66 } 67} 68 69VideoBlot.blotName = 'simpleVideo' 70VideoBlot.tagName = 'video' 71Quill.register(VideoBlot) 72 73// export 74export default { 75 name: 'myquill', 76 data() { 77 return { 78 _options: {}, 79 _content: '', 80 defaultOptions 81 } 82 }, 83 props: { 84 content: String, 85 value: String, 86 disabled: { 87 type: Boolean, 88 default: false 89 }, 90 options: { 91 type: Object, 92 required: false, 93 default: () => ({}) 94 }, 95 globalOptions: { 96 type: Object, 97 required: false, 98 default: () => ({}) 99 }, //文件大小阈值,单位字节B,大于1M=1024B 100 threshold: { 101 type: Number, 102 default: 1025 103 }, 104 //宽度 105 width: { 106 type: Number, 107 default: 1080 108 }, 109 //高度 110 height: { 111 type: Number, 112 default: 100 113 }, 114 //高度 115 quality: { 116 type: Number, 117 default: 0.2 118 } 119 }, 120 mounted() { 121 this.initialize() 122 }, 123 beforeDestroy() { 124 this.quill = null 125 delete this.quill 126 }, 127 methods: { 128 // Init Quill instance 129 initialize() { 130 if (this.$el) { 131 // Options 132 this._options = Object.assign({}, this.defaultOptions, this.globalOptions, this.options) 133 134 // Instance 135 this.quill = new Quill(this.$refs.editor, this._options) 136 137 this.quill.getModule('toolbar').addHandler('image', this.uploadImageHandler) 138 139 this.quill.getModule('toolbar').addHandler('video', this.uploadVideoHandler) 140 141 this.quill.enable(false) 142 143 // Set editor content 144 if (this.value || this.content) { 145 this.quill.pasteHTML(this.value || this.content) 146 } 147 148 // Disabled editor 149 if (!this.disabled) { 150 this.quill.enable(true) 151 } 152 153 // Mark model as touched if editor lost focus 154 this.quill.on('selection-change', (range) => { 155 if (!range) { 156 this.$emit('blur', this.quill) 157 } else { 158 this.$emit('focus', this.quill) 159 } 160 }) 161 162 // Update model if text changes 163 this.quill.on('text-change', (delta, oldDelta, source) => { 164 let html = this.$refs.editor.children[0].innerHTML 165 const quill = this.quill 166 const text = this.quill.getText() 167 if (html === '<p><br></p>') html = '' 168 this._content = html 169 this.$emit('input', this._content) 170 this.$emit('change', { html, text, quill }) 171 }) 172 173 // Emit ready event 174 this.$emit('ready', this.quill) 175 } 176 }, 177 uploadImageHandler() { 178 const input = document.querySelector('#uploadImg') 179 input.value = '' 180 input.click() 181 // this.$refs.uploadImage.value = ""; 182 // this.$refs.uploadImage.click(); 183 }, 184 185 //el-upload文件上传的处理逻辑 186 beforeUpload(file) { 187 const isJPGorPNG = file.type === 'image/jpeg' || file.type === 'image/png' 188 const isLessthan2M = file.size / 1024 / 1024 < 2 //最大限制2M 189 190 if (!isJPGorPNG) { 191 this.$message.error('上传图片只能是 JPG,PNG 格式!') 192 } 193 if (!isLessthan2M) { 194 this.$message.error('上传图片大小不能超过 2MB!') 195 } 196 return isJPGorPNG && isLessthan2M 197 }, 198 uploadImage(event) { 199 var file = event.target.files[0] 200 const that = this 201 if (that.beforeUpload(file)) { 202 //上传图片大于1M进行压缩 203 if (file.size / 1024 > that.threshold) { 204 this.condenseFile(file, function(base64Codes) { 205 that.uploadImageSucess(1, base64Codes) 206 }) 207 } else { 208 this.convertBase64Url(file) 209 } 210 this.uploadImageSucess(1, this.condenseBase64) 211 } 212 }, 213 214 uploadVideoHandler() { 215 const input = document.querySelector('#uploadVideo') 216 input.value = '' 217 input.click() 218 // this.$refs.uploadVideo.value = ""; 219 // this.$refs.uploadVideo.click(); 220 }, 221 222 uploadVideo(event) { 223 if (typeof XMLHttpRequest === 'undefined') { 224 return 225 } 226 var xhr = new XMLHttpRequest() 227 const formData = new FormData() 228 // formData.append('upload_file', event.target.files[0]) 229 var text = '成功' 230 const that = this 231 xhr.onload = function onload() { 232 if (xhr.status < 200 || xhr.status >= 300) { 233 // return option.onError(this.getError(action, option, xhr)); 234 text = xhr.responseText || xhr.response 235 alert('失败' + text) 236 } 237 // console.log(text) 238 that.uploadImageSucess(2, 'https://www.w3school.com.cn/i/movie.mp4') 239 } 240 241 xhr.open('post', 'http://localhost:8088/test', true) 242 xhr.send(formData) 243 }, 244 245 uploadImageSucess(type, url) { 246 // const addImageRange = this.quill.getSelection() 247 // const newRange = 0 + (addImageRange !== null ? addImageRange.index : 0) 248 let newRange = this.quill.selection.savedRange.index 249 250 if (type === 1) { 251 // const url = 252 // 'https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100' 253 this.quill.insertEmbed(newRange, 'image', url) 254 this.quill.setSelection(1 + newRange) 255 console.log( 256 '$ uploadImageSucess 7777 his.quill.selection.savedRange.index:{this.quill.selection.savedRange.index}' 257 ) 258 } else { 259 // const url = 'https://www.w3school.com.cn/i/movie.mp4' 260 this.quill.insertEmbed(newRange, 'simpleVideo', { 261 url, 262 controls: 'controls', 263 width: '320', 264 height: '240', 265 autoplay: 'autoplay' 266 }) 267 this.quill.setSelection(1 + newRange) 268 } 269 //获取内容 270 // console.log(this.quill.getContents()) 271 // console.log(this.quill.getText()) 272 // var html = this.quill.root.innerHTML 273 // alert(html); 274 // console.log(html) 275 // var xx = this.quillGetHTML(this.quill.getContents()) 276 // alert(xx); 277 // console.log(xx) 278 }, 279 280 //获取富文本信息 281 quillGetHTML(inputDelta) { 282 //护球编辑器的内容 283 var tempCont = document.createElement('div') 284 new Quill(tempCont).setContents(inputDelta) 285 return tempCont.getElementsByClassName('ql-editor')[0].innerHTML 286 }, 287 288 //文件转换成base64字符串 289 convertBase64Url(file) { 290 var ready = new FileReader() 291 ready.readAsDataURL(file) 292 const that = this 293 ready.onload = function() { 294 var fileResult = this.result 295 that.uploadImageSucess(1, fileResult) 296 } 297 }, 298 299 //压缩文件第一步 300 condenseFile(file, callback) { 301 var ready = new FileReader() 302 ready.readAsDataURL(file) 303 const that = this 304 ready.onload = function() { 305 var fileResult = this.result 306 that.condenseCanvasDataURL(fileResult, callback) 307 } 308 }, 309 310 //压缩文件第二步,重新绘制图片,并返回压缩以后的文件的base64的值,可以把base64的值作为参数传给回调接口 311 condenseCanvasDataURL(path, callback) { 312 var img = new Image() 313 img.src = path 314 const that1 = this 315 img.onload = function() { 316 var that = this 317 //默认压缩后图片规格 318 var quality = 0.5 319 var w = that.width 320 var h = that.height 321 var scale = w / h 322 //计算图片的实际大小,设置宽高比例 323 w = w > that1.width ? that1.width : w 324 h = w / scale 325 if (that1.quality && that1.quality > 0 && that1.quality <= 1) { 326 quality = that1.quality 327 } 328 329 //生成canvas 330 var canvas = document.createElement('canvas') 331 var ctx = canvas.getContext('2d') 332 // 创建属性节点 333 var anw = document.createAttribute('width') 334 anw.nodeValue = w 335 var anh = document.createAttribute('height') 336 anh.nodeValue = h 337 canvas.setAttributeNode(anw) 338 canvas.setAttributeNode(anh) 339 ctx.drawImage(that, 0, 0, w, h) 340 341 var base64 = canvas.toDataURL('image/jpeg', quality) 342 // 回调函数返回压缩以后的文件的base64的值 343 callback(base64) 344 } 345 }, 346 347 condenseconvertBase64UrlToBlob(urlData) { 348 var arr = urlData.split(','), 349 mime = arr[0].match(/:(.*?);/)[1], 350 bstr = atob(arr[1]), 351 n = bstr.length, 352 u8arr = new Uint8Array(n) 353 while (n--) { 354 u8arr[n] = bstr.charCodeAt(n) 355 } 356 return new Blob([u8arr], { type: mime }) 357 }, 358 359 //压缩文件第一步 360 uploadFile(event) { 361 if (typeof XMLHttpRequest === 'undefined') { 362 return 363 } 364 var xhr = new XMLHttpRequest() 365 if (xhr.upload) { 366 xhr.upload.onprogress = function progress(e) { 367 if (e.total > 0) { 368 e.percent = (e.loaded / e.total) * 100 369 } 370 // option.onProgress(e);吧上传进度回调给其他的接口 371 } 372 } 373 const formData = new FormData() 374 // formData.append('upload_file', event.target.files[0]); 375 var file = event.target.files[0] 376 const that = this 377 //上传图片大于1M进行压缩 378 if (file.size / 1024 > 1025) { 379 that.photoCompress(file, { quality: 0.2 }, function(base64Codes) { 380 var bl = that.convertBase64UrlToBlob(base64Codes) 381 formData.append('files', bl, file.name) 382 }) 383 } else { 384 formData.append('files', file, file.name) 385 } 386 xhr.onerror = function error(e) { 387 alert('失败' + e) 388 } 389 var text = '成功' 390 xhr.onload = function onload() { 391 if (xhr.status < 200 || xhr.status >= 300) { 392 text = xhr.responseText || xhr.response 393 alert('失败' + text) 394 } 395 // console.log(text) 396 //上传成功,根据xhr.response的返回信息中的url,将url插入到编辑区 397 that.uploadImageSucess(1) 398 } 399 400 xhr.open('post', 'http://localhost:8084/test', true) 401 xhr.send(formData) 402 }, 403 //图片压缩处理 压缩文件第一步 404 photoCompress(file, objCompressed, objDiv) { 405 var ready = new FileReader() 406 ready.readAsDataURL(file) 407 const that = this 408 ready.onload = function() { 409 var fileResult = this.result 410 that.canvasDataURL(fileResult, objCompressed, objDiv) 411 } 412 }, 413 //压缩文件第二步 414 canvasDataURL(path, objCompressed, callback) { 415 var img = new Image() 416 img.src = path 417 img.onload = function() { 418 var that = this 419 //默认压缩后图片规格 420 var quality = 0.5 421 var w = that.width 422 var h = that.height 423 var scale = w / h 424 //实际要求 425 w = objCompressed.width || w 426 h = objCompressed.height || w / scale 427 if (objCompressed.quality && objCompressed.quality > 0 && objCompressed.quality <= 1) { 428 quality = objCompressed.quality 429 } 430 431 //生成canvas 432 var canvas = document.createElement('canvas') 433 var ctx = canvas.getContext('2d') 434 // 创建属性节点 435 var anw = document.createAttribute('width') 436 anw.nodeValue = w 437 var anh = document.createAttribute('height') 438 anh.nodeValue = h 439 canvas.setAttributeNode(anw) 440 canvas.setAttributeNode(anh) 441 ctx.drawImage(that, 0, 0, w, h) 442 443 var base64 = canvas.toDataURL('image/jpeg', quality) 444 // 回调函数返回base64的值 445 callback(base64) 446 } 447 }, 448 //上传文件压缩的处理完成之后,回调方法 压缩文件第三步 449 convertBase64UrlToBlob(urlData) { 450 var arr = urlData.split(','), 451 mime = arr[0].match(/:(.*?);/)[1], 452 bstr = atob(arr[1]), 453 n = bstr.length, 454 u8arr = new Uint8Array(n) 455 while (n--) { 456 u8arr[n] = bstr.charCodeAt(n) 457 } 458 return new Blob([u8arr], { type: mime }) 459 } 460 }, 461 watch: { 462 // Watch content change 463 content(newVal, oldVal) { 464 if (this.quill) { 465 if (newVal && newVal !== this._content) { 466 this._content = newVal 467 this.quill.pasteHTML(newVal) 468 } else if (!newVal) { 469 this.quill.setText('') 470 } 471 } 472 }, 473 // Watch content change 474 value(newVal, oldVal) { 475 if (this.quill) { 476 if (newVal && newVal !== this._content) { 477 this._content = newVal 478 this.quill.pasteHTML(newVal) 479 } else if (!newVal) { 480 this.quill.setText('') 481 } 482 } 483 }, 484 // Watch disabled change 485 disabled(newVal, oldVal) { 486 if (this.quill) { 487 this.quill.enable(!newVal) 488 } 489 } 490 } 491} 492</script>

options.js文件内容如下

1export default { 2 theme: 'snow', 3 boundary: document.body, 4 modules: { 5 toolbar: [ 6 ['bold', 'italic', 'underline', 'strike'], 7 ['blockquote', 'code-block'], 8 [{ header: 1 }, { header: 2 }], 9 [{ list: 'ordered' }, { list: 'bullet' }], 10 [{ script: 'sub' }, { script: 'super' }], 11 [{ indent: '-1' }, { indent: '+1' }], 12 [{ direction: 'rtl' }], 13 [{ size: ['small', false, 'large', 'huge'] }], 14 [{ header: [1, 2, 3, 4, 5, 6, false] }], 15 [{ color: [] }, { background: [] }], 16 [{ font: [] }], 17 [{ align: [] }], 18 ['link', 'image', 'video', 'formula'], 19 ['clean'] 20 ] 21 }, 22 // placeholder: 'Insert text here ...', 23 placeholder: '我的编辑器占位符...', 24 readOnly: false 25}

3、再其他的vue文件中引用,内容如下

1<template> 2 <!-- 使用 quill富文本编辑器组件--> 3 <myquill v-model="content" ref="myQuillEditor"></myquill> 4</template> 5 6<script> 7import myquill from "./myquil.vue"; //导入定义的myquil.vue文件 8export default { 9 name: "App", 10 components: { 11 myquill //注册quill富文本编辑器组件 12 }, 13 data() { 14 return { 15 content: "quill富文本编辑器初始值" 16 }; 17 } 18}; 19</script> 20 21<style scoped> 22.ql-container.ql-snow { 23 margin-bottom: 22px; 24} 25.ql-editor { 26 height: 500px; 27} 28</style>
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )