前言
小编参加的第一个项目,就遇到了麻烦的多图上传,通过多天的努力,写出了一个多图的组件,希望可以帮助到大家
组件template部分
多图上传按钮+多图上传弹窗+图片上的预览删除图标
<template>
<div>
<div class="many">
<el-form-item>
<div class="upload-item">
<el-button type="primary" @click="uploadFile">多图上传</el-button>
</div>
</el-form-item>
</div>
<el-dialog title="图片预览" :visible.sync="dialogImgVisible" width="50%">
<img :src="dialogImageUrl" alt="" class="previewImg" />
</el-dialog>
<!--多图上传弹窗界面-->
<el-dialog :title="'上传'" :visible.sync="dialogFormVisible" custom-class="pub_dialog" >
<el-form style="width: 750px;height: 380px">
<!--内容部分 -->
<el-form-item><!---->
<div style="display: flex;justify-content: center">
<label>选择文件:</label>
<div>
<div class="desc">支持 jpg, png 图片格式,且不超过500kb</div>
<el-upload
:action="UPLOAD_URL"
:headers="authorToken"
:auto-upload="true"
accept="image/jpg,image/png,image/jpeg"
:on-success="handleSuccess"
:before-upload="handleBeforeUpload"
:show-file-list="false"
multiple
:limit="10"
:on-exceed="handleExceed"
:file-list="fileList">
<el-button size="small" type="primary">上传图片</el-button>
</el-upload>
</div>
</div>
<div class="fileList" style="margin-top: 10px;display: flex;flex-wrap: wrap;">
<div class="item" v-for="(item,index) in images" :key="index">
<img :src="item.url" alt="" :key="index" style=" width: 45%;height: 100%" class = "imgList">
<div class="scissor-icon">
<i class="el-icon-scissors" @click="changeFile(item)"></i>
</div>
<div class="delete-icon">
<i class="el-icon-delete" @click="handleRemove(item)"></i>
</div>
<div class="search-icon">
<i class="el-icon-search" @click="handlePreview(item)"></i>
</div>
<el-input
type="textarea"
:autosize="{ minRows: 7, maxRows: 7}"
placeholder="请输入图片描述"
v-model="item.manyDescription"
:key="index"
style=" width: 55%;height: 100%;margin-left: 10px">
</el-input>
</div>
</div>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="closeDialog">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
组件script部分
1.变量数据区域
代码如下(示例):
<script>
export default {
name: "UploadMany",
data() {
return {
textarea:'',
dialogImageUrl: '',
dialogImgVisible: false,
dialogVisible: false,
fileList: [],
imgs: [],
images: [],
UPLOAD_URL: "/v1/admin/common/upload",//这里填写你的后端上传接口地址
authorToken: {
Authorization: 'Bearer ' + sessionStorage.getItem("token"),
siteId:sessionStorage.getItem("siteId")
},
param: {
token: ''
},
fileNum: 0,
num: 0,
dialogFormVisible: false,//添加表单弹出框是否显示
dialogChangeVisible: false,
picsList: [], //页面显示的数组
// 防止重复提交
loading: true,
}
},
2.方法区域
代码如下(示例):
methods: {
//删除方法
handleRemove(file) {
console.log(file)
// 1.获取将要删除的图片的临时路径
const filePath = file.url
// 2.从数组中,找到这个图片对应的索引值
const i = this.imgs.findIndex((x) => x.url === filePath)
// 3.调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除
this.imgs.splice(i, 1)
console.log(this.imgs)
},
//预览方法
handlePreview(file) {
console.log(file);
this.dialogImageUrl = file.url;
this.dialogImgVisible = true;
},
//限制上传文件个数
handleExceed(files, fileList) {
this.$message.warning(`当前限制选择 10 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
//上传成功后
handleSuccess(response, fileList) {
console.log(response);
console.log(fileList)
this.loading = false
if(response.code === 200){
this.imgs.push({name: response.data.resourceName, url: response.data.resourceUrl, manyDescription: '', manyResourceId: response.data.id})
this.num++;
if(this.num == this.fileNum){
for(let i = 0; i < this.num ; i++){
this.$emit('getManyImg', this.imgs[i])
}
this.num = 0;
this.fileNum = 0;
this.images = this.imgs;
this.imgs = [];
}
}else{
this.$message.error('上传失败');
}
},
handleBeforeUpload(file) {
// 这里做可以做文件校验操作
const isImg = /^image\/\w+$/i.test(file.type)
if (!isImg && this.fileType == 'image/*') {
this.$message.error('只能上传 JPG、PNG、GIF 格式!')
return false
}
this.fileNum++;
},
uploadFile(){
this.dialogFormVisible = true;
this.loading = false;
},
closeDialog(){
this.dialogFormVisible = false;
this.imgs = [];
this.images = [];
}
}
}
组件使用
1.在你需要用到的界面vue里导入组件
import UploadMany from '@/components/upload/UploadMany';
import {getToken} from '@/utils/auth';
export default {
name: "TestEditor",
components: {
UploadMany,
},
2.template部分使用组件
<el-col :span="24">
<el-form-item prop="manyImg" label="多图上传:" :label-width="imgWidth" class="form">
<upload-many v-model="dialogForm.manyImg" @getManyImg="getManyImg" ></upload-many>
<div class="fileList" style="margin-top: 10px;display: flex;flex-wrap: wrap;">
<div class="item" v-for="(itemPhoto,indexPhoto) in dialogForm.images" :key="indexPhoto">
<div class="item-left" style="position: relative">
<img :src="itemPhoto.url" alt="" :key="indexPhoto" class = "imgList">
<div class="item-bottom">
<div class="search-icon">
<i class="el-icon-search" @click="handlePreview(itemPhoto)"></i>
</div>
<div class="delete-icon">
<i class="el-icon-delete" @click="handleRemove(itemPhoto)"></i>
</div>
</div>
</div>
<el-input
type="textarea"
:autosize="{ minRows: 7, maxRows: 7}"
placeholder="请输入图片描述"
v-model="itemPhoto.manyDescription"
style=" width: 55%;height: 100%;margin-left: 10px">
</el-input>
</div>
</div>
</el-form-item>
</el-col>
3.方法部分
getManyImg(imgs) {
this.dialogForm.images.push(imgs);
console.log(this.dialogForm.images)
},
handleRemove(file) {
console.log(file)
// 1.获取将要删除的图片的临时路径
const filePath = file.url
// 2.从数组中,找到这个图片对应的索引值
const i = this.dialogForm.images.findIndex((x) => x.url === filePath)
// 3.调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除
this.dialogForm.images.splice(i, 1)
},
//预览图片
handlePreview(file) {
console.log(file);
this.dialogImageUrl = file.url;
this.dialogImgVisible = true;
},
组件完整代码(含裁剪组件,不需要请手动删除)
<template>
<div>
<div class="many">
<el-form-item>
<div class="upload-item">
<el-button type="primary" @click="uploadFile">多图上传</el-button>
</div>
</el-form-item>
</div>
<!--裁剪弹窗-->
<!-- vueCropper 剪裁图片实现-->
<el-dialog title="图片剪裁" :visible.sync="dialogChangeVisible" append-to-body>
<div class="cropper-content">
<div class="cropper" style="text-align:center">
<vueCropper
ref="cropper"
:img="option.img"
:outputSize="option.size"
:outputType="option.outputType"
:info="true"
:full="option.full"
:canMove="option.canMove"
:canMoveBox="option.canMoveBox"
:original="option.original"
:autoCrop="option.autoCrop"
:fixed="option.fixed"
:fixedNumber="option.fixedNumber"
:centerBox="option.centerBox"
:infoTrue="option.infoTrue"
:fixedBox="option.fixedBox"
></vueCropper>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogChangeVisible = false">取 消</el-button>
<el-button type="primary" @click="finish" :loading="loading">确认</el-button>
</div>
</el-dialog>
<el-dialog title="图片预览" :visible.sync="dialogImgVisible" width="50%">
<img :src="dialogImageUrl" alt="" class="previewImg" />
</el-dialog>
<!--多图上传弹窗界面-->
<el-dialog :title="'上传'" :visible.sync="dialogFormVisible" custom-class="pub_dialog" >
<el-form style="width: 750px;height: 380px">
<!--内容部分 -->
<el-form-item><!---->
<div style="display: flex;justify-content: center">
<label>选择文件:</label>
<div>
<div class="desc">支持 jpg, png 图片格式,且不超过500kb</div>
<el-upload
:action="UPLOAD_URL"
:headers="authorToken"
:auto-upload="true"
accept="image/jpg,image/png,image/jpeg"
:on-success="handleSuccess"
:before-upload="handleBeforeUpload"
:show-file-list="false"
multiple
:limit="10"
:on-exceed="handleExceed"
:file-list="fileList">
<el-button size="small" type="primary">上传图片</el-button>
</el-upload>
</div>
</div>
<div class="fileList" style="margin-top: 10px;display: flex;flex-wrap: wrap;">
<div class="item" v-for="(item,index) in images" :key="index">
<img :src="item.url" alt="" :key="index" style=" width: 45%;height: 100%" class = "imgList">
<div class="scissor-icon">
<i class="el-icon-scissors" @click="changeFile(item)"></i>
</div>
<!-- <div class="refresh-icon">-->
<!-- <i class="el-icon-refresh" @click="handleRemove()"></i>-->
<!-- </div>-->
<div class="delete-icon">
<i class="el-icon-delete" @click="handleRemove(item)"></i>
</div>
<div class="search-icon">
<i class="el-icon-search" @click="handlePreview(item)"></i>
</div>
<el-input
type="textarea"
:autosize="{ minRows: 7, maxRows: 7}"
placeholder="请输入图片描述"
v-model="item.manyDescription"
:key="index"
style=" width: 55%;height: 100%;margin-left: 10px">
</el-input>
</div>
</div>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="closeDialog">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import VueCropper from 'vue-cropper'
import Vue from "vue";
Vue.use(VueCropper)
export default {
name: "UploadMany",
data() {
return {
textarea:'',
dialogImageUrl: '',
dialogImgVisible: false,
dialogVisible: false,
fileList: [],
imgs: [],
images: [],
UPLOAD_URL: "/v1/admin/common/upload",
authorToken: {
Authorization: 'Bearer ' + sessionStorage.getItem("token"),
siteId:sessionStorage.getItem("siteId")
},
param: {
token: ''
},
fileNum: 0,
num: 0,
dialogFormVisible: false,//添加表单弹出框是否显示
dialogChangeVisible: false,
// 裁剪组件的基础配置option
option: {
img: '', // 裁剪图片的地址
info: true, // 裁剪框的大小信息
outputSize: 0.8, // 裁剪生成图片的质量
outputType: 'jpeg', // 裁剪生成图片的格式
canScale: false, // 图片是否允许滚轮缩放
autoCrop: true, // 是否默认生成截图框
// autoCropWidth: 300, // 默认生成截图框宽度
// autoCropHeight: 200, // 默认生成截图框高度
fixedBox: true, // 固定截图框大小 不允许改变
fixed: true, // 是否开启截图框宽高固定比例
fixedNumber: [7, 5], // 截图框的宽高比例
full: true, // 是否输出原图比例的截图
canMoveBox: false, // 截图框能否拖动
original: false, // 上传图片按照原始比例渲染
centerBox: false, // 截图框是否被限制在图片里面
infoTrue: true // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
},
picsList: [], //页面显示的数组
// 防止重复提交
loading: true,
}
},
methods: {
handleRemove(file) {
console.log(file)
// 1.获取将要删除的图片的临时路径
const filePath = file.url
// 2.从数组中,找到这个图片对应的索引值
const i = this.imgs.findIndex((x) => x.url === filePath)
// 3.调用数组的 splice 方法,把图片信息对象,从 pics 数组中移除
this.imgs.splice(i, 1)
console.log(this.imgs)
},
handlePreview(file) {
console.log(file);
this.dialogImageUrl = file.url;
this.dialogImgVisible = true;
},
handleExceed(files, fileList) {
this.$message.warning(`当前限制选择 10 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
handleSuccess(response, fileList) {
console.log(response);
console.log(fileList)
this.loading = false
if(response.code === 200){
this.imgs.push({name: response.data.resourceName, url: response.data.resourceUrl, manyDescription: '', manyResourceId: response.data.id})
this.num++;
if(this.num == this.fileNum){
for(let i = 0; i < this.num ; i++){
this.$emit('getManyImg', this.imgs[i])
}
this.num = 0;
this.fileNum = 0;
this.images = this.imgs;
this.imgs = [];
}
}else{
this.$message.error('上传失败');
}
},
// 裁剪按钮 限制图片大小
changeFile(file) {
console.log(file)
this.option.img = file.url
console.log(this.option.img)
this.dialogChangeVisible = true
},
// 点击裁剪
finish() {
this.$refs.cropper.getCropBlob((data) => {
console.log(data)
console.log(data.size)
this.$data.dialogChangeVisible = false
this.axios.post("/v1/admin/common/upload",data).then((res) => {
let code = res.data.code;
if (code == 200) {
this.$data.dialogFormVisible = false
this.$message.success("上传成功");
}
}).catch((error) => {
console.log(error);
});
})
},
handleBeforeUpload(file) {
// 这里做可以做文件校验操作
const isImg = /^image\/\w+$/i.test(file.type)
if (!isImg && this.fileType == 'image/*') {
this.$message.error('只能上传 JPG、PNG、GIF 格式!')
return false
}
this.fileNum++;
},
uploadFile(){
this.dialogFormVisible = true;
this.loading = false;
},
closeDialog(){
this.dialogFormVisible = false;
this.imgs = [];
this.images = [];
}
}
}
</script>
<style lang="scss" scoped>
.el-dialog{
width: 50%;
}
.item {
width: 300px;
height: 140px;
position: relative;
display: flex;
margin: 10px;
.delete-icon {
display: none;
}
.refresh-icon {
display: none;
}
.search-icon {
display: none;
}
.scissor-icon {
display: none;
}
&:hover {
.scissor-icon {
display: block;
position: absolute;
width: 35px;
height: 40px;
line-height: 40px;
left: 100px;
top: 100px;
background: rgba(59, 60, 61, 0.5);
// box-sizing: content-box;
z-index: 999;
cursor: pointer;
text-align: center;
i {
margin: 8px 10px 0 0;
display: block;
font-size: 24px;
color: white;
}
}
.delete-icon {
display: block;
position: absolute;
width: 35px;
height: 40px;
left: 0px;
top: 100px;
background: rgba(59, 60, 61, 0.5);
// box-sizing: content-box;
z-index: 999;
cursor: pointer;
text-align: center;
i {
margin: 8px 10px 0 10px;
display: block;
font-size: 24px;
color: white;
}
}
.refresh-icon {
display: block;
position: absolute;
width: 35px;
height: 40px;
left: 35px;
top: 100px;
background: rgba(59, 60, 61, 0.5);
// box-sizing: content-box;
z-index: 999;
cursor: pointer;
text-align: center;
i {
margin: 8px 10px 0 0;
display: block;
font-size: 24px;
color: white;
}
}
.search-icon {
display: block;
position: absolute;
width: 65px;
height: 40px;
left: 35px;
top: 100px;
background: rgba(59, 60, 61, 0.5);
// box-sizing: content-box;
z-index: 999;
cursor: pointer;
text-align: center;
i {
margin: 8px 10px 0 10px;
display: block;
font-size: 24px;
color: white;
}
}
}
}
.imgList {
border: 1px dashed #d9d9d9;
border-radius: 5px;
box-sizing: border-box;
width: 180px;
height: 180px;
margin-top: 0px;
&:hover {
border: 1px dashed #409eff;
}
}
// 截图
.cropper-content {
.cropper {
width: auto;
height: 300px;
}
}
.previewImg {
width: 50%;
height: 100%
}
</style>