Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

改造vue-quill-editor:实现图片上传到服务器再插入富文本 #2

Open
NextBoy opened this issue Dec 28, 2017 · 49 comments
Open

Comments

@NextBoy
Copy link
Owner

NextBoy commented Dec 28, 2017

需求概述

vue-quill-editor是我们再使用vue框架的时候常用的一个富文本编辑器,在进行富文本编辑的时候,我们往往要插入一些图片,vue-quill-editor默认的处理方式是直接将图片转成base64编码,这样的结果是整个富文本的html片段十分冗余,通常来讲,每个服务器端接收的post的数据大小都是有限制的,这样的话有可能导致提交失败,或者是用户体验很差,数据要传递很久才全部传送到服务器。
因此,在富文本编辑的过程中,对于图片的处理,我们更合理的做法是将图片上传到服务器,再将图片链接插入到富文本中,以达到最优的体验。
废话不多说,接下来直接看如何改造

改造分析

查阅网上的资料,我感觉提供的方案都不是特别友好,网上搜索的基本都是这一个方法
配合 element-ui 实现上传图片/视频到七牛或者是直接重新写一个按钮来进行自定义图片操作

坦白讲,上面这2个方法都很特别,也的确有效果,但是我个人还是觉得不完美,第一个方法写得太麻烦,第二个方法有点投机取巧。
结合上面两种方法以及官方的文档,我这里提供一个新的改造思路给大家参考。

引入element-ui

和第一种方法类似,为了更好的控制上传的图片,我这里也是引用了element-ui的上传图片组件

<template>
    <div>
        <!-- 图片上传组件辅助-->
        <el-upload
                class="avatar-uploader"
                :action="serverUrl"
                name="img"
                :headers="header"
                :show-file-list="false"
                :on-success="uploadSuccess"
                :on-error="uploadError"
                :before-upload="beforeUpload">
        </el-upload>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                serverUrl: '',  // 这里写你要上传的图片服务器地址
                header: {token: sessionStorage.token}  // 有的图片服务器要求请求头需要有token  
            }
        },
        methods: {
            // 上传图片前
            beforeUpload(res, file) {},
            // 上传图片成功
            uploadSuccess(res, file) {},
            // 上传图片失败
            uploadError(res, file) {}
        }
    }
</script>

这里要使用element-ui主要有2个好处

  • 可以对图片上传前,图片上传成功,图片上传失败等情况进行操作,也就是代码中的
  :on-success="uploadSuccess"  //  图片上传成功
  :on-error="uploadError"  // 图片上传失败
  :before-upload="beforeUpload"  // 图片上传前

引入vue-quill-editor

这里对于如何安装和引入vue-quill-editor和就不多做陈述了,不清楚的同学自己Google下哈。
在代码中写入vue-quill-editor后如下

<template>
    <div>
        <!-- 图片上传组件辅助-->
        <el-upload
                class="avatar-uploader"
                :action="serverUrl"
                name="img"
                :headers="header"
                :show-file-list="false"
                :on-success="uploadSuccess"
                :on-error="uploadError"
                :before-upload="beforeUpload">
        </el-upload>
        <!--富文本编辑器组件-->
       <el-row v-loading="uillUpdateImg">
        <quill-editor
                v-model="detailContent"
                ref="myQuillEditor"
                :options="editorOption"
                @change="onEditorChange($event)"
                @ready="onEditorReady($event)"
        >
        </quill-editor>
       </el-row>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
                serverUrl: '',  // 这里写你要上传的图片服务器地址
                header: {token: sessionStorage.token},  // 有的图片服务器要求请求头需要有token之类的参数,写在这里
                detailContent: '', // 富文本内容
                editorOption: {}  // 富文本编辑器配置
            }
        },
        methods: {
            // 上传图片前
            beforeUpload(res, file) {},
            // 上传图片成功
            uploadSuccess(res, file) {},
            // 上传图片失败
            uploadError(res, file) {}
        }
    }
</script>

这里可以看到我们用一个包裹我们的富文本组件,是为了使用loading动画,就是v-loading这个设置

重写点击图片按钮事件

从下图可以看到,默认的配置中,整个工具栏具备了所有的功能,自然也包括红圈中的图片上传功能了。
那么接下来我们要怎么去重写这个按钮的事件呢。
clipboard.png

很简单,我们需要在editorOption配置中这么写

