vue使用富文本编辑器vue-quill-editor实现配合后台将图片上传至七牛
一、全局注册:main.js
import Vue from 'vue' import VueQuillEditor, { Quill } from 'vue-quill-editor' import { ImageDrop } from 'quill-image-drop-module' import ImageResize from 'quill-image-resize-module' import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' Quill.register('modules/imageDrop', ImageDrop) Quill.register('modules/imageResize', ImageResize) Vue.use(VueQuillEditor)
二、在组件中使用editor.vue
<template> <div class="editor"> <h3 class="otitle">写文章</h3> <!-- 编辑器 --> <div class="editor_box"> <quill-editor v-model="content" ref="myQuillEditor" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)"> </quill-editor> </div> <!-- 保存 --> <div class="save_box"> <el-button type="primary" @click="save()" size="mini">保存</el-button> </div> <!-- 文件上传组件 --> <input type="file" value="" name="image" accept="image/*" @change="uploadImg" ref="uploadInput" class="uploadInput" /> </div> </template> <script> import { createAPI, addAccessToken, createImgSrc } from '@/utils/request' import qs from 'qs' import { unloadImg } from '@/utils/qiniu_unload' // 编辑器的参数配置 const toolbarOptions = [ ['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'], // 清除文本格式 ['link', 'image' // 'video' ] // 链接、图片、视频 ] let iniCont = '' let myvue = { name: '', data () { return { content: iniCont, // 编辑器内容 // 工具栏配置 editorOption: { theme: 'snow', // 主题 placeholder: '您想说点什么?', modules: { toolbar: { container: toolbarOptions, // container: "#toolbar",//也可直接指向元素 handlers: { image: function (value) { if (value) { document.querySelector('.uploadInput').click() } else { this.quill.format('image', false) } } // link: function(value) { // if (value) { // var href = prompt('请输入url'); // this.quill.format("link", href); // } else { // this.quill.format("link", false); // } // }, } }, imageResize: { //设置图片大小 displayStyles: { backgroundColor: 'black', border: 'none', color: 'white' }, modules: [ 'Resize', 'DisplaySize', 'Toolbar' ] } } }, header: { // token: sessionStorage.token } } }, computed: {}, mounted () {}, methods: { // 保存 save () { }, // 失去焦点 onEditorBlur (quill) { // console.log('editor blur!', quill) }, // 获得焦点 onEditorFocus (quill) { // console.log('editor focus!', quill) }, // 编辑器已准备完成 onEditorReady (quill) { // console.log('editor ready!', quill) }, // 关闭弹窗 close () { this.$refs.uploadInput.value = '' this.$store.commit('changeDialog', false) }, // 获取裁剪后的图片 getCropBlob () { let that = this that.$refs.cropper.getCropBlob((data) => { let _this = this unloadImg(data, function (res) {//上传图片 // 获取富文本组件实例 let quill = _this.$refs.myQuillEditor.quill // 获取光标所在位置 let length = quill.getSelection().index // 插入图片 res.url为服务器返回的图片地址 quill.insertEmbed(length, 'image', createImgSrc(res.res)) // 调整光标到最后 quill.setSelection(length + 1) }, function (res) { this.$message.error(res.res) }) that.close() }) } }, created () { } } export default myvue </script> <style> /* 汉化 */ .ql-container.ql-snow::-webkit-scrollbar { display: none; } .ql-editor::-webkit-scrollbar{ 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> <style lang='scss' > @import './editor.scss'; </style>
以上流程是由<input type="file" value="" name="image" accept="image/*" @change="uploadImg" ref="uploadInput" class="uploadInput" />获取文件对象,然后将文件对象传至事先封装好的unloadImg方法中,如果上传成功,则回调方法中返回图片的key,再将拼接的图片插入vue-quill-editor的光标处。
三、qiniu_unload.js将图片上传至七牛
import {createAPI, addAccessToken} from '@/utils/request' import storeLocal from 'store' // import qs from 'qs' let uploadToken = storeLocal.get('imgtoken') || 'abcdefg'// 从本地获取图片上传凭证 let imgcache = '' let f1cache = '' let f2cache = '' // 上传图片 function unloadImg (data, f1, f2, boot) { imgcache = data f1cache = f1 f2cache = f2 const formdata = new FormData() formdata.append('file', data) formdata.append('key', 'img_' + new Date().getTime()) // 文件名 formdata.append('token', uploadToken) addAccessToken('no_check')// 由于七牛云token失效返回401与本地后台token失效code码一致,因此在此处传值申明不验证本地登录 .post('http://upload-z2.qiniup.com/', formdata, { validateStatus: status => status === 200 }) .then(reponse => { let obj = { type: true, res: reponse.data.key } f1(obj)// 成功回调 }) .catch((response) => { if (boot) { // 拦截,防止死循环 let obj = { type: false, res: response.data.error } f2(obj)// 失败回调 } if (response.status === 401) { // 上传的图片token失效 getTokenAgain()// 获取新的token } }) } // 重新请求token function getTokenAgain () { addAccessToken() .post(createAPI('files/get_token'), '', { validateStatus: status => status === 200 }) .then(reponse => { uploadToken = reponse.data.data.unload_token storeLocal.set('imgtoken', uploadToken) unloadImg(imgcache, f1cache, f2cache, 1) }) .catch((response) => { return false }) } export { unloadImg }
结果展示:
裁剪工具用的vue-cropper,本文不做详细介绍。
注意事项
1、vue-quill-editor插件quill-image-resize-module 报错解决方法
解决方式就是修改wabpack配置
webpack.dev.conf.js
webpack.prod.conf.js 添加上即可解决问题,webpack.dev.conf.js是解决本地运行的问题,webpack.dev.conf.js是解决打包运行出错。
new webpack.ProvidePlugin({ 'window.Quill': 'quill/dist/quill.js', 'Quill': 'quill/dist/quill.js' })