上传图片

基于el-upload的上传组件,包含宽高比 大小等校验

<!--
* @description 图片上传
!-->
<template>
    <div class="upload-image-box">
        <el-upload
            v-bind="$attrs"
            class="avatar-uploader"
            :action="uploadUrl"
            :accept="accept"
            :class="{ 'is-disabled': $attrs.disabled, 'has-img': !!imgUrl }"
            :headers="headers"
            :show-file-list="false"
            :on-success="handleAvatarSuccess"
            :before-upload="beforeAvatarUpload"
        >
            <img v-if="imgUrl" :src="imgUrl" class="avatar" />
            <div v-else class="uploader-placeholder">
                <span v-if="!isFailed">
                    <i class="el-icon-plus"></i>
                    <br />
                    <span>点击上传</span>
                </span>
                <div v-else class="failed-mask" @click="handleReUpload">
                    <span class="failed-text">点击重试</span>
                </div>
            </div>
            <div v-if="imgUrl" class="mask" @click.stop>
                <i class="ic-delete" @click.stop="deleteImg"></i>
            </div>
        </el-upload>
        <span class="tip-text">{{ tipText }}</span>
    </div>
</template>

<script>
import { getAccessToken } from '@/utils/auth'
export default {
    name: 'UploadImage',
    props: {
        imageUrl: {
            type: String,
            default: ''
        },
        accept: {
            type: String,
            default: '.png, .jpg, .jpeg, .gif'
        },
        tipText: {
            type: String,
            default: '支持png、jpg格式,大小不超过20M'
        },
        // 文件最大限制,单位M
        maxFileSize: {
            type: Number,
            default: 20
        },
        // 宽高比1:1, 用于宽高比校验
        ratio: {
            type: String,
            default: ''
        },
        // 校验最小宽度
        checkMinWidth: {
            type: Number,
            default: null
        },
        // 校验最小高度
        checkMinHeight: {
            type: Number,
            default: null
        }
    },
    data() {
        return {
            uploadUrl: process.env.VUE_APP_BASE_API + '/upload',
            headers: { Authorization: 'Bearer ' + getAccessToken() },
            imgUrl: '',
            isFailed: false
        }
    },
    watch: {
        imageUrl(val) {
            this.imgUrl = val
        }
    },
    created() {
        this.imgUrl = this.imageUrl
    },
    methods: {
        deleteImg() {
            this.imgUrl = ''
            this.$emit('update:imageUrl', this.imgUrl)
        },
        // 重新上传
        handleReUpload() {
            this.isFailed = false
        },
        handleAvatarSuccess(response, file) {
            if (response.code !== 0) {
                this.isFailed = true
            }
            this.imgUrl = response?.data?.filePath || ''
            this.$emit('update:imageUrl', this.imgUrl)
        },
        beforeAvatarUpload(file) {
            return new Promise( async (resolve, reject) => {
                const { width, height } = await this.getImageDimensions(file)
                if (this.ratio) {
                    const ratio = this.ratio.split(':')[0] / this.ratio.split(':')[1]
                    if (width / height !== ratio) {
                        this.$message.error(`上传图片宽高比必须为${this.ratio}`)
                        reject()
                    }
                }
                if (this.checkMinWidth && width < this.checkMinWidth) {
                    this.$message.error(`上传图片宽需大于${this.checkMinWidth}px`)
                    reject()
                }
                if (this.checkMinHeight && width < this.checkMinHeight) {
                    this.$message.error(`上传图片高需大于${this.checkMinHeight}px`)
                    reject()
                }
                if (!(file.size / 1024 / 1024 < this.maxFileSize)) {
                    this.$message.error(`上传图片大小不能超过 ${this.maxFileSize}MB`)
                    reject()
                }
                resolve(file)
            })
        },
        getImageDimensions(file) {
            return new Promise((resolve, reject) => {
                const reader = new FileReader()
                reader.onload = e => {
                    const img = new Image()
                    img.onload = () => {
                        resolve({ width: img.width, height: img.height })
                    }
                    img.onerror = reject
                    img.src = e.target.result
                }
                reader.onerror = reject
                reader.readAsDataURL(file)
            })
        }
    }
}
</script>
<style lang="scss" scoped>
.upload-image-box {
    display: flex;
    .avatar-uploader {
        width: 112px;
        height: 112px;
        box-sizing: border-box;
        &.is-disabled {
            ::v-deep {
                .el-upload {
                    border: none;
                }
            }
        }
        &.has-img {
            ::v-deep {
                .el-upload {
                    &:hover {
                        border-color: transparent;
                    }
                }
            }
        }
        ::v-deep {
            .el-upload {
                position: relative;
                width: 112px;
                height: 112px;
                background: #fbfaf8;
                border-radius: 4px;
                border: 1px dashed #ebedf3;
                overflow: hidden;
                cursor: pointer;
                &:hover {
                    border-color: #db9130;
                    .uploader-placeholder {
                        color: #db9130;
                    }
                    .mask {
                        display: block;
                    }
                }
                .uploader-placeholder {
                    display: flex;
                    width: 112px;
                    height: 112px;
                    justify-content: center;
                    align-items: center;
                    font-weight: 400;
                    font-size: 14px;
                    color: #8c939d;
                    line-height: 22px;
                    .el-icon-plus {
                        font-size: 16px;
                        text-align: center;
                        box-sizing: border-box;
                    }
                    .failed-mask {
                        width: 112px;
                        height: 112px;
                        background: rgba(102, 102, 102, 0.8);
                        border-radius: 4px;
                        overflow: hidden;
                        .failed-text {
                            position: absolute;
                            display: inline-block;
                            top: 50%;
                            left: 50%;
                            font-weight: 400;
                            font-size: 12px;
                            color: #ffffff;
                            line-height: 20px;
                            transform: translate(-50%, -50%);
                        }
                    }
                }

                .avatar {
                    display: block;
                    width: 112px;
                    height: 112px;
                    object-fit: contain;
                    box-sizing: border-box;
                }
            }
        }
        .mask {
            position: absolute;
            display: none;
            width: 112px;
            height: 112px;
            left: 0;
            top: 0;
            background: rgba(102, 102, 102, 0.8);
            border-radius: 4px;
            overflow: hidden;
            .ic-delete {
                position: absolute;
                display: inline-block;
                width: 14px;
                height: 16px;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: url('~@/assets/images/resourceLibrary/ic_7.png') no-repeat center center;
                background-size: 100% 100%;
            }
        }
    }
    .tip-text {
        margin-left: 12px;
        align-self: flex-end;
        font-weight: 400;
        font-size: 14px;
        color: #999999;
        line-height: 20px;
    }
}
</style>

 

posted @ 2024-12-27 17:13  hong_li  阅读(34)  评论(0)    收藏  举报