功能
可自定义设置图片尺寸信息、图片上传类型、图片最多可上传数量、父组件拿到图片的字段名。
安装element plus及vue-cropperjs
npm install element-plus --save
npm install --save vue-cropperjs
父组件A
一共需要自定义6个数据,分别是
- imgUploadType(类型:cover-单张图的上传,image_list-轮播图)
- maxLength(最多上传数量:当类型为cover时只能是1)
- imgWidth和imgHeight(图片宽度及高度:根据这个数据计算裁剪比例)
- imgUploadName(字段名:子组件上传完后父组件可根据这个字段名获取数据,作用是区别一父组件同时引用多次该子组件)
- imgSize(图片大小:目前没有做判断,仅仅展示使用)
获取的信息格式可以自行修改
<template>
<div class="bg_white" style="padding-bottom: 200px;">
<el-form :model="dataInfo" label-width="auto" style="max-width: 800px" ref="editForm" :rules="rules">
<el-form-item label="首页封面:" prop="coverImageId">
<ImageUpload imgUploadType="cover" :maxLength="1" :imgWidth="390" :imgHeight="280" :imgSize="200"
imgUploadName="cover" @subImageInfo="getImageInfo" />
</el-form-item>
<el-form-item label="门店轮播图:" prop="coverImageId">
<ImageUpload imgUploadType="image_list" :maxLength="3" :imgWidth="390" :imgHeight="220" :imgSize="200"
imgUploadName="image_list" @subImageInfo="getImageInfo" />
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, onMounted, defineProps } from 'vue'
import { getStore, updateStore, getStoreDetail } from '@/api/store.js'
import router from '@/router/index.js'
import ImageUpload from '@/components/upload/ImageUpload.vue';
defineOptions({
name: 'StoreDetail',
})
const dataInfo = ref({})
const getImageInfo = (info) => {
switch (info['imgUploadName']) {
case 'cover':
//获取传过来的值(具体什么格式可以在子组件内更改)
dataInfo.value.coverImageId = info['coverId']
break;
case 'image_list':
//获取传过来的值
dataInfo.value.carouselImages = info['image_listId']
break;
}
}
</script>
子组件B
<template>
<div>
<div v-if="imgUploadType == 'image_list'" class="margin_t">
<div v-if="image_list.length>0" class="avater-container">
<div v-for="(item, index) in image_list" :key="index" class="margin_r">
<img :src="item" alt="Avater" class="avater">
<div class="color_gray" @click="image_list.splice(index, 1)">删除</div>
</div>
</div>
<el-upload class="avater-uploader" :show-file-list="false" :on-change="handlePictureCardPreview"
list-type="picture-card" :auto-upload="false" v-if="!cover && (image_list.length < maxLength)">
<div class="width100 height100 text_align">
<img src="@/assets/jia.png" class="icon_size40 margin_t30" alt="">
<p class="color_gray margin_t">上传照片</p>
</div>
</el-upload>
</div>
<el-upload class="avater-uploader" :show-file-list="false" :on-change="handlePictureCardPreview"
list-type="picture-card" :auto-upload="false" v-if="imgUploadType == 'cover'">
<img :src="cover" v-if="cover" alt="Avater" class="avater">
<div class="width100 height100 text_align" v-else>
<img src="@/assets/jia.png" class="icon_size40 margin_t30" alt="">
<p class="color_gray margin_t">上传照片</p>
</div>
</el-upload>
<div class="color_gray margin_t10">建议宽{{imgWidth}}px,高{{imgHeight}}px,大小不超过{{imgSize}}k,只支持JPG、JPEG或PNG格式的图片文件
</div>
<el-dialog v-model="showCropper">
<template #header="{ close, titleId }">
<div class="my-header">
<h4 :id="titleId" class="titlestyle">图片裁剪</h4>
<div class="cha-style" @click="close">
<img src="@/assets/cha.png" alt="" style="width: 20px;height: 20px;margin: 0 auto;">
</div>
</div>
</template>
<div class="relative">
<VueCropper ref="cropper" :img="previewImage" :src="previewImage" preview=".preview" v-if="showCropper"
:aspect-ratio="aspectRatio" :view-mode="1" :drag-mode="'move'" class="height100" />
<div class="margin_t">
<el-button @click="showCropper = false">取消</el-button>
<el-button type="primary" @click="getCroppedImage">裁剪并上传</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted, defineEmits } from 'vue'
import VueCropper from 'vue-cropperjs';
import 'cropperjs/dist/cropper.min.css'
import { dataURLtoFile } from "@/utils/compress";
import { upload } from '@/api/upload'
const emit = defineEmits(['subImageInfo']);
// 父组件传值
const props = defineProps({
imgUploadType: {
type: String,
required: true
},
imgUploadName: {
type: String,
required: true
},
maxLength: {
type: Number
},
imgWidth: {
type: Number
},
imgHeight: {
type: Number
},
imgSize: {
type: Number
},
})
const path = ref(import.meta.env.VITE_BASE_API + '/')
const previewImage = ref('')
const previewImageRef = ref(null)
const fileName = ref('')
const showCropper = ref(false)
const cropper = ref(null)
const aspectRatio = ref((props.imgWidth / props.imgHeight).toFixed(2))
const image_list = ref([])
const image_listID = ref([])
const cover = ref('')
const file = ref({})
const handlePictureCardPreview = (file) => {
previewImage.value = JSON.parse(JSON.stringify(file.url))
file.value = file
fileName.value = file.name
showCropper.value = true;
}
const getCroppedImage = () => {
let img = cropper.value.getCroppedCanvas().toDataURL('image/jpeg')
let newImg = dataURLtoFile(img, fileName.value)
let formData = new FormData();
let parentInfo = {
imgUploadName: props.imgUploadName,
coverID: '',
image_listID: [],
cover: '',
image_list: []
}
formData.append("file", newImg.file);
upload(formData).then(res => {
if (res.url === undefined) {
file.value = {}
}
//此处更改数据格式
if (props.imgUploadType == 'image_list') {
let imageList = image_list.value,
image_ids = image_listID.value
if (imageList === undefined) {
imageList = []
image_listID = []
}
imageList.push(path.value + res.data.file.url)
image_ids.push({ id: res.data.file.ID })
image_list.value = imageList
image_listID.value = image_ids
file.value = {}
parentInfo[props.imgUploadName] = image_list.value
parentInfo[props.imgUploadName + 'Id'] = image_listID.value
} else if (props.imgUploadType == 'cover') {
cover.value = path.value + res.data.file.url
parentInfo[props.imgUploadName] = cover.value
parentInfo[props.imgUploadName + 'Id'] = res.data.file.ID
}
emit('subImageInfo', parentInfo);
})
showCropper.value = false
}
</script>
<style>
.avater-uploader {
border-radius: 6px;
cursor: pointer;
overflow: hidden;
width: 150px;
height: 150px;
margin-top: 20px;
}
.avater {
border-radius: 6px;
cursor: pointer;
overflow: hidden;
width: 150px;
height: 150px;
}
.avater-container {
display: flex;
margin-top: 20px;
}
</style>
子组件引用的方法dataURLtoFile
上传文件格式都有可能存在不同,不同格式不同处理
// 将base64转换为file文件
export function dataURLtoFile (dataurl, filename) {
let arr = dataurl.split(',');
let mime = arr[0].match(/:(.*?);/)[1];
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
let file = new File([u8arr], filename, {type: mime});
const data = { //拼成你所需要传给后端的格式
file:file,
content:dataurl,
message:'',
status:''
};
return data
}