vue-quill-editor富文本編輯器上傳視頻功能詳解
插入視頻
富文本編輯器中插入視頻思路:劫持原來的視頻上傳事件,然后,自定義上傳組件將視頻上傳到服務(wù)器,服務(wù)器返回一個(gè)視頻鏈接,再插入到富文本編輯器中。
封裝富文本編輯器組件 quill.vue
:
<!--富文本編輯器--> <template> <div class="rich-text-editor-container" v-loading="loading"> <quill-editor :content="content" :options="editorOptions" class="ql-editor" ref="myQuillEditor" @change="onEditorChange($event)"> </quill-editor> <!--視頻上傳彈窗--> <div> <el-dialog :close-on-click-modal="false" width="50%" style="margin-top: 1px" title="視頻上傳" :visible.sync="videoForm.show" append-to-body> <el-tabs v-model="videoForm.activeName"> <el-tab-pane label="添加視頻鏈接" name="first"> <el-input v-model="videoForm.videoLink" placeholder="請輸入視頻鏈接" clearable></el-input> <el-button type="primary" size="small" style="margin: 20px 0px 0px 0px " @click="insertVideoLink(videoForm.videoLink)">確認(rèn) </el-button> </el-tab-pane> <el-tab-pane label="本地視頻上傳" name="second"> <el-upload v-loading="loading" style="text-align: center;" drag :action="uploadVideoConfig.uploadUrl" accept="video/*" :name="uploadVideoConfig.name" :before-upload="onBeforeUploadVideo" :on-success="onSuccessVideo" :on-error="onErrorVideo" :multiple="false"> <i class="el-icon-upload"></i> <div class="el-upload__text">將文件拖到此處,或<em>點(diǎn)擊上傳</em></div> <div class="el-upload__tip" slot="tip">只能上傳MP4文件,且不超過{{uploadVideoConfig.maxSize}}M</div> </el-upload> </el-tab-pane> </el-tabs> </el-dialog> </div> </div> </template> <script> // require styles import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' import { quillEditor } from 'vue-quill-editor' // 工具欄 const toolbarOptions = [ ["bold", "italic", "underline", "strike"], // 加粗 斜體 下劃線 刪除線 ["blockquote", "code-block"], // 引用 代碼塊 [{ list: "ordered" }, { list: "bullet" }], // 有序、無序列表 [{ indent: "-1" }, { indent: "+1" }], // 縮進(jìn) [{ size: ["small", false, "large", "huge"] }], // 字體大小 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 標(biāo)題 [{ color: [] }, { background: [] }], // 字體顏色、字體背景顏色 [{ align: [] }], // 對齊方式 ["clean"], // 清除文本格式 ['video'] // 視頻 ] export default { name: 'RichTextEditor', components: { quillEditor }, props: { /* 編輯器的內(nèi)容 */ content: { // 返回的html片段 type: String, default: '' }, // 視頻上傳配置 uploadVideoConfig: { type: Object, default () { return { uploadUrl: '', // 上傳地址 maxSize: 10, // 圖片上傳大小限制,默認(rèn)不超過2M name: 'Filedata' // 圖片上傳字段 } } } }, data () { let _self = this; return { loading: false, // 加載loading editorOptions: { placeholder: '', theme: 'snow', // or 'bubble' modules: { toolbar: { container: toolbarOptions, // 工具欄 handlers: { 'video': () => { // 覆蓋默認(rèn)的上傳視頻,點(diǎn)擊視頻圖標(biāo),顯示彈窗 _self.videoForm.show = true } } } } }, // 視頻上傳變量 videoForm: { show: false, // 顯示插入視頻鏈接彈框的標(biāo)識 videoLink: '', activeName: 'first' } } }, mounted () { }, methods: { // 文本編輯 onEditorChange ({ quill, html, text }) { console.log('editor changed:', quill, html, text) console.log('html.replace(/<[^>]*>|/g:', html.replace(/<[^>]*>|/g)) this.$emit('update:content', html) this.$emit('change', html) }, hideLoading () { this.loading = false }, /** --------- 視頻上傳相關(guān) start --------- */ insertVideoLink (videoLink) { if (!videoLink) return this.$message.error('視頻地址不能為空!') this.videoForm.show = false let quill = this.$refs['myQuillEditor'].quill // 獲取富文本 let range = quill.getSelection() // 獲取光標(biāo)位置:當(dāng)編輯器中沒有輸入文本時(shí),這里獲取到的 range 為 null let index = range ? range.index : 0 // 在光標(biāo)所在位置 插入視頻 quill.insertEmbed(index, 'video', videoLink) // 調(diào)整光標(biāo)到最后 quill.setSelection(index + 1) }, // el-文件上傳組件 onBeforeUploadVideo (file) { this.loading = true let acceptArr = ['video/mp4'] const isVideo = acceptArr.includes(file.type) const isLt1M = file.size / 1024 / 1024 < this.uploadVideoConfig.maxSize if (!isVideo) { this.hideLoading() this.$message.error('只能上傳mp4格式文件!') } if (!isLt1M) { this.hideLoading() this.$message.error(`上傳文件大小不能超過 ${this.uploadVideoConfig.maxSize}MB!`) } return isLt1M && isVideo }, // 文件上傳成功時(shí)的鉤子 onSuccessVideo (res) { this.hideLoading() if (res.code === '100') { this.insertVideoLink(res.url) } else { this.$message.error(res.desc) } }, // 文件上傳失敗時(shí)的鉤子 onErrorVideo () { this.hideLoading() this.$message.error('上傳失敗') }, /**--------- 視頻上傳相關(guān) end --------- */ } } </script> <style lang='less'> .rich-text-editor-container .ql-container { height: 300px; } .rich-text-editor-container .ql-editor { padding: 0; } .rich-text-editor-container .ql-tooltip { left: 5px !important; } </style>
頁面使用封裝的富文本編輯器組件,新建 add.vue
:
<template> <div> <editor :content="content" v-model="content" :height="480" /> </div> </template> <script> import Editor from "./quill"; export default { components: { Editor }, data() { return { content: "" }; } }; </script>
設(shè)置工具欄中文標(biāo)題
新建 quill-title.js
:
// 給工具欄設(shè)置title const titleConfig = { 'ql-bold': '加粗', 'ql-font': '字體', 'ql-code': '插入代碼', 'ql-italic': '斜體', 'ql-link': '添加鏈接', 'ql-color': '字體顏色', 'ql-background': '背景顏色', 'ql-size': '字體大小', 'ql-strike': '刪除線', 'ql-script': '上標(biāo)/下標(biāo)', 'ql-underline': '下劃線', 'ql-blockquote': '引用', 'ql-header': '標(biāo)題', 'ql-indent': '縮進(jìn)', 'ql-list': '列表', 'ql-align': '文本對齊', 'ql-direction': '文本方向', 'ql-code-block': '代碼塊', 'ql-formula': '公式', 'ql-image': '圖片', 'ql-video': '視頻', 'ql-clean': '清除字體樣式' } export function setQuillTitle () { const oToolBar = document.querySelector('.ql-toolbar') const aButton = oToolBar.querySelectorAll('button') const aSelect = oToolBar.querySelectorAll('select') aButton.forEach(function (item) { if (item.className === 'ql-script') { item.value === 'sub' ? item.title = '下標(biāo)' : item.title = '上標(biāo)' } else if (item.className === 'ql-indent') { item.value === '+1' ? item.title = '向右縮進(jìn)' : item.title = '向左縮進(jìn)' } else { item.title = titleConfig[item.className] } }) // 字體顏色和字體背景特殊處理,兩個(gè)在相同的盒子 aSelect.forEach(function (item) { if (item.className.indexOf('ql-background') > -1) { item.previousSibling.title = titleConfig['ql-background'] } else if (item.className.indexOf('ql-color') > -1) { item.previousSibling.title = titleConfig['ql-color'] } else { item.parentNode.title = titleConfig[item.className] } }) }
修改 quill.vue
文件:
// 設(shè)置title import { setQuillTitle } from './quill-title.js'
在 mounted
中調(diào)用 setQuillTitle
方法:
mounted () { // 初始給編輯器設(shè)置title setQuillTitle() }
但是,以上富文本編輯器有一個(gè)問題:就是 vue-quill-editor
默認(rèn)是以 iframe
保存的,插入到編輯器中的標(biāo)簽是 <iframe class=\"ql-video\" frameborder=\"0\" allowfullscreen=\"true\" src=\"https://xxx\"></iframe>
,如下圖:
但是,實(shí)際使用過程中,需要的是插入一個(gè) video
標(biāo)簽。
修改視頻iframe標(biāo)簽為video
新建 quill-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標(biāo)簽所需的屬性 node.setAttribute('controls', 'controls') node.setAttribute('type', 'video/mp4') node.setAttribute('src', this.sanitize(value)) 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') } 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}" rel="external nofollow" rel="external nofollow" >${video}</a>` } } Video.blotName = 'video' // 這里不用改,樓主不用iframe,直接替換掉原來,如果需要也可以保留原來的,這里用個(gè)新的blot Video.className = 'ql-video' Video.tagName = 'video' // 用video標(biāo)簽替換iframe export default Video
修改 quill.vue
文件:
import { quillEditor, Quill } from 'vue-quill-editor' // 這里引入修改過的video模塊并注冊 import Video from './video' Quill.register(Video, true)
如下圖:
在頁面可以看到,插入到編輯器中的標(biāo)簽是 <video class=\"ql-video\" controls=\"controls\" type=\"video/mp4\" src=\"xxx\"></video>
,如下圖:
設(shè)置video 標(biāo)簽自定義屬性
有時(shí)候,還需要給 video
標(biāo)簽添加一些自定義的屬性:修改 quill-video.js
文件:
修改 quill.vue
文件:
這樣,就給 video
標(biāo)簽加上了自定義屬性:<video class=\"ql-video\" controls=\"controls\" playsinline=\"true\" webkit-playsinline=\"true\" type=\"video/mp4\" poster=\"https://xxx\" src=\"https://xxx\"></video>
。
PS:以此類推,也可以給 video
標(biāo)簽添加一些其它屬性,例如 width,height
等等啦,只需要照著上面的方式修改對應(yīng)地方即可。
頁面效果:
完整 quill.vue
代碼:
<!--富文本編輯器--> <template> <div class="rich-text-editor-container" v-loading="loading"> <quill-editor :content="content" :options="editorOptions" class="ql-editor" ref="myQuillEditor" @change="onEditorChange($event)"> </quill-editor> <!--視頻上傳彈窗--> <div> <el-dialog :close-on-click-modal="false" width="50%" style="margin-top: 1px" title="視頻上傳" :visible.sync="videoForm.show" append-to-body> <el-tabs v-model="videoForm.activeName"> <el-tab-pane label="添加視頻鏈接" name="first"> <el-input v-model="videoForm.videoLink" placeholder="請輸入視頻鏈接" clearable></el-input> <el-button type="primary" size="small" style="margin: 20px 0px 0px 0px " @click="insertVideoLink(videoForm.videoLink,'')">確認(rèn) </el-button> </el-tab-pane> <el-tab-pane label="本地視頻上傳" name="second"> <el-upload v-loading="loading" style="text-align: center;" drag :action="uploadVideoConfig.uploadUrl" accept="video/*" :name="uploadVideoConfig.name" :before-upload="onBeforeUploadVideo" :on-success="onSuccessVideo" :on-error="onErrorVideo" :multiple="false"> <i class="el-icon-upload"></i> <div class="el-upload__text">將文件拖到此處,或<em>點(diǎn)擊上傳</em></div> <div class="el-upload__tip" slot="tip">只能上傳MP4文件,且不超過{{uploadVideoConfig.maxSize}}M</div> </el-upload> </el-tab-pane> </el-tabs> </el-dialog> </div> </div> </template> <script> // require styles import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' import { quillEditor, Quill } from 'vue-quill-editor' // 這里引入修改過的video模塊并注冊 import Video from './video' Quill.register(Video, true) // 設(shè)置title import { setQuillTitle } from './quill-title.js' // 工具欄 const toolbarOptions = [ ["bold", "italic", "underline", "strike"], // 加粗 斜體 下劃線 刪除線 ["blockquote", "code-block"], // 引用 代碼塊 [{ list: "ordered" }, { list: "bullet" }], // 有序、無序列表 [{ indent: "-1" }, { indent: "+1" }], // 縮進(jìn) [{ size: ["small", false, "large", "huge"] }], // 字體大小 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 標(biāo)題 [{ color: [] }, { background: [] }], // 字體顏色、字體背景顏色 [{ align: [] }], // 對齊方式 ["clean"], // 清除文本格式 ['video'] // 視頻 ] export default { name: 'RichTextEditor', components: { quillEditor }, props: { /* 編輯器的內(nèi)容 */ content: { // 返回的html片段 type: String, default: '' }, // 視頻上傳配置 uploadVideoConfig: { type: Object, default () { return { uploadUrl: '', // 上傳地址 maxSize: 10, // 圖片上傳大小限制,默認(rèn)不超過2M name: 'Filedata' // 圖片上傳字段 } } } }, data () { let _self = this; return { loading: false, // 加載loading editorOptions: { placeholder: '', theme: 'snow', // or 'bubble' modules: { toolbar: { container: toolbarOptions, // 工具欄 handlers: { 'video': () => { // 覆蓋默認(rèn)的上傳視頻,點(diǎn)擊視頻圖標(biāo),顯示彈窗 _self.videoForm.show = true } } } } }, // 視頻上傳變量 videoForm: { show: false, // 顯示插入視頻鏈接彈框的標(biāo)識 videoLink: '', activeName: 'first' } } }, mounted () { // 初始給編輯器設(shè)置title setQuillTitle() }, methods: { // 文本編輯 onEditorChange ({ quill, html, text }) { console.log('editor changed:', quill, html, text) console.log('html.replace(/<[^>]*>|/g:', html.replace(/<[^>]*>|/g)) this.$emit('update:content', html) this.$emit('change', html) }, hideLoading () { this.loading = false }, /** --------- 視頻上傳相關(guān) start --------- */ insertVideoLink (videoLink, poster) { if (!videoLink) return this.$message.error('視頻地址不能為空!') this.videoForm.show = false let quill = this.$refs['myQuillEditor'].quill // 獲取富文本 let range = quill.getSelection() // 獲取光標(biāo)位置:當(dāng)編輯器中沒有輸入文本時(shí),這里獲取到的 range 為 null let index = range ? range.index : 0 // 沒有視頻默認(rèn)封面時(shí),設(shè)置默認(rèn)視頻封面圖片 const img = poster ? poster : 'https://cube.elemecdn.com/6/94/4d3ea53c084bad6931a56d5158a48jpeg.jpeg' // 在光標(biāo)所在位置 插入視頻 quill.insertEmbed(index, 'video', { url: videoLink, poster: img }) // 調(diào)整光標(biāo)到最后 quill.setSelection(index + 1) }, // el-文件上傳組件 onBeforeUploadVideo (file) { this.loading = true let acceptArr = ['video/mp4'] const isVideo = acceptArr.includes(file.type) const isLt1M = file.size / 1024 / 1024 < this.uploadVideoConfig.maxSize if (!isVideo) { this.hideLoading() this.$message.error('只能上傳mp4格式文件!') } if (!isLt1M) { this.hideLoading() this.$message.error(`上傳文件大小不能超過 ${this.uploadVideoConfig.maxSize}MB!`) } return isLt1M && isVideo }, // 文件上傳成功時(shí)的鉤子 onSuccessVideo (res) { this.hideLoading() if (res.code === '100') { this.insertVideoLink(res.url, res.cover) } else { this.$message.error(res.desc) } }, // 文件上傳失敗時(shí)的鉤子 onErrorVideo () { this.hideLoading() this.$message.error('上傳失敗') }, /**--------- 視頻上傳相關(guān) end --------- */ } } </script> <style lang='less'> .rich-text-editor-container .ql-container { height: 300px; } .rich-text-editor-container .ql-editor { padding: 0; } .rich-text-editor-container .ql-tooltip { left: 5px !important; } </style>
完整 quill-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) { let node = super.create() // 添加video標(biāo)簽所需的屬性 node.setAttribute('controls', 'controls') node.setAttribute('playsinline', 'true') node.setAttribute('webkit-playsinline', 'true') node.setAttribute('type', 'video/mp4') // poster 屬性指定視頻下載時(shí)顯示的圖像,或者在用戶點(diǎn)擊播放按鈕前顯示的圖像。 node.setAttribute('poster', value.poster) node.setAttribute('src', this.sanitize(value.url)) 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) { // 設(shè)置自定義的屬性值 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}" rel="external nofollow" rel="external nofollow" >${video}</a>` } } Video.blotName = 'video' // 這里不用改,不用iframe,直接替換掉原來,如果需要也可以保留原來的,這里用個(gè)新的blot Video.className = 'ql-video' // 可添加樣式,看實(shí)際使用需要 Video.tagName = 'video' // 用video標(biāo)簽替換iframe export default Video
總結(jié)
到此這篇關(guān)于vue-quill-editor富文本編輯器上傳視頻功能詳解的文章就介紹到這了,更多相關(guān)vue-quill-editor富文本編輯器上傳視頻內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue?elementui二次封裝el-table帶插槽問題
這篇文章主要介紹了vue?elementui二次封裝el-table帶插槽問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08vue?elementui?實(shí)現(xiàn)圖片上傳后拖拽排序功能示例代碼
這篇文章主要介紹了vue?elementui?實(shí)現(xiàn)圖片上傳后拖拽排序功能,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01vue.js基于v-for實(shí)現(xiàn)批量渲染 Json數(shù)組對象列表數(shù)據(jù)示例
這篇文章主要介紹了vue.js基于v-for實(shí)現(xiàn)批量渲染 Json數(shù)組對象列表數(shù)據(jù),結(jié)合實(shí)例形式分析了vue.js使用v-for遍歷json格式數(shù)據(jù)渲染列表相關(guān)操作技巧,需要的朋友可以參考下2019-08-08Vuex報(bào)錯(cuò)之[vuex] unknown mutation type: han
這篇文章主要介紹了Vuex報(bào)錯(cuò)之[vuex] unknown mutation type: handlePower問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Element Table 自適應(yīng)高度的實(shí)現(xiàn)示例
el-table的高度不能適應(yīng)不同電腦的分辨率,也不能跟隨瀏覽器的高度變化而變化的問題,本文就來解決一下Element Table 自適應(yīng)高度,感興趣的可以了解一下2024-07-07在Vue項(xiàng)目中使用d3.js的實(shí)例代碼
這篇文章主要介紹了在Vue項(xiàng)目中使用d3.js的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值價(jià)值,需要的朋友可以參考下2018-05-05Vue3+cesium環(huán)境搭建的實(shí)現(xiàn)示例
本文主要介紹了Vue3+cesium環(huán)境搭建的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08解決element-ui里的下拉多選框 el-select 時(shí),默認(rèn)值不可刪除問題
這篇文章主要介紹了解決element-ui里的下拉多選框 el-select 時(shí),默認(rèn)值不可刪除問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08