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',
};
此时我们就完整实现了覆盖官方图片上传的行为。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
评论