export default {
data() {
            return {
                serverUrl: '',  // 这里写你要上传的图片服务器地址
                header: {token: sessionStorage.token},  // 有的图片服务器要求请求头需要有token之类的参数,写在这里
                detailContent: '', // 富文本内容
                editorOption: {
                    placeholder: '',
                    theme: 'snow',  // or 'bubble'
                    modules: {
                        toolbar: {
                            container: toolbarOptions,  // 工具栏
                            handlers: {
                                'image': function (value) {
                                    if (value) {
                                        document.querySelector('#quill-upload input').click()
                                    } else {
                                        this.quill.format('image', false);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
}

配置中的handlers是用来定义自定义程序的,然而我们配置完后会懵逼地发现,整个富文本编辑器的工具栏的图片上传等按钮都不见了 只保留了几个基本的富文本功能。

这个是因为添加自定义处理程序将覆盖默认的工具栏和主题行为
因此我们要再自行配置下我们需要的工具栏,所有功能的配置如下,大家可以按需配置

<script>
// 工具栏配置
const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
  ['blockquote', 'code-block'],

  [{'header': 1}, {'header': 2}],               // custom button values
  [{'list': 'ordered'}, {'list': 'bullet'}],
  [{'script': 'sub'}, {'script': 'super'}],      // superscript/subscript
  [{'indent': '-1'}, {'indent': '+1'}],          // outdent/indent
  [{'direction': 'rtl'}],                         // text direction

  [{'size': ['small', false, 'large', 'huge']}],  // custom dropdown
  [{'header': [1, 2, 3, 4, 5, 6, false]}],

  [{'color': []}, {'background': []}],          // dropdown with defaults from theme
  [{'font': []}],
  [{'align': []}],
  ['link', 'image', 'video'],
  ['clean']                                         // remove formatting button
]

export default {
data() {
 return {
editorOption: {
          placeholder: '',
          theme: 'snow',  // or 'bubble'
          modules: {
            toolbar: {
              container: toolbarOptions,  // 工具栏
              handlers: {
                'image': function (value) {
                  if (value) {
                    alert(1)
                  } else {
                    this.quill.format('image', false);
                  }
                }
              }
            }
          }
        }
    }
 }
}

</script>

由于这里的工具栏配置列举了所有,看起来很长一堆,我建议大家可以写在单独一个文件,然后再引入,美观一点

自定义按钮事件打开上传图片

经过上面的配置,大家点击一下图片,可以看出弹出了个1,说明我们的自定义事件生效了,那么接下来,大家的思路是不是就很清晰啦?
我们需要在handles里面继续完善我们的图片点击事件。

  • 第一步,点击按钮选择本地图片
handlers: {
           'image': function (value) {
             if (value) {
             // 触发input框选择图片文件
                document.querySelector('.avatar-uploader input').click()
               } else {
                this.quill.format('image', false);
              }
           }
        }

在这里我们的自定义事件就结束了,接下来图片上传成功或者失败都由

 :on-success="uploadSuccess"  //  图片上传成功
  :on-error="uploadError"  // 图片上传失败
  :before-upload="beforeUpload"  // 图片上传前

这三个函数来处理

 // 富文本图片上传前
            beforeUpload() {
                // 显示loading动画
                this.quillUpdateImg = true
            },
            
            uploadSuccess(res, file) {
                // res为图片服务器返回的数据
                // 获取富文本组件实例
                let quill = this.$refs.myQuillEditor.quill
                // 如果上传成功
                if (res.code === '200' && res.info !== null) {
                    // 获取光标所在位置
                    let length = quill.getSelection().index;
                    // 插入图片  res.info为服务器返回的图片地址
                    quill.insertEmbed(length, 'image', res.info)
                    // 调整光标到最后
                    quill.setSelection(length + 1)
                } else {
                    this.$message.error('图片插入失败')
                }
                // loading动画消失
                this.quillUpdateImg = false
            },
       
            // 富文本图片上传失败
            uploadError() {
                // loading动画消失
                this.quillUpdateImg = false
                this.$message.error('图片插入失败')
            }

好了,本文就讲到这,目前运行良好,整个文章的代码比较多,但是实际上需要去深入理解的地方很少,我们只是简单重定义了图片按钮的触发事件。
对了,大家别忘记安装element-ui和vue-quill-editor哦。
如果有错误,欢迎大家多提提意见,希望这篇文章能帮到有需要的人。

@Bloodmsg
Copy link

你好!我在nuxt中使用vue-quill时出现了编辑器会自动删除img标签script标签等,但是我在option里没有做任何设置。。。我现在参照你的教程写了一次,什么功能都正常,就是还会自动删除img等标签。。迷茫。。求指点!

@GrowingMonkey
Copy link

实测 确实可以 谢谢up主

@cqs199005
Copy link

感谢up主

@WangXi01
Copy link

感谢up主,有用,已经成功!

@470772345
Copy link

请问这个 vue-quill-editor 可以实现 table ,编辑表格的功能吗?

@MeridaFeng
Copy link

感谢ღ( ´・ᴗ・` )

@Jamayette
Copy link

该方法有一个问题就是一个页面如果引用了多个富文本编辑器,上传的图片默认会到第一个编辑器里, 不知道是不是我使用有误

@NextBoy
Copy link
Owner Author

NextBoy commented Dec 27, 2018

@Jamayette
Copy link

@Jamayette https://github.com/NextBoy/quill-image-extend-module 用这个

感谢,我加了一个index 现在能用了 后面有时间再切成这个

@cheesekun
Copy link

很棒,感恩

@hujiajia1018
Copy link

亲测好使,谢谢大佬

@zhaoweih
Copy link

thanks

@skmucheng
Copy link

您好,按照您的方法写下来,我可以在后台接收图片,并写入文件夹中,但在前台接受不到返回的路径,还有,返回的路径应该是什么路径?怎末在前台显示图片?,我的用是前后台分离的vue+springboot

@tAnzEnGsHi98
Copy link

非常完美,感谢!

@zhaoweih
Copy link

如果需要在img标签里面添加alt属性怎么添加?网上找了好久没找着

@MircoSans
Copy link

请问一下 console.log(file) 提示undefined
每次都是插入图片失败怎么解决

@228443632
Copy link

大佬 厉害

@AlwaysSkylll
Copy link

前来还愿

@StephanieTM
Copy link

@zhaoweih

如果需要在img标签里面添加alt属性怎么添加?网上找了好久没找着

根据文档中关于updateContents的说明,
updateContents(delta: Delta, source: String = 'api'): Delta
可以通过直接提供一个Delta的方式来修改编辑器内容,Delta提供了retain(保留), delete(删除)和insert(插入)等操作,且可以通过attributes来对alt等属性进行详设。此处我们需要用到retain和insert。

image
注:如上图,官方文档不建议手动创建Delta,而是通过insert() delete() retain()等方法进行链式操作,但由于时间仓促本人尝试时发现有报错,就直接根据文档手动创建了一个Delta,具体改写方法如下。

imgUploadSuccess(res, file) {
    const quill = this.$refs.contentEditor.quill;
    const length = quill.getSelection().index;
    const delta = length ? {
        ops: [
            { retain: length },
            { insert: { image: res }, attributes: { alt: file.name } }
        ]
    } : {
        ops: [
            { insert: { image: res }, attributes: { alt: file.name } }        
    ]
    };
    quill.updateContents(delta);
    quill.setSelection(length + 1);
}

注意代码中根据当前光标位置length是否为0对delta赋值,当光标位置为0时不retain,直接insert,是因为retain的值为0的话会报错。

@15993799331
Copy link

vue-quill-editor可以实现实时浏览吗

@ssshjx
Copy link

ssshjx commented Oct 11, 2019

万分感谢解决了问题! 想做一下自己的记录转载一下地址 有需要请联系删除 谢谢

@zxc19890923
Copy link

您好 我在上传图片的时候报错
error: "file is not specified in multipart"
你遇到过吗?

@ssshjx
Copy link

ssshjx commented Nov 19, 2019 via email

@zxc19890923
Copy link

@ssshjx 没有 就一张

@ssshjx
Copy link

ssshjx commented Nov 19, 2019 via email

@zxc19890923
Copy link

@sssh
好了,因为七牛上传对file有要求 name="file"
image

@ssshjx
Copy link

ssshjx commented Nov 19, 2019 via email

@limuen
Copy link

limuen commented Nov 21, 2019

完美解决 谢谢了

@dyan1992
Copy link

dyan1992 commented Feb 7, 2020

this.quill是什么,项目里报错,没有定义

@ssshjx
Copy link

ssshjx commented Feb 7, 2020 via email

@dyan1992
Copy link

dyan1992 commented Feb 7, 2020

quill 是当前这个vue-quill-editor ,报错是不是指向有问题?

------------------ 原始邮件 ------------------ 发件人: "dyan1992"<notifications@github.com>; 发送时间: 2020年2月7日(星期五) 下午2:59 收件人: "NextBoy/skill"<skill@noreply.github.com>; 抄送: "tany"<1209177833@qq.com>;"Mention"<mention@noreply.github.com>; 主题: Re: [NextBoy/skill] 改造vue-quill-editor:实现图片上传到服务器再插入富文本 (#2) this.quill是什么,项目里报错,没有定义 — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

quill 是当前这个vue-quill-editor ,报错是不是指向有问题?

------------------ 原始邮件 ------------------ 发件人: "dyan1992"<notifications@github.com>; 发送时间: 2020年2月7日(星期五) 下午2:59 收件人: "NextBoy/skill"<skill@noreply.github.com>; 抄送: "tany"<1209177833@qq.com>;"Mention"<mention@noreply.github.com>; 主题: Re: [NextBoy/skill] 改造vue-quill-editor:实现图片上传到服务器再插入富文本 (#2) this.quill是什么,项目里报错,没有定义 — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

image
自定义的上传图片方法无法识别
this.$refs.myQuillEditor.quill 的 .quill这个方法

@ssshjx
Copy link

ssshjx commented Feb 7, 2020 via email

@dyan1992
Copy link

dyan1992 commented Feb 7, 2020 via email

@dyan1992
Copy link

dyan1992 commented Feb 7, 2020 via email

@ssshjx
Copy link

ssshjx commented Feb 7, 2020 via email

@dyan1992
Copy link

dyan1992 commented Feb 7, 2020 via email

@dyan1992
Copy link

dyan1992 commented Feb 7, 2020 via email

@ssshjx
Copy link

ssshjx commented Feb 7, 2020 via email

@zheyaowang
Copy link

方法不错,解决了当前问题,但是删除当前图片信息时,如何删除服务器上的图片

@ssshjx
Copy link

ssshjx commented Feb 27, 2020 via email

@PG-one-Mr-Wang
Copy link

你们有谁报跨域的错误吗?为啥我这个会报跨域的错啊

@wellfrog1
Copy link

你们有谁报跨域的错误吗?为啥我这个会报跨域的错啊

我也报跨域问题,老哥,你解决了吗

@Hxmic
Copy link

Hxmic commented Oct 14, 2020

各位老哥我想问下,添加后的数据,添加后,再次编辑,会报错。 拿不到数据
代码如下:

import Quill from 'quill'
const BlockEmbed = Quill.import('blots/block/embed')


class LinkImage extends BlockEmbed {
	static create(value) {
		const node = super.create()
		node.setAttribute('href', value.url)
		node.setAttribute('target', '_blank')

		const child = document.createElement('img')
		child.setAttribute('src', value.src)
                child.setAttribute('alt', value.alt)

		node.appendChild(child)
		return node
	}

	// static value(domNode) {
	// 	return domNode.querySelector('src')
	// }
}

LinkImage.blotName = 'link-image'
LinkImage.tagName = 'a'
Quill.register(LinkImage)

export default LinkImage;


// let DEFAULT = {
                //     src: 'https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1602665105&di=d32971bce3bb455ae15bfe021dd05ebc&src=http://a0.att.hudong.com/18/56/14300000958002128488569856508.jpg',
                //     alt: '',
                //     url: ''
                // }


                // this.editor.insertEmbed(index, 'link-image', DEFAULT)

有没有遇到过,求解

@xibushijie
Copy link

可以,好用,美得很

@suhangshow
Copy link

老哥,按照上面的代码,服务器可以接受到图片,但是on:success钩子函数不生效是啥原因,有没有我这种问题的

@xibushijie
Copy link

确定接口没问题吗?我用的就是这个方法,正常啊

@suhangshow
Copy link

确定接口没问题吗?我用的就是这个方法,正常啊

老哥你看我这个,我上传成功的函数打印不出任何东西,也没有报错,后台倒是收到了我上传成功的数据
微信图片_20211220142355
微信图片_20211220142405

@DJNnnnn
Copy link

DJNnnnn commented May 22, 2022

有没有老哥知道 为什么会报401的错误

@S-hz
Copy link

S-hz commented Nov 11, 2022

image
获取布到.quill怎么办啊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests