效果:
1、引入vue-quill-editor
cnpm install vue-quill-editor
2、使用vue-quill-editor
template:
<!-- 上傳組件輔助-->
<el-upload class="avatar-uploader-editor" :action="serverUrl" name="img" :headers="header" :show-file-list="false"
:on-success="uploadSuccess" :on-error="uploadError"> </el-upload>
<el-upload class="avatar-uploader-editor-video" :action="serverUrl" name="video" :headers="header" :show-file-list="false"
:on-success="uploadSuccessVideo" :on-error="uploadError"> </el-upload>
<el-upload class="avatar-uploader-editor-voice" :action="serverUrl" name="voice" :headers="header" :show-file-list="false"
:on-success="uploadSuccessVoice" :on-error="uploadError"> </el-upload>
<quill-editor v-model="content" ref="myQuillEditor" style="height: 500px;margin-top: -22px;" :options="editorOption">
<div id="toolbar" slot="toolbar">
<button class="ql-bold" title="加粗">Bold</button>
<button class="ql-italic" title="斜體">Italic</button>
<button class="ql-underline" title="下劃線">underline</button>
<button class="ql-strike" title="刪除線">strike</button>
<button class="ql-blockquote" title="引用"></button>
<button class="ql-code-block" title="代碼"></button>
<button class="ql-header" value="1" title="標題1"></button>
<button class="ql-header" value="2" title="標題2"></button>
<button class="ql-list" value="ordered" title="有序列表"></button>
<button class="ql-list" value="bullet" title="無序列表"></button>
<select class="ql-header" title="段落格式">
<option selected>段落</option>
<option value="1">標題1</option>
<option value="2">標題2</option>
<option value="3">標題3</option>
<option value="4">標題4</option>
<option value="5">標題5</option>
<option value="6">標題6</option>
</select>
<select class="ql-size" title="字體大小">
<option value="10px">10px</option>
<option value="12px">12px</option>
<option value="14px">14px</option>
<option value="16px" selected>16px</option>
<option value="18px">18px</option>
<option value="20px">20px</option>
<option value="30px">30px</option>
</select>
<select class="ql-font" title="字體">
<option value="SimSun">宋體</option>
<option value="SimHei">黑體</option>
<option value="Microsoft-YaHei">微軟雅黑</option>
<option value="KaiTi">楷體</option>
<option value="FangSong">仿宋</option>
<option value="Arial">Arial</option>
</select>
<select class="ql-color" value="color" title="字體顏色"></select>
<select class="ql-background" value="background" title="背景顏色"></select>
<select class="ql-align" value="align" title="對齊"></select>
<button class="ql-clean" title="清除字體樣式"></button>
<button class="ql-image" title="圖片"></button>
<button class="ql-video" title="視頻"></button>
<button class="ql-audio" title="音頻"><i class="el-icon-headset"></i></button>
</div>
</quill-editor>
注意1:
<button class="ql-audio" title="音頻"><i class="el-icon-headset"></i></button>
此行爲新加的標籤,以此方式引用,簡單又有頁面效果!
注意2:
在上傳視頻、音頻、圖片時,用到了element上傳文件組件
- 先引入上傳文件組件,再對組件進行隱藏
- 對上傳文件組件添加class,方便後期引用!
- 對三個組件的上傳成功的方法分別寫,因爲處理方式不同
script:
import {
Quill,quillEditor
} from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
// 自定義字體大小
let Size = Quill.import('attributors/style/size')
Size.whitelist = ['10px', '12px', '14px', '16px', '18px', '20px', '30px']
Quill.register(Size, true)
// 自定義字體類型
var fonts = ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial', 'Times-New-Roman', 'sans-serif',
'宋體', '黑體']
var Font = Quill.import('formats/font')
Font.whitelist = fonts
Quill.register(Font, true)
//視頻標籤插入(樣式保持寬度100%)
import Video from './video.js'
Quill.register(Video, true)
//音頻標籤插入
import Audio from './audio.js'
Quill.register(Audio, true)
export default {
components: {
quillEditor
},
data() {
return {
content: '',
editorOption: {
placeholder: "請輸入", //文章初始化提示語
theme: "snow", // or 'bubble'
modules: {
toolbar: {
container: '#toolbar',
handlers: {
'image': function (value) {
if (value) {
// 觸發input框選擇圖片文件
document.querySelector('.avatar-uploader-editor input').click()
} else {
this.quill.format('image', false);
}
},
'video': function (value) {
if (value) {
// 觸發input框選擇視頻文件
document.querySelector('.avatar-uploader-editor-video input').click()
} else {
this.quill.format('video', false);
}
},
'audio': function (value) {
if (value) {
// 觸發input框選擇音頻文件
document.querySelector('.avatar-uploader-editor-voice input').click()
} else {
this.quill.format('audio', false);
}
},
}
}
}
},
serverUrl: '/api/files/headUpload', //上傳的圖片服務器地址
}
},
methods: {
// 富文本圖片上傳成功
uploadSuccess(res,file) {
// console.log(res)
let quill = this.$refs.myQuillEditor.quill
// 如果上傳成功
if (res.code == 0) {
// 獲取光標所在位置
let length = quill.getSelection().index;
// 插入圖片res.url爲服務器返回的圖片地址
quill.insertEmbed(length, 'image', res.data.url)
// 調整光標到最後
quill.setSelection(length + 1)
} else {
this.$message.error('圖片插入失敗')
}
},
//上傳視頻
uploadSuccessVideo(res,file) {
let quill = this.$refs.myQuillEditor.quill
// 如果上傳成功
if (res.code == 0) {
// 獲取光標所在位置
let length = quill.getSelection().index;
// 插入圖片res.url爲服務器返回的地址
quill.insertEmbed(length, 'video', res.data.url)
// 調整光標到最後
quill.setSelection(length + 1)
} else {
this.$message.error('視頻插入失敗')
}
},
//上傳音頻-處理很重要!!!!
uploadSuccessVoice(res,file) {
let quill = this.$refs.myQuillEditor.quill
// 如果上傳成功
if (res.code == 0) {
// 獲取光標所在位置
let length = quill.getSelection().index;
let BlockEmbed = Quill.import('blots/block/embed');
class AudioBlot extends BlockEmbed {
static create(value) {
let node = super.create();
node.setAttribute('src', res.data.url); //設置audio的src屬性
node.setAttribute('controls', true); //設置audio的controls,否則他將不會顯示
node.setAttribute('controlsList', 'nodownload'); //設置audio的下載功能爲不能下載
node.setAttribute('id', 'voice'); //設置一個id
return node;
}
}
AudioBlot.blotName = 'audio';
AudioBlot.tagName = 'audio'; //自定義的標籤爲audio
Quill.register(AudioBlot);
// insertEmbed(index: Number(插入的位置), type: String(標籤類型), value: any(參數,將傳入到create的方法中去), source: String = 'api')
quill.insertEmbed(length, 'audio', res.data.url);
quill.setSelection(length + 1); //光標位置向後移動一位
} else {
this.$message.error('音頻插入失敗')
}
},
// 富文本圖片/視頻/音頻上傳失敗
uploadError() {
this.$message.error('插入失敗')
},
}
}
style:
.avatar-uploader-editor{
display: inline-block;
}
.avatar-uploader-editor-video{
display: inline-block;
}
.avatar-uploader-editor-voice{
display: inline-block;
}
引入資源: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') // 控制播放器
//刪除原生video的控制條的下載或者全屏按鈕的方法
//<video controls controlsList='nofullscreen nodownload noremote footbar' ></video>
//不用哪個在下面加上哪個
node.setAttribute('controlsList', 'nofullscreen') // 控制刪除
node.setAttribute('type', 'video/mp4')
node.setAttribute('style', 'object-fit:fill;width: 100%;')
node.setAttribute('preload', 'auto') // auto - 當頁面加載後載入整個視頻 meta - 當頁面加載後只載入元數據 none - 當頁面加載後不載入視頻
node.setAttribute('playsinline', 'true')
node.setAttribute('x-webkit-airplay', 'allow')
// node.setAttribute('x5-video-player-type', 'h5') // 啓用H5播放器,是wechat安卓版特性
node.setAttribute('x5-video-orientation', 'portraint') // 豎屏播放 聲明瞭h5才能使用 播放器支付的方向,landscape橫屏,portraint豎屏,默認值爲豎屏
node.setAttribute('x5-playsinline', 'true') // 兼容安卓 不全屏播放
node.setAttribute('x5-video-player-fullscreen', 'true') // 全屏設置,設置爲 true 是防止橫屏
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}">${video}</a>`
}
}
Video.blotName = 'video' // 這裏不用改,樓主不用iframe,直接替換掉原來,如果需要也可以保留原來的,這裏用個新的blot
Video.className = 'ql-video'
Video.tagName = 'video' // 用video標籤替換iframe
export default Video
audio.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 audio extends BlockEmbed {
static create (value) {
const node = super.create(value)
// 添加audio標籤所需的屬性
node.setAttribute('controls', 'controls')
node.setAttribute('type', 'audio/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 { audio } = this.value()
return `<a href="${audio}">${audio}</a>`
}
}
audio.blotName = 'audio' // 這裏不用改,樓主不用iframe,直接替換掉原來,如果需要也可以保留原來的,這裏用個新的blot
audio.className = 'ql-audio'
audio.tagName = 'audio' // 用audio標籤替換iframe
export default audio
font.css(此文件爲修改編輯器的樣式,必引!)
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
content: "宋體";
font-family: "SimSun";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
content: "黑體";
font-family: "SimHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
content: "微軟雅黑";
font-family: "Microsoft YaHei";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
content: "楷體";
font-family: "KaiTi";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
content: "仿宋";
font-family: "FangSong";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
content: "Arial";
font-family: "Arial";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
content: "Times New Roman";
font-family: "Times New Roman";
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
content: "sans-serif";
font-family: "sans-serif";
}
.ql-font-SimSun {
font-family: "SimSun";
}
.ql-font-SimHei {
font-family: "SimHei";
}
.ql-font-Microsoft-YaHei {
font-family: "Microsoft YaHei";
}
.ql-font-KaiTi {
font-family: "KaiTi";
}
.ql-font-FangSong {
font-family: "FangSong";
}
.ql-font-Arial {
font-family: "Arial";
}
.ql-font-Times-New-Roman {
font-family: "Times New Roman";
}
.ql-font-sans-serif {
font-family: "sans-serif";
}
複製粘貼即可實現!調研N久,拿走不謝!