项目中有个使用vue-quill-editor富文本编辑器的功能,当上传大图片时,图片资源会被转成base64格式,当图片过大时字符串的大小可能达到2M以上,数据库无法保存。
vue-quill-editor提供了一个 handlers
可以自定义图片上传的方式,意思是点击这个图片按钮的时候,会出发一个回调,可以在回调里触发自己的文件上传开关。这里我用的文件上传是antd-vue的a-upload属性。
quill-quill-editor:
<quill-editor v-model="message" :options="editorOption" @change="change"> <div id="toolbar" slot="toolbar"></div> </quill-editor>
参数:
editorOption: { modules: { toolbar: { container: [ ["bold", "italic", "underline", "strike"], //加粗,斜体,下划线,删除线 ["blockquote", "code-block"], //引用,代码块 [{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、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"], //清除字体样式 ["image"], //上传图片 ], handlers: { image: (value) => { if (value) {
// 触发antd-vue的modal对话框 this.visible = true; } else { this.quill.format("image", false); } }, }, }, syntax: { highlight: (text) => hljs.highlightAuto(text).value, }, }, },
说明:
- 这里的思路是当点击图片按钮时,显示antd-vue的对话框,上传组件就放在对话框中
- 通过上传组件选择图片,并通过后端给的上传接口获取返回的地址
- 然后把返回的地址使用字符串处理把img的src动态拼接到内容中
对话框:
<a-modal title="选择图片" :visible="visible" @ok="handleOk" @cancel="handleCancel" :closable="false" okText="确定" cancelText="取消" :file-list="fileList" :before-upload="beforeUpload" > <a-upload class="pdupload" name="file" :multiple="false" :action="$api.upload" @change="handleChange1" accept=".jpg, .jpeg, .png, .webp" :fileList="fl" > <a-button> <a-icon type="upload" />选择图片 </a-button> <span style="padding-left: 15px;color:#888;font-size:12px;">最多一次上传1张图片</span> </a-upload> </a-modal>
handleChange1(info) { console.log("图片", info); if (info.fileList.length > 1) { this.$message.warning("每次仅能上传一张图片!"); return; } this.fl = info.fileList; if (info.file.status !== "uploading") { console.log(info.file, info.fileList); } if (info.file.status === "done") { this.detailImage = info.file.response.response.file_url; this.$message.success(`${info.file.name}上传成功`); } else if (info.file.status === "error") { this.$message.error(`${info.file.name}上传失败`); } }, handleOk() { this.visible = false; // 把图片用img标签的形式添加到详情中 console.log("结束标签所处位置", this.message.lastIndexOf("</p>")); if (this.detailImage) { let n = this.message.lastIndexOf("</p>"); let s = this.message; let s1 = s.substring(0, n); let domS = `<img src='${this.detailImage}'/></p>`; s1 += domS; this.message = s1; } // 清空已经选择的图片 this.fl = []; this.detailImage = ""; }, handleCancel() { this.visible = false; }
组件源码
<template> <div class="pro_detail"> <!-- 产品详情组件 --> <common-tit title="产品详情"></common-tit> <quill-editor v-model="message" :options="editorOption" @change="change"> <div id="toolbar" slot="toolbar"></div> </quill-editor> <a-modal title="选择图片" :visible="visible" @ok="handleOk" @cancel="handleCancel" :closable="false" okText="确定" cancelText="取消" :file-list="fileList" :before-upload="beforeUpload" > <a-upload class="pdupload" name="file" :multiple="false" :action="$api.upload" @change="handleChange1" accept=".jpg, .jpeg, .png, .webp" :fileList="fl" > <a-button> <a-icon type="upload" />选择图片 </a-button> <span style="padding-left: 15px;color:#888;font-size:12px;">最多一次上传1张图片</span> </a-upload> </a-modal> </div> </template> <script> import CommonTit from "../CommonTit"; export default { components: { CommonTit }, props: ["proDetail"], data() { return { fl: [], visible: false, previewVisible: false, detailImage: "", message: "", editorOption: { modules: { toolbar: { container: [ ["bold", "italic", "underline", "strike"], //加粗,斜体,下划线,删除线 ["blockquote", "code-block"], //引用,代码块 [{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、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"], //清除字体样式 ["image"], //上传图片 ], handlers: { image: (value) => { if (value) { this.visible = true; } else { this.quill.format("image", false); } }, }, }, syntax: { highlight: (text) => hljs.highlightAuto(text).value, }, }, }, }; }, methods: { change() { console.log(this.message); this.$emit("update:proDetail", this.message); }, handleChange1(info) { console.log("图片", info); if (info.fileList.length > 1) { this.$message.warning("每次仅能上传一张图片!"); return; } this.fl = info.fileList; if (info.file.status !== "uploading") { console.log(info.file, info.fileList); } if (info.file.status === "done") { this.detailImage = info.file.response.response.file_url; this.$message.success(`${info.file.name}上传成功`); } else if (info.file.status === "error") { this.$message.error(`${info.file.name}上传失败`); } }, handleOk() { this.visible = false; // 把图片用img标签的形式添加到详情中 console.log("当前内容:", this.message); console.log("结束标签所处位置", this.message.lastIndexOf("</p>")); if (this.detailImage) { let n = this.message.lastIndexOf("</p>"); let s = this.message; let s1 = s.substring(0, n); let domS = `<img src='${this.detailImage}'/></p>`; s1 += domS; this.message = s1; } // 清空已经选择的图片 this.fl = []; this.detailImage = ""; }, handleCancel() { this.visible = false; }, }, mounted() { this.message = this.proDetail; }, }; </script> <style lang="scss" scoped> .pro_detail { height: 594px !important; border-bottom-width: 0; .quill-editor { text-align: left; height: 491px; >>> .ql-toolbar .ql-formats { margin-right: 0; } } >>> .ant-upload { display: flex; flex-direction: column; } } </style>