问题:
vue-quill-editor富文本编辑器上传图片默认为base64,存入数据库过于庞大,使用 paste 方法 实现图片地址上传。
实现效果图:
NPM
npm install vue-quill-editor --save
完整代码
<template>
<div :class="prefixCls">
<quill-editor v-model="content" ref="myQuillEditor" :content="value" :options="editorOption"
@blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)"
@change="onEditorChange($event)">
</quill-editor>
</div>
</template>
<script>
import axios from 'axios'
import { quillEditor, Quill } from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
export default {
name: 'JNPFQuill',
components: { quillEditor },
props: {
prefixCls: {
type: String,
default: 'jnpf-editor-quill'
},
// 表单校验用字段
// eslint-disable-next-line
value: {
type: String
},
placeholder: {
type: String,
default: '请输入'
}
},
data() {
return {
content: this.value,
editorOption: {
modules: {
toolbar: [
["bold", "italic", "underline", "strike"],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ size: ["small", false, "large", "huge"] }],
[{ color: [] }, { background: [] }],
["blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }],
[{ indent: "-1" }, { indent: "+1" }],
[{ align: [] }],
[{ direction: "rtl" }],
["clean"],
["link", "image"],
],
},
theme: 'snow',
placeholder: this.placeholder
},
uploadNum:0,// 记录上传次数
uploadPicList:[] // 图片URL list
}
},
mounted() {
let quill = this.$refs.myQuillEditor.quill;
// 粘贴事件
quill.root.addEventListener("paste", (evt) => {
const rtf = evt.clipboardData.getData('text/rtf');
// 提取图片信息
const hexStrings = this.extractImageDataFromRtf(rtf);
// 获取base64图片数据
const base64Images = hexStrings.map((hexObj) => {
return this.convertHexToBase64(hexObj.hex);
})
// 调用上传接口
for (let i = 0; i < base64Images.length; i++) {
this.picUpload(`data:image/png;base64,${base64Images[i]}`,base64Images.length)
}
})
},
methods: {
base64toFile(dataurl, filename = 'file') {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
let file = new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
return file
},
/**
* data: 图片Base64 编码
* len : 上传图片数量
*/
async picUpload(data, len) {
++this.uploadNum
let file = this.base64toFile(data);
var formData = new FormData();
formData.append('file', file);
await axios({
ContentType:'multipart/form-data',
url: '/api/file/Uploader',
method: 'post',
data: formData
}).then(res => {
this.uploadPicList.push(res.data.data.url)
if (this.uploadNum === len) {
setTimeout(() => {
this.setResponseUrl(res.data.data.url)
},500)
}
})
},
setResponseUrl() {
const editorDom = this.$refs.myQuillEditor.quill.root;
const editorImgs = editorDom.querySelectorAll('img[src*="//:0"]');
// 替换图片Url
editorImgs.forEach((item, index) => {
item.src = this.define.comUrl + this.uploadPicList[index];
})
},
/**
*
* rtf中提取图片信息
* 利用正则从rtf内容中提取到图片的核心信息,得到数组。其中数组中保存的信息有
{
type: ‘’, //图片类型
hex: ‘’ // hex字符串
}
* @param {*} rtfData
*/
extractImageDataFromRtf(rtfData) {
if (!rtfData) {
return [];
}
const regexPictureHeader = /{\\pict[\s\S]+?({\\\*\\blipuid\s?[\da-fA-F]+)[\s}]*/
const regexPicture = new RegExp('(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g');
const images = rtfData.match(regexPicture);
const result = [];
if (images) {
for (const image of images) {
let imageType = false;
if (image.includes('\\pngblip')) {
imageType = 'image/png';
} else if (image.includes('\\jpegblip')) {
imageType = 'image/jpeg';
}
if (imageType) {
result.push({
hex: image.replace(regexPictureHeader, '').replace(/[^\da-fA-F]/g, ''),
type: imageType
});
}
}
}
return result;
},
// 讲hex格式转化为base64
convertHexToBase64(hexString) {
return btoa(hexString.match(/\w{2}/g).map(char => {
return String.fromCharCode(parseInt(char, 16));
}).join(''));
},
onEditorBlur(quill) {
// console.log('editor blur!', quill)
},
onEditorFocus(quill) {
// console.log('editor focus!', quill)
},
onEditorReady(quill) {
// console.log('editor ready!', quill)
},
onEditorChange({ quill, html, text }) {
this.$emit("input", this.content);
},
insertText(content) {
let quill = this.$refs.myQuillEditor.quill
let index = quill.selection.savedRange.index
quill.insertText(index, content)
index += content.length
quill.setSelection(index)
}
},
computed: {
editor() {
return this.$refs.myQuillEditor.quill
}
},
watch: {
value(val) {
this.content = val
},
placeholder(val) {
this.$set(this.editorOption, 'placeholder', val)
}
}
}
</script>