本文介绍在vue3环境下使用vue-quill富文本编辑器,并实现使用自定义上传接口将图片上传至服务器并保存为img标签src为服务器中图片的地址而不是默认的base64格式图片。从而解决富文本编辑器使用base64导致字段超长问题。
1.安装依赖库
| npm install @vueup/vue-quill |
| npm install quill-image-uploader |
复制
vue-quill为基础富文本组件
quill-image-uploader为图片上传扩展模块
2.引入组件
| |
| import { QuillEditor, Quill } from '@vueup/vue-quill'; |
| import ImageUploader from "quill-image-uploader"; |
| import '@vueup/vue-quill/dist/vue-quill.snow.css'; |
| Quill.register("modules/imageUploader", ImageUploader); |
| export default { |
| components: { |
| QuillEditor |
| }, |
| } |
复制
3.使用组件
| <quill-editor |
| class="ql-editor" |
| :options="editorOption" |
| v-model:content="form.content" |
| content-type="html"> |
| </quill-editor> |
复制
其中content-type设置为html后v-model绑定的值则自动获取到为带html标签的文本
4.配置图片上传以及富文本组件
| editorOption: { |
| modules: { |
| imageUploader: { |
| upload: async (file) => { |
| let res = await uploadFileAPI({file}) |
| return res.data.filePath |
| } |
| }, |
| toolbar: [ |
| ['bold', 'italic', 'underline', 'strike'], |
| ['blockquote', 'code-block'], |
| [{ header: 1 }, { header: 2 }], |
| [{ list: 'ordered' }, { list: 'bullet' }], |
| [{ script: 'sub' }, { script: 'super' }], |
| [{ indent: '-1' }, { indent: '+1' }], |
| [{ direction: 'rtl' }], |
| [{ 'size': ['small', false, 'large', 'huge'] }], |
| [{ header: [1, 2, 3, 4, 5, 6, false] }], |
| [{ color: [] }, { background: [] }], |
| [{ font: [] }], |
| [{ align: [] }], |
| ['clean'], |
| ['link', 'image', 'video'], |
| ], |
| }, |
复制
此处需要使用自己的上传接口,下面为前端上传接口示例:
| export const uploadFileAPI = (data) => { |
| return request({ |
| url: "/file/upload", |
| method: "post", |
| data, |
| headers: { |
| "Content-Type": "multipart/form-data" |
| } |
| }) |
| } |
复制
后端接口示例(此处使用的为本地nginx文件服务器,可替换为云服务器地址):
| final String UPLOAD_DIRECTORY = "D:/Project/FileServer/"; |
| final String BASE_URL = "http://localhost:9090/"; |
| @PostMapping("/upload") |
| public R uploadFile(@RequestParam("file") MultipartFile file) { |
| if (file.isEmpty()) { |
| return R.failed("请选择要上传的文件"); |
| } |
| |
| try { |
| String originalFilename = file.getOriginalFilename(); |
| |
| String newFilename = null; |
| if (originalFilename != null) { |
| newFilename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf(".")); |
| } |
| |
| |
| String filePath = UPLOAD_DIRECTORY + newFilename; |
| |
| file.transferTo(new File(filePath)); |
| |
| |
| Map<String, String> res = new HashMap<>(); |
| res.put("fileName", newFilename); |
| res.put("filePath", BASE_URL + newFilename); |
| return R.ok(res, "上传成功"); |
| } catch (Exception e) { |
| return R.failed("文件上传失败: " + e.getMessage()); |
| } |
| } |
复制
5.汉化富文本工具栏
| /deep/ .ql-snow .ql-tooltip[data-mode=link]::before { |
| content: "请输入链接地址:"; |
| } |
| /deep/ .ql-snow .ql-tooltip.ql-editing a.ql-action::after { |
| border-right: 0px; |
| content: '保存'; |
| padding-right: 0px; |
| } |
| |
| /deep/ .ql-snow .ql-tooltip[data-mode=video]::before { |
| content: "请输入视频地址:"; |
| } |
| |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label::before, |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item::before { |
| content: '14px'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before, |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before { |
| content: '10px'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before, |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before { |
| content: '18px'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before, |
| /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before { |
| content: '32px'; |
| } |
| |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label::before, |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item::before { |
| content: '文本'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { |
| content: '标题1'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { |
| content: '标题2'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { |
| content: '标题3'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { |
| content: "标题4"; |
| } |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { |
| content: '标题5'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, |
| /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { |
| content: '标题6'; |
| } |
| |
| /deep/ .ql-snow .ql-picker.ql-font .ql-picker-label::before, |
| /deep/ .ql-snow .ql-picker.ql-font .ql-picker-item::before { |
| content: '标准字体'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before, |
| /deep/ .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before { |
| content: '衬线字体'; |
| } |
| /deep/ .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before, |
| /deep/ .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before { |
| content: '等宽字体'; |
| } |
| /deep/ .ql-align-center{ |
| text-align: center; |
| } |
| /deep/ .ql-align-right{ |
| text-align: right; |
| } |
| /deep/ .ql-align-left{ |
| text-align: left; |
| } |
复制