vue-quill-editor富文本編輯器上傳視頻功能詳解
插入視頻
富文本編輯器中插入視頻思路:劫持原來的視頻上傳事件,然后,自定義上傳組件將視頻上傳到服務(wù)器,服務(wù)器返回一個視頻鏈接,再插入到富文本編輯器中。
封裝富文本編輯器組件 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>點擊上傳</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)的上傳視頻,點擊視頻圖標(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)編輯器中沒有輸入文本時,這里獲取到的 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
},
// 文件上傳成功時的鉤子
onSuccessVideo (res) {
this.hideLoading()
if (res.code === '100') {
this.insertVideoLink(res.url)
} else {
this.$message.error(res.desc)
}
},
// 文件上傳失敗時的鉤子
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]
}
})
// 字體顏色和字體背景特殊處理,兩個在相同的盒子
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()
}但是,以上富文本編輯器有一個問題:就是 vue-quill-editor 默認(rèn)是以 iframe 保存的,插入到編輯器中的標(biāo)簽是 <iframe class=\"ql-video\" frameborder=\"0\" allowfullscreen=\"true\" src=\"https://xxx\"></iframe>,如下圖:

但是,實際使用過程中,需要的是插入一個 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,直接替換掉原來,如果需要也可以保留原來的,這里用個新的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)簽自定義屬性
有時候,還需要給 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>點擊上傳</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)的上傳視頻,點擊視頻圖標(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)編輯器中沒有輸入文本時,這里獲取到的 range 為 null
let index = range ? range.index : 0
// 沒有視頻默認(rèn)封面時,設(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
},
// 文件上傳成功時的鉤子
onSuccessVideo (res) {
this.hideLoading()
if (res.code === '100') {
this.insertVideoLink(res.url, res.cover)
} else {
this.$message.error(res.desc)
}
},
// 文件上傳失敗時的鉤子
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 屬性指定視頻下載時顯示的圖像,或者在用戶點擊播放按鈕前顯示的圖像。
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,直接替換掉原來,如果需要也可以保留原來的,這里用個新的blot
Video.className = 'ql-video' // 可添加樣式,看實際使用需要
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帶插槽問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
vue?elementui?實現(xiàn)圖片上傳后拖拽排序功能示例代碼
這篇文章主要介紹了vue?elementui?實現(xiàn)圖片上傳后拖拽排序功能,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-01-01
vue.js基于v-for實現(xiàn)批量渲染 Json數(shù)組對象列表數(shù)據(jù)示例
這篇文章主要介紹了vue.js基于v-for實現(xiàn)批量渲染 Json數(shù)組對象列表數(shù)據(jù),結(jié)合實例形式分析了vue.js使用v-for遍歷json格式數(shù)據(jù)渲染列表相關(guān)操作技巧,需要的朋友可以參考下2019-08-08
Vuex報錯之[vuex] unknown mutation type: han
這篇文章主要介紹了Vuex報錯之[vuex] unknown mutation type: handlePower問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
Element Table 自適應(yīng)高度的實現(xiàn)示例
el-table的高度不能適應(yīng)不同電腦的分辨率,也不能跟隨瀏覽器的高度變化而變化的問題,本文就來解決一下Element Table 自適應(yīng)高度,感興趣的可以了解一下2024-07-07
Vue3+cesium環(huán)境搭建的實現(xiàn)示例
本文主要介紹了Vue3+cesium環(huán)境搭建的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
解決element-ui里的下拉多選框 el-select 時,默認(rèn)值不可刪除問題
這篇文章主要介紹了解決element-ui里的下拉多選框 el-select 時,默認(rèn)值不可刪除問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

