目录
之前我整理过一篇文章,Quill编辑器实现图片上传功能(戳链接可查看)
但是想来那个是相当于自定义的更加简洁实用的工具栏toolbar,不过需要自定义的代码有些繁琐。
再遇到类似功能时我就想着寻找一个更简单的办法,使toolbar用原生的,仅仅改变img的处理方式。
注:上次quill是局部引用,这次是全局引用,不再赘述。两种方法各有优劣,各位看官,酌情使用。
1、页面引入
<!-- 富文本编辑器 -->
<el-form-item label="图文详情" prop="graphicDetails">
<quill-editor v-model="addForm.graphicDetails" ref="myQuillEditor"></quill-editor>
</el-form-item>
2、自定义imge处理的图片上传组件
思路:自定义的处理方法使之弹出自定义的图片上传组件即可,之后便是上传的那一套流程。
mounted() {
this.$refs.myQuillEditor.quill
.getModule('toolbar')
.addHandler('image', this.imgHandler)
},
imgHandler(state) {
this.addRange = this.$refs.myQuillEditor.quill.getSelection()
if (state) {
document.querySelector('.open-file').click()
}
},
<el-upload action="" :show-file-list="false" :before-upload="quillUpload">
<el-button v-show="false" ref="myButton" class="open-file" size="small" type="primary">点击上传</el-button>
</el-upload>
3、图片上传成功后回显嵌入
async quillUpload(file) {
const isJPG = file.type === 'image/jpeg'
const isLt2M = file.size / 1024 / 1024 < 5
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!')
return
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 5MB!')
return
}
var formData = new FormData()
formData.append('file', file)
const { data: res } = await this.$http.post('/upload', formData)
if (!res.success) {
//this.$message.error('图片上传失败!')
return this.$message.error(res.msg)
}
var url = res.data.url
var that = this
setTimeout(function () {
// 获取富文本组件实例
let quill = that.$refs.myQuillEditor.quill
// 获取光标所在位置
let length = quill.getSelection().index
quill.insertEmbed(length, 'image', url) // 调整光标到最后
quill.setSelection(length + 1)
}, 500)
},
4、最终呈现效果
5、源码
之后再附上全部vue源码,方便各位参考。
<template>
<!-- 卡片视图 -->
<el-card>
<!-- 提示 -->
<el-alert title="添加商品信息" type="info" center show-icon :closable="false"></el-alert>
<!-- Tab栏 -->
<el-form :model="addForm" size="small" :rules="addFormRules" ref="addFormRef" label-width="100px">
<el-form-item label="所属店铺" prop="shopId">
<el-select v-model="addForm.shopId" placeholder="请选择" style="width:100%;">
<el-option v-for="item in shopList" :key="item.shopId" :label="item.shopName" :value="item.shopId">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品标题" prop="title">
<el-input v-model="addForm.title"></el-input>
</el-form-item>
<el-form-item label="商品名称" prop="goodsName">
<el-input v-model="addForm.goodsName"></el-input>
</el-form-item>
<el-form-item label="商品编号" prop="goodsNum">
<el-input v-model="addForm.goodsNum"></el-input>
</el-form-item>
<el-form-item label="市场价格" prop="marketPrice">
<el-input v-model="addForm.marketPrice" type="number"></el-input>
</el-form-item>
<el-form-item label="实际定价" prop="truePrice">
<el-input v-model="addForm.truePrice" type="number"></el-input>
</el-form-item>
<el-form-item label="商品分类" prop="goodsType">
<el-cascader v-model="addForm.goodsType" :show-all-levels="false" :options="treeList" :props="cascaderProps" @change="onCascadeChange" style="width:100%;"></el-cascader>
</el-form-item>
<el-form-item label="产地" prop="productionPlace">
<el-input v-model="addForm.productionPlace"></el-input>
</el-form-item>
<el-form-item label="规格/重量" prop="spec">
<el-input v-model="addForm.spec"></el-input>
</el-form-item>
<el-form-item label="数量" prop="stockNum">
<el-input v-model="addForm.stockNum" type="number"></el-input>
</el-form-item>
<el-form-item label="保质期" prop="shelfLife">
<el-input v-model="addForm.shelfLife"></el-input>
</el-form-item>
<!-- <el-form-item label="轮播图片" prop="">
<el-upload action="" list-type="picture-card" :on-remove="handleRemove" :http-request="UploadImage" :file-list="imgList">
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item> -->
<el-form-item label="轮播图片" prop="">
<el-image :src="item.url" style="width:178px;height:178px;" :key="index" v-for="(item,index) in imgList" :preview-src-list="[item.url]"></el-image>
<el-upload v-if="imgList.length<5" accept="image/png, image/jpeg" class="avatar-uploader" action="" :show-file-list="false" :before-upload="beforeAvatarUpload">
<i class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="商品主图" prop="mainImg">
<el-radio-group v-model="addForm.mainImg">
<el-radio :label="item.url" :key="index" v-for="(item,index) in imgList" style="width: 178px;display:inline-block;text-align:center;margin-right:10px;">选择</el-radio>
</el-radio-group>
</el-form-item>
<!-- 富文本编辑器 -->
<el-form-item label="图文详情" prop="graphicDetails">
<quill-editor v-model="addForm.graphicDetails" ref="myQuillEditor"></quill-editor>
</el-form-item>
<!-- 添加商品 -->
<el-form-item>
<el-button type="primary" @click="addGoods">添加</el-button>
<el-button @click="cancelBtn">取消</el-button>
</el-form-item>
</el-form>
<el-upload action="" :show-file-list="false" :before-upload="quillUpload">
<el-button v-show="false" ref="myButton" class="open-file" size="small" type="primary">点击上传</el-button>
</el-upload>
</el-card>
</template>
<script>
import 'quill/dist/quill.snow.css'
export default {
components: {},
data() {
return {
shopList: [],
imgList: [],
addForm: {
shopId: '',
title: '',
goodsName: '',
goodsNum: '',
productionPlace: '',
shelfLife: '',
truePrice: undefined,
marketPrice: undefined,
spec: '',
stockNum: undefined,
//轮播图
carouselImg: '',
//商品主图
mainImg: '',
goodsType: '',
// 商品详情描述
graphicDetails: '',
},
addFormRules: {
title: [
{
required: true,
message: '请输入商品标题',
trigger: 'blur',
},
],
goodsName: [
{
required: true,
message: '请输入商品名称',
trigger: 'blur',
},
],
truePrice: [
{
required: true,
message: '请输入实际定价',
trigger: 'blur',
},
],
marketPrice: [
{
required: true,
message: '请输入市场价格',
trigger: 'blur',
},
],
promotionPrice: [
{
required: true,
message: '请输入促销价格',
trigger: 'blur',
},
],
spec: [
{
required: true,
message: '请输入商品重量',
trigger: 'blur',
},
],
stockNum: [
{
required: true,
message: '请输入商品数量',
trigger: 'blur',
},
],
goodsType: [
{
required: true,
message: '请选择商品分类',
trigger: 'blur',
},
],
mainImg: [
{
required: true,
message: '请选择商品主图',
trigger: 'change',
},
],
},
treeList: [],
// 级联选择器配置
cascaderProps: {
// 配置触发选项 hover/click
expandTrigger: 'hover',
value: 'name',
label: 'name',
children: 'children',
checkStrictly: true,
},
editorOption: {
modules: {
toolbar: {
container: '#toolbar',
handlers: {
image: function (value) {
let that = this
if (value) {
that.container
.querySelector('.open-file')
.click()
} else {
this.quill.format('image', false)
}
},
},
},
},
},
}
},
mounted() {
this.getCascadeList()
this.getShopList()
},
mounted() {
this.$refs.myQuillEditor.quill
.getModule('toolbar')
.addHandler('image', this.imgHandler)
},
computed: {},
methods: {
// 点击图片ICON触发事件
imgHandler(state) {
this.addRange = this.$refs.myQuillEditor.quill.getSelection()
if (state) {
document.querySelector('.open-file').click()
}
},
async quillUpload(file) {
const isJPG = file.type === 'image/jpeg'
const isLt2M = file.size / 1024 / 1024 < 5
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!')
return
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 5MB!')
return
}
var formData = new FormData()
formData.append('file', file)
const { data: res } = await this.$http.post('/upload', formData)
if (!res.success) {
//this.$message.error('图片上传失败!')
return this.$message.error(res.msg)
}
var url = res.data.url
var that = this
setTimeout(function () {
// 获取富文本组件实例
let quill = that.$refs.myQuillEditor.quill
// 获取光标所在位置
let length = quill.getSelection().index
quill.insertEmbed(length, 'image', url) // 调整光标到最后
quill.setSelection(length + 1)
}, 500)
},
async getShopList() {
const { data: res } = await this.$http.get('/shop/singleList')
if (!res.success) {
//return this.$message.error('获取商品分类列表失败!')
return this.$message.error(res.msg)
}
this.shopList = res.data
},
async beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg'
const isLt2M = file.size / 1024 / 1024 < 5
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!')
return false
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 5MB!')
return false
}
var formData = new FormData()
formData.append('file', file)
const { data: res } = await this.$http.post(
'/uploadShrink',
formData
)
console.info(res)
if (!res.success) {
//return this.$message.error('图片上传失败!')
return this.$message.error(res.msg)
}
this.imgList.push({ url: res.data.url })
console.info(this.imgList)
return this.$message.success('图片上传成功!')
},
async getCascadeList() {
const { data: res } = await this.$http.get('/category/treeList')
if (!res.success) {
//return this.$message.error('获取商品分类列表失败!')
return this.$message.error(res.msg)
}
this.treeList = res.data
},
// 级联选择器选中项变化时出发
// 把level id 加一并回传
onCascadeChange(val) {
this.addForm.goodsType = val[val.length - 1]
},
// 添加商品表单验证
addGoods() {
this.addForm.carouselImg = '' //初始化
if (this.imgList.length == 0) {
this.$message.error('请至少设置一张轮播图')
return false
} else {
this.imgList.map((item) => {
this.addForm.carouselImg += item.url + ','
})
this.addForm.carouselImg = this.addForm.carouselImg.substring(
0,
this.addForm.carouselImg.length - 1
)
}
this.$refs.addFormRef.validate(async (valid) => {
if (!valid) return this.$message.error('表单验证不通过!')
const { data: res } = await this.$http.post(
'/goods/add',
this.addForm
)
if (!res.success) return this.$message.error(res.msg)
this.$message.success('添加商品成功!')
this.$router.push('/goodsList')
})
},
cancelBtn() {
this.$router.push('/goodsList')
},
},
}
</script>
<style lang='less' scoped>
.el-checkbox {
margin: 0 8px 0 0 !important;
}
.el-steps {
margin-top: 15px;
margin-bottom: 15px;
}
.el-tabs {
margin-top: 15px;
}
// .avatar-uploader
.el-image {
border: 1px dashed #d9d9d9;
border-radius: 6px;
width: 178px;
height: 178px;
margin-right: 10px;
}
.avatar-uploader {
border: 1px dashed #d9d9d9;
border-radius: 6px;
height: 178px;
cursor: pointer;
display: inline-block;
width: 178px;
vertical-align: top;
}
.avatar-uploader:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
//border: 1px solid red;
//margin-top: 80px;
line-height: 178px;
text-align: center;
}
</style>
就是这样啦,如果你想让我知道你来过,欢迎关注、收藏和评论,本程序媛此刻心情好,过时不候^-^