问题
编辑器在一个页面使用多个富文本框上传图片时报
TypeError: Cannot read property ‘index‘ of null
问题原因
在上传图片成功方法中,代码是这样子的:
quillImgSuccess(res, file) {console.log(res);console.log(file);// res为图片服务器返回的数据// 获取富文本组件实例let quill = this.$refs.quillEditor.quill;// 如果上传成功if (res.code == 200) {// 获取光标所在位置let length = quill.getSelection().index;// 插入图片 res.url为服务器返回的图片地址console.log(res)quill.insertEmbed(length, "image", risun.util.nginxServerUrl()+res.fileName);// 调整光标到最后quill.setSelection(length + 1);} else {this.$message.error("图片插入失败");}},// 富文本图片上传失败uploadError() {// loading动画消失this.$message.error("图片插入失败");}
其中的这一句:
// 获取光标所在位置let length = quill.getSelection().index;
会报这个错误。究其原因,其实是
<quill-editorclass="editor"v-model="content"ref="quillEditor":options="editorOption"@blur="onEditorBlur($event)"@focus="onEditorFocus($event)"@change="onEditorChange($event)"></quill-editor>
中的
ref="quillEditor"
请注意,这里的 ref 被写死了。在同一个页面使用多个 vue-quill-editor 时,这个上传完成后调用的方法 quillImgSuccess 用 this 获得的对象只能获得第一个富文本框实例!这就是为什么出错的原因!
解决方法
完整代码如下
<template><div><!-- 图片上传组件辅助 --><el-upload:key="randomString(3)"class="avatar-uploader quill-img":action="uploadImgUrl"name="file":headers="headers":show-file-list="false":on-success="quillImgSuccess":on-error="uploadError":before-upload="quillImgBefore"accept='.jpg,.jpeg,.png,.gif,.jfif'></el-upload><!-- 富文本组件 --><quill-editorclass="ql-editor"v-model="content":ref="toref":options="editorOption"@blur="onEditorBlur($event)"@focus="onEditorFocus($event)"@change="onEditorChange($event)"></quill-editor></div></template><script>import { getToken } from '@/utils/auth'// import { getToken } from "@/api/system/upload.js";// 工具栏配置const toolbarOptions = [["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线["blockquote", "code-block"], // 引用 代码块[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表[{ indent: "-1" }, { indent: "+1" }], // 缩进[{ size: ["small", false, "large", "huge"] }], // 字体大小[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色[{ align: [] }], // 对齐方式["clean"], // 清除文本格式["link", "image", /*"video"*/] // 链接、图片、视频];import { quillEditor } from "vue-quill-editor";import "quill/dist/quill.core.css";import "quill/dist/quill.snow.css";import "quill/dist/quill.bubble.css";export default {props: {/* 编辑器的内容 */value: {type: String},/* 图片大小 */maxSize: {type: Number,default: 4000 //kb},toref: {type: String,default:"quillEditor"},quillIndex:{type:Number,default:0},},components: { quillEditor },data() {return {content: this.value,host: process.env.VUE_APP_BASE_API,// reqData: {// },// dir:"fuwenben",editorOption: {placeholder: "",theme: "snow", // or 'bubble'placeholder: "请输入内容",modules: {toolbar: {container: toolbarOptions,handlers: {image: (value) => {if (value) {// 触发input框选择图片文件const index = this.quillIndex;document.querySelectorAll(".quill-img input")[index].click();} else {this.quill.format("image", false);}}}}}},uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址headers: {Authorization: 'Bearer ' + getToken()}};},watch: {value: function() {this.content = this.value;}},created() {console.log(this.toref,"## toref")},methods: {onEditorBlur() {//失去焦点事件},onEditorFocus() {//获得焦点事件},onEditorChange() {//内容改变事件this.$emit("input", this.content);},randomString(len){//默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';var tempLen = chars.length, tempStr='';for(var i=0; i<len; ++i){tempStr += chars.charAt(Math.floor(Math.random() * tempLen ));}return tempStr;},// 富文本图片上传前async quillImgBefore(file) {let fileType = file.type;if(fileType === 'image/jpeg' || fileType === 'image/png' || fileType === 'image/jpg'){return true;}else {this.$message.error('请插入图片类型文件(jpg/jpeg/png)');return false;}},quillImgSuccess(res, file) {// res为图片服务器返回的数据// 获取富文本组件实例// let quill = this.$refs.quillEditor.quill;// 获取富文本组件实例console.log("this:",this.$refs);console.log("toref:",this.toref);let toeval = "this.$refs."+this.toref+".quill"console.log("toeval",toeval);let quill = eval(toeval);console.log("quill",quill);// 如果上传成功// 获取光标所在位置// let length = quill.getSelection().index;let length = quill.selection.savedRange.index;// 插入图片quill.insertEmbed(length, "image",this.host + res.key);// 调整光标到最后quill.setSelection(length + 1);},// 富文本图片上传失败uploadError() {// loading动画消失this.$message.error("图片插入失败");}}};</script><style>.ql-editor {line-height: normal !important;min-height: 300px;}.quill-img {display: none;}.ql-snow .ql-tooltip[data-mode="link"]::before {content: "请输入链接地址:";}.ql-snow .ql-tooltip.ql-editing a.ql-action::after {border-right: 0px;content: "保存";padding-right: 0px;}.ql-snow .ql-tooltip[data-mode="video"]::before {content: "请输入视频地址:";}.ql-snow .ql-picker.ql-size .ql-picker-label::before,.ql-snow .ql-picker.ql-size .ql-picker-item::before {content: "14px";}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {content: "10px";}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {content: "18px";}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {content: "32px";}.ql-snow .ql-picker.ql-header .ql-picker-label::before,.ql-snow .ql-picker.ql-header .ql-picker-item::before {content: "文本";}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {content: "标题1";}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {content: "标题2";}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {content: "标题3";}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {content: "标题4";}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {content: "标题5";}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {content: "标题6";}.ql-snow .ql-picker.ql-font .ql-picker-label::before,.ql-snow .ql-picker.ql-font .ql-picker-item::before {content: "标准字体";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {content: "衬线字体";}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {content: "等宽字体";}</style>
如何引用
<el-form-item label="课程介绍" prop="introduce"><editor toref="torefIntroduce" :quillIndex="0" v-model="form.introduce" :min-height="192"/></el-form-item><el-form-item label="试看内容" ><editor toref="torefTrySeeContent" :quillIndex="1" v-model="form.trySeeContent" :min-height="192"/></el-form-item><el-form-item label="内容" ><editor toref="torefContent" :quillIndex="2" v-model="form.content" :min-height="192"/></el-form-item>
这是在页面引用组件时的代码,js 代码忽略了。
注意一下 quillIndex 的顺序别搞错了,根据索引找到富文本组件
最关键的是这几个地方:
一、在组件里
1. 富文本组件里的 ref 属性:
:ref="toref"
这里的 toref 是页面传过来的值。
- props 中增加:
toref: {type: String,default:"quillEditor"},

