踩在這個作者的肩膀上實現(xiàn)了自定義圖片/視頻上傳 https://sanshui.blog.csdn.net/article/details/81464100状植,原文章有些地方有點沒說清楚的地方怎棱,稍作了些修改窖贤。也解決了文中未提及的bug火邓。
復(fù)制以下代碼即可使用
一堆巧,安裝 vue-quill-editor
npm install vue-quill-editor --save
二勤揩,
創(chuàng)建QuillEditor組件
<template>
<div>
<quill-editor
ref="myTextEditor"
v-model="contentValue"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@ready="onEditorReady($event)"
@change="onEditorChange($event)"
class="cfpa-quill-editor"
:style="{ height: quillEditorHeight + 'px' }"
>
<div id="toolbar" slot="toolbar">
<!-- Add a bold button -->
<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="標(biāo)題1"></button>
<button class="ql-header" value="2" title="標(biāo)題2"></button>
<!--Add list -->
<button class="ql-list" value="ordered" title="有序列表"></button>
<button class="ql-list" value="bullet" title="無序列表"></button>
<!-- Add font size dropdown -->
<select class="ql-header" title="段落格式">
<option selected>段落</option>
<option value="1">標(biāo)題1</option>
<option value="2">標(biāo)題2</option>
<option value="3">標(biāo)題3</option>
<option value="4">標(biāo)題4</option>
<option value="5">標(biāo)題5</option>
<option value="6">標(biāo)題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>
</select>
<select class="ql-font" title="字體">
<option value="SimSun" selected="selected"></option>
<option value="SimHei"></option>
<option value="Microsoft-YaHei"></option>
<option value="KaiTi"></option>
<option value="FangSong"></option>
<option value="Arial"></option>
<!-- <option value="Times-New-Roman"></option>
<option value="sans-serif"></option> -->
</select>
<!-- Add subscript and superscript buttons -->
<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-link" title="超鏈接"></button> -->
<!-- You can also add your own -->
<button
id="custom-button"
@click.prevent="fnOpenUploadImage"
title="圖片"
>
<i class="iconfont el-icon-picture"></i>
</button>
<button
id="custom-button"
@click.prevent="fnOpenUploadVideo"
title="視頻"
>
<i class="iconfont el-icon-video-play"></i>
</button>
</div>
</quill-editor>
<div :style="wordCount" v-if="wordCount" class="cfpa-quill-wordCount">
<div class="cfpa-quill-wordCount-text">
當(dāng)前已經(jīng)輸入<span style="color: red">{{ contentLength }}</span
>個字符
</div>
</div>
<el-dialog
:title="title"
width="30%"
:visible.sync="dialogFnOpenUpload"
:close-on-click-modal="false"
>
<file-upload
:data_extra="data_extra"
@fnUploadSucess="fnUploadSucess"
@fnCloseDialog="dialogFnOpenUpload = false"
ref="fileUpload"
:types="
uploadType === 'video'
? ['video/mp4']
: ['image/jpeg', 'image/png', 'image/jpg', 'image/bmp']
"
></file-upload>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogFnOpenUpload = false">取 消</el-button>
<el-button type="primary" @click="fnOpenUploadSubmit">確 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import fileUpload from './upload'
import { Quill, quillEditor } from 'vue-quill-editor'
// 這里引入修改過的video模塊并注冊
import Video from './upload/video'
Quill.register(Video, true)
// 圖片可收縮
import { ImageDrop } from 'quill-image-drop-module'
import ImageResize from 'quill-image-resize-module'
Quill.register('modules/imageDrop', ImageDrop)
Quill.register('modules/imageResize', ImageResize)
// 自定義字體大小
let Size = Quill.import('attributors/style/size')
Size.whitelist = ['10px', '12px', '14px', '16px', '18px', '20px']
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)
export default {
name: 'editor',
components: {
quillEditor,
fileUpload
},
props: {
value: {
type: String,
default: ''
},
editorHeight: {
type: Number,
default: 355
},
editorWordCount: {
type: Number,
default: 0
}
},
data () {
return {
contentValue: '',
preContent: '',
dialogFnOpenUpload: false,
uploadType: 'image',
editorOption: {
modules: {
toolbar: '#toolbar',
history: {
delay: 1000,
maxStack: 50,
userOnly: false
},
imageDrop: true,
imageResize: {
displayStyles: {
backgroundColor: 'black',
border: 'none',
color: 'white'
},
modules: ['Resize', 'DisplaySize', 'Toolbar']
}
},
placeholder: '請編寫內(nèi)容...'
},
data_extra: {
parentId: 0,
fileName: ''
},
contentLength: 0,
wordCount: '',
title: '添加圖片',
quillEditorHeight: 300
}
},
computed: {
editor () {
return this.$refs.myTextEditor.quill
}
},
methods: {
/**
* @description [onEditorBlur 失去焦點]
* @author zoumiao
* @param {Object} editor 返回的quill對象
* @return {null} [沒有返回]
*/
onEditorBlur (editor) {
this.$emit('editorBlur')
},
/**
* @description [onEditorFocus 獲取焦點]
* @author zoumiao
* @param {Object} editor 返回的quill對象
* @return {null} [沒有返回]
*/
onEditorFocus (editor) {
this.$emit('editorFocus')
},
/**
* @description [onEditorReady 可以輸入]
* @author zoumiao
* @param {Object} editor 返回的quill對象
* @return {null} [沒有返回]
*/
onEditorReady (editor) {
},
/**
* @description [onEditorChange 輸入文本改變事件]
* @author zoumiao
* @param {Object} editor 返回的編輯對象{html, text, quill}
* @return {null} [沒有返回]
*/
onEditorChange (editor) {
console.log(editor);
let html = editor.html
this.preContent = html
this.$emit('input', html)
this.contentLength = editor.text.trim().length
},
/**
* @description [fnOpenUploadImage 上傳圖片]
* @author zoumiao
* @return {null} [沒有返回]
*/
fnOpenUploadImage () {
this.uploadType = 'image'
this.title = '添加圖片'
this.dialogFnOpenUpload = true
},
/**
* @description [fnOpenUploadVideo 上傳視頻]
* @author zoumiao
* @return {null} [沒有返回]
*/
fnOpenUploadVideo () {
this.uploadType = 'video'
this.title = '添加視頻'
this.dialogFnOpenUpload = true
},
/**
* [fnOpenUploadSubmit 提交上傳文件]
* @author zoumiao
* @return {null} [沒有返回]
*/
async fnOpenUploadSubmit () {
await this.$refs.fileUpload.$refs.upload.submit()
},
/**
* [fnUploadSucess 上傳文件成功]
* @author zoumiao
* @param {Array} uploadFileUrlList [上傳文件返回的url]
* @return {null} [沒有返回]
*/
fnUploadSucess (uploadFileUrlList) {
this.editor.focus()
this.editor.insertEmbed(this.editor.getSelection().index, this.uploadType, uploadFileUrlList)
this.dialogFnOpenUpload = false
}
},
created () {
this.quillEditorHeight = document.body.clientHeight - this.editorHeight
this.contentValue = this.value
this.contentLength = this.editorWordCount || 0
},
mounted () {
let toolbar = document.querySelector('div.ql-toolbar.ql-snow')
if (toolbar) {
let toolbarHeight = toolbar.offsetHeight
this.wordCount = {
'top': `${toolbarHeight}px`
}
return
}
this.wordCount = {
'top': '42px'
}
},
watch: {
// Watch content change
value (newVal, oldVal) {
if (newVal && newVal !== this.preContent) {
this.preContent = newVal
this.contentValue = newVal
} else if (!newVal) {
this.contentValue = ''
}
}
}
}
</script>
<style lang="scss">
.cfpa-quill-editor {
line-height: 24px;
.ql-snow {
background-color: #ffffff;
}
}
.cfpa-quill-wordCount {
background-color: #ffffff;
position: relative;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
line-height: 20px;
font-size: 12px;
.cfpa-quill-wordCount-text {
text-align: right;
margin-right: 10px;
color: #aaa;
}
}
</style>
創(chuàng)建上傳的圖片和視頻的組件寞奸,基于element ui和oss,可根據(jù)自己的需求修改
./upload/index.vue
<template>
<div id="fileUpload">
<el-upload
:disabled="loading && showLoading"
:action="action"
:data="dataObj"
:before-upload="beforeUpload"
:on-success="fnUploadSucess"
:on-error="handlerError"
:on-preview="handlePreview"
:show-file-list="false"
:on-remove="handleRemove"
:loading="loading"
:file-list="fileList"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">將文件拖到此處溶诞,或<em>點擊上傳</em></div>
</el-upload>
</div>
</template>
<script>
export default {
name: 'FileUpload',
data () {
return {
action: 'api/third/oss/upload',
dataObj: {},
fileList: [],
loading: false,
}
},
props: {
types: {
type: Array
},
showLoading: {
type: Boolean,
default: true
},
limitSize: {
type: Boolean,
default: false
}
},
created () {
},
methods: {
handleRemove (file, fileList) {
this.$emit('removeFile', [file, fileList])
},
handlePreview (file, fileList) {
this.$emit('preview', [file, fileList])
},
beforeUpload (file) {
let canUpload = false
// 若文件限制格式不為空但是在可上傳類型內(nèi) 或 未指定上傳文件 則讓他上傳
if (this.types && this.types.length !== 0) {
for (let i = 0; i < this.types.length; i++) {
if (file.type === this.types[i]) {
canUpload = true
}
}
} else {
canUpload = true
}
if (!canUpload) {
this.$message.error('上傳僅支持:' + this.types + '文件')
return false
}
if (this.limitSize) {
//限制大小
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.$message.error('上傳圖片大小不能超過 2MB!');
return false
}
}
this.loading = true
//獲取時長
this.getVideoDuration(file)
},
getVideoDuration (file) {
var url = URL.createObjectURL(file);
var audioElement = new Audio(url);
var duration;
audioElement.addEventListener("loadedmetadata", () => {
duration = audioElement.duration; //時長為秒,小數(shù)决侈,182.36
this.$emit('getDuration', duration)
});
},
fnUploadSucess (res, file) {
this.$emit('fnUploadSucess', file.response.data)
this.loading = false
this.fileList = []
},
handlerError () {
this.loading = false
this.$message.error('上傳失敗')
this.$emit('error')
}
}
}
</script>
<style scoped lang="scss">
#fileUpload {
.fileInput {
display: none;
}
}
</style>
修改Quill內(nèi)置的video blot螺垢,用video標(biāo)簽替換iframe
./upload/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}">${video}</a>`;
}
}
Video.blotName = "video"; // 這里不用改赖歌,樓主不用iframe枉圃,直接替換掉原來,如果需要也可以保留原來的庐冯,這里用個新的blot
Video.className = "ql-video";
Video.tagName = "video"; // 用video標(biāo)簽替換iframe
export default Video;
如果報錯說沒有下面這些依賴孽亲,請依次安裝
npm install quill-image-extend-module --save-dev
npm install quill-image-drop-module --save-dev
npm install quill-image-resize-module --save-dev
如果在引進(jìn)quill-image-resize-module 報錯TypeError: Cannot read property 'imports' of undefined,
在vue.config.js中:(沒有就在在根目錄下新建vue.config.js 文件 (不是在src 下))(配置后需要重新運行一下)
var webpack = require('webpack');
module.exports = {
// 在vue.config.js中configureWebpack中配置
// 要引入webpack
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
'window.Quill': 'quill/dist/quill.js',
'Quill': 'quill/dist/quill.js'
}),
]
}
}
最后在頁面中使用:
<template>
<div>
<quill-editor @input="changeContent" ref="quill"></quill-editor>
</div>
</template>
import QuillEditor from '@/components/QuillEditor'
//多余代碼就不寫了
components: {
QuillEditor
},
data(){
return {
content :""
}
}
methods:{
// 獲取html
changeContent (val) {
this.content = val
console.log(val);
},
}
清空編輯區(qū)內(nèi)容使用:
this.$refs.quill.$refs.myTextEditor.quill.setText('\n')