此处以 vue-quill-editor 进行示范,其他富文本大体都差不多
可以粘贴直接从文件夹里复制出来的图片,也可以粘贴截图;
<template>
<div>
<quill-editor v-model="content" ref="myQuillEditor" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @change="onEditorChange($event)" />
<!-- 图片上传组件 -->
<file-upload @change="imgChange" id="enclosureInput" ref="ossUpload" :folderName="'financial'" :isMultiple="true" style="display: none" />
</div>
</template>
import { quillEditor, Quill } from "vue-quill-editor";
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
Quill.register('modules/ImageExtend', ImageExtend)
export default {
components: {
quillEditor,
},
data () {
return {
content: '',
editorOption: {
placeholder: "请在这里输入",
modules: {
toolbar: {
container: container,
handlers: {
'image': function (value) {
if (value) {
// 绑定上传图片按钮 自带的图片上传,我没用过,应该问题不大
document.getElementById('enclosureInput').children[0].children[0].children[0].click()
} else {
this.quill.format('image', false)
}
}
}
}
}
},
oldIndex: 0, // 记录光标位置
imgArr: [], // 用来存放图片的 base64 字符串、图片上传后的 url 和 图片上传状态
subDisabled: false, // 判断是否可以提交
}
},
methods: {
//组件的change事件 非重点,不做介绍
imgChange (fileList) {
},
onEditorReady (editor) {
},
onEditorBlur () {
},
// 失去焦点事件
onEditorFocus () { console.log('获得焦点'); }, // 获得焦点事件
// 内容改变事件
onEditorChange () {
this.oldIndex = this.$refs.myQuillEditor.quill.selection.savedRange.index;
},
pasteListener () {
// 清空数组,防止脏数据
this.imgArr.length = 0;
// 监听粘贴事件
document.getElementsByClassName('quill-editor')[0].removeEventListener("paste", this.listenerFn);
document.getElementsByClassName('quill-editor')[0].addEventListener('paste', this.listenerFn);
},
// 取消监听事件
removeListener () { document.getElementsByClassName('quill-editor')[0].removeEventListener("paste", this.listenerFn) },
listenerFn (e) {
let quill = this.$refs.myQuillEditor.quill;
// 获取光标初始位置
this.oldIndex = quill.selection.savedRange.index;
if (!(e.clipboardData && e.clipboardData.items)) return;
e.clipboardData.items.forEach(async item => {
// 字符串
if (item.kind == 'string') item.getAsString(str => { console.log(str); })
else if (item.kind == 'file') {
// 获取文件
let file = await item.getAsFile();
// 对指定格式的图片进行操作
if (/\.(jpg|jpeg|png|GIF|JPG|PNG)$/.test(file.name)) {
this.translateBlobToBase64(file, async baseStr => {
// 判断,如果是直接粘贴的文件,光标不会移动,需要手动往里面塞
if (this.oldIndex == quill.selection.savedRange.index) {
quill.insertEmbed(this.oldIndex, 'image', baseStr);
// 光标 +1
quill.setSelection(this.oldIndex + 1);
}
// 将base64转换为文件后,不同的图片放到数组中,然后进行上传操作;
// 通过上面的塞值操作之后,没粘贴一张图片,富文本中都会有对应的 base64 字符串去,通过字符串切割成数组的方式,判断是否有粘贴相同的图片,相同的图片 base64 编码是一样的,这样只需要上传一次即可
if (this.content.indexOf(baseStr) == -1 || (((this.content.split(baseStr)).length - 1) == 1)) {
let imgObj = {
baseStr, // base64 字符串,用于匹配和替换
state: false, // 上传状态,用来判断图片是否上传成功
url: undefined, // 图片上传成功后的地址
}
this.subDisabled = true;
// 图片上传,此处前端直接传 阿里oss,不同需求单独处理
let rsp = await uploadOSS({ file }, this.$refs.ossUpload.Aliyun, 'img');
if (rsp) {
// 上传成功之后,更改状态,并放到 imgArr 数组中,再遍历该数组,判断是否所有图片上传状态;
let obj = this.$refs.ossUpload.transformObj(rsp);
imgObj.state = true;
imgObj.url = obj.url;
this.imgArr.push(imgObj);
this.subDisabled = false;
this.imgArr.forEach(item => {
if (!item.state) this.subDisabled = true;
})
}
}
// 重新获取光标位置
this.oldIndex = quill.selection.savedRange.index;
})
}
}
})
},
// 将base64 全部替换成 url
// 此方法在提交数据之前执行
imgChaec () {
if (this.imgArr.length > 0) this.imgArr.forEach(item => this.content = this.content.replaceAll(item.baseStr, item.url));
},
}
}
总结:
- 使用了发布订阅者的设计思想,每个图片单独处理,互不影响;
- 可以粘贴文件夹里复制的图片;
- 传给后端的图片时 url 连接,减小数据库字段长度;
- 粘贴图片的时候,如果是截取的图片,组件自己会将截取的图片的 base64 编码放到组件数据中,因为数据不好判断,也不好获取数据中的 base64 编码,所以也手动根据 file 对象重新生成了 base64 编码,后续可以优化;
- 图片上传失败没有做处理,后续有时间优化;
网友评论