quill 富文本编辑器自定义图片上传(修改默认按钮行为)
前言
quill默认图片上传是将图片转为base64字符串,这个主意很不错,但是如果考虑后端存储的大小,就不太美丽了,因为base64的字符串大小往往比实际图片大小占用还要大一些,所以一般的做法是自定义文件上传,后端返回外链地址,用链地址来代替base64,以减少富文本的内容大小。
我目前的做法是将官方默认图片上传的行为改变,这样按钮样式啥的就不用自己操心了。
教程
修改默认按钮行为
我们在初始化quill实例的时候,会传入一个options来控制样式和顶部菜单功能。
import type { QuillOptionsStatic } from 'quill';
const editorOptions: QuillOptionsStatic = {
theme: 'snow',
modules: {
toolbar: [
[{
header: [1, 2, 3, 4, 5, 6, false]
}],
['bold', 'italic', 'underline', 'strike'],
[{
list: 'ordered'
}, {
list: 'bullet'
}],
['blockquote', 'code-block'],
[{
color: []
}, {
background: []
}],
[{
align: []
}],
['link', 'image', 'video'],
['clean'],
]
},
bounds: '.quill-editor-wrapper',
};
这是我的配置,其中theme必填项,bounds用于指定定位的容器,比如quill的内置弹窗,如果不指定你会发现定位非常奇怪。
modules中toolbar用于控制顶部操作栏,官方文档只告知了它可以是一个数组,而ts的类型定义中并没有对应的类型提示。
真实情况是它是可以支持键值对象的,通过这种形式我们才可以自定义按钮和修改官方默认行为。
const editorOptions: QuillOptionsStatic = {
theme: 'snow',
modules: {
toolbar: {
container: [
[{
header: [1, 2, 3, 4, 5, 6, false]
}],
['bold', 'italic', 'underline', 'strike'],
[{
list: 'ordered'
}, {
list: 'bullet'
}],
['blockquote', 'code-block'],
[{
color: []
}, {
background: []
}],
[{
align: []
}],
['link', 'image', 'video'],
['clean'],
],
handlers: {
image: () => {
//自定义图片按钮行为
},
},
},
},
bounds: '.quill-editor-wrapper',
};
container
表示工具栏容器配置,而handlers
表示行为,如果我们需要自定义行为就在handlers中配置,比如我需要覆盖image
图片上传行为,就如上写法。
如果你是希望新增一个自定义控制按钮,可以如下:
const editorOptions: QuillOptionsStatic = {
theme: 'snow',
modules: {
toolbar: {
container: [
[{
header: [1, 2, 3, 4, 5, 6, false]
}],
['bold', 'italic', 'underline', 'strike'],
[{
list: 'ordered'
}, {
list: 'bullet'
}],
['blockquote', 'code-block'],
[{
color: []
}, {
background: []
}],
[{
align: []
}],
['link', 'image', 'video'],
['clean'],
['custom-button']
],
handlers: {
image: () => {
//自定义图片按钮行为
},
'custom-button': function() {
// 自定义按钮的点击事件处理程序
console.log('Custom button clicked!');
// 执行其他自定义操作
}
},
},
},
bounds: '.quill-editor-wrapper',
};
custom-button
就是自定义按钮,当然可能后续还有更加复杂的需求,我目前的应用场景没有这种需求,就当做抛砖引玉了,更加深入的需求就自己去看文档吧。
实现文件上传
原理也很简单,我们利用原生input的功能实现
<input class="quill-editor-input-file" ref="quillEditorInputFileRef" type="file" accept="image/*" @change="onFileChange" />
const quillEditorInputFileRef = ref<HTMLDivElement>();
/** 文件上传loading */
const uploadLoading = ref(false);
/** 文件change事件 */
function onFileChange(event: Event) {
const files = (event.target as HTMLInputElement).files!;
let file: File | null = null;
if (files.length <= 0) {
file = null;
return;
}
file = files[0]; // 只取第一个文件
if (!file) return;
uploadLoading.value = true;
uploadImage(file)
.then((src) => {
const range = editor.getSelection(true);
editor.insertEmbed(range.index, 'image', src, Quill.sources.USER);
})
.catch((error) => {
ElMessage.error(`上传图片失败`);
console.error('上传图片失败', error);
})
.finally(() => {
uploadLoading.value = false;
quillEditorInputFileRef.value!.value = ''; // 清空input file
});
}
/** 上传图片api */
function uploadImage(file: File): Promise < string > {
return new Promise((resolve, reject) => {
const fromData = new FormData();
fromData.append('file', file);
uploadFile(fromData)
.then((response: unknown) => {
const { data } = response as ResponseInterface<string> ;
return resolve(data);
})
.catch((err) => {
return reject(err);
});
});
}
监听change
事件,需要注意的是,如果用户并没有选择文件点击了取消,change事件是不会触发的,而如果是先选择了文件,在点击input重新选择,不选择任何文件,此时再点击取消,则会触发change文件,因为此时文件被清空了,理论上确实应该触发,所以通过files
获取的类数组,不一定是有值的,记得判空。
上传图片api是我自己项目中的写法,参考即可。
Quill.sources.USER
表示这是一个用户行为,用于编辑器还原使用,具体怎么回事,等我深入了解后再说吧!
为了防止用户重复点击上传,我添加了一个uploadLoading
,大家可以基于这个进行遮罩或者禁用按钮的方式防止用户重复操作。
注意样式上,将input的display设置为none。
.quill-editor-input-file {
display: none;
}
注意:
不清空value会导致相同文件不触发change事件!
整合
现在文件上传也没问题了,我们主需要在image的行为函数中,手动触发文件上传即可。
const editorOptions: QuillOptionsStatic = {
theme: 'snow',
modules: {
toolbar: {
container: [
[{
header: [1, 2, 3, 4, 5, 6, false]
}],
['bold', 'italic', 'underline', 'strike'],
[{
list: 'ordered'
}, {
list: 'bullet'
}],
['blockquote', 'code-block'],
[{
color: []
}, {
background: []
}],
[{
align: []
}],
['link', 'image', 'video'],
['clean'],
],
handlers: {
image: () => {
quillEditorInputFileRef.value?.click();
},
},
},
},
bounds: '.quill-editor-wrapper',
};
此时我们就完整实现了覆盖官方图片上传的行为。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
全部评论 12
11
Google Chrome Windows 10import { QuillEditor, Quill } from '@vueup/vue-quill'
const editor = new Quill()
引入 Quill ,创建 editor 实例,会报错:
TypeError: Cannot read properties of undefined (reading 'scrollTop')
您那边会有这样的问题不
木灵鱼儿
FireFox Windows 1011
Google Chrome Windows 10木灵鱼儿
FireFox Windows 1011
Google Chrome Windows 10木灵鱼儿
FireFox Windows 1011
Google Chrome Windows 10木灵鱼儿
FireFox Windows 1011
Google Chrome Windows 1011
Google Chrome Windows 1011
Google Chrome Windows 10你好, editor 和 Quill 这两个是什么呀
然后这个 uploadFile 是一个用来把 fromData 对象给后端,后端返回图片路径的接口函数把
木灵鱼儿
FireFox Windows 10