vue3和element Plus完成可自定义裁剪大小、上传类型的公共组件

功能

可自定义设置图片尺寸信息、图片上传类型、图片最多可上传数量、父组件拿到图片的字段名。

安装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,只支持JPGJPEGPNG格式的图片文件
        </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
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小婵婵不怕鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值