el-image图片展示弹窗组件

有时候在项目中有多处需要图片大图展示的功能,这时候一个一个独立的写有些麻烦,维护起来也很难,那么这时候就需要写成一个统一的组件,维护性和复用性高。

功能简介:点击按钮或拖动上传图片,大图展示,缩略图选中,切换页面,比例选择(等比缩放,原始大小,变比缩放),具备自适应大小。

1.预览

2.如何实现

tip:本文采用vue3+element Plus

1.模版

<template><!--该组件为编辑图片弹窗组件:可上传删除查看图片-->
    <div>
        <el-dialog v-model="imgDialogShow" ref="uploadImg" top="5vh" style="max-width:800px"  width="80%" draggable modal-class="bgcfff1"  :title="$getText('添加')+'/'+$getText('修改图片')" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="true" @close="closeUpload">
            <div class="insideBox">
                <div class="btns">
                    <el-upload
                        v-model:file-list="fileList"
                        class="upload-demo"
                        ref="uploadFolder"
                        accept="image/*"
                        :show-file-list="false"
                        list-type="picture"
                        :http-request="uploadHttpRequest"
                    >
                    <el-button icon="DocumentAdd" type="primary" :id="addImgBtnId" size="small" plain  class="upload-button" v-dk="{ dom: () => $refs.uploadImg.d_o, fn:()=>{uploadImg()}, key: 'a' }">{
  
  {$getText('添加')}}</el-button>
                    </el-upload>
                    
                    <el-button icon="Delete" type="danger" :id="delImgBtnId" size="small" plain @click="delImg" v-dk="{ dom: () => $refs.uploadImg.d_o, fn:()=>{delImg()}, key: 'd' }">{
  
  {$getText('删除')}}</el-button>
                </div>
                <div class="flexBox flex1 allImgBox">
                    <div class="flex1">
                        <el-upload
                            v-model:file-list="fileList"
                            class="uploadBigImg"
                            ref="uploadFolder"
                            accept="image/*"
                            :show-file-list="false"
                            list-type="picture"
                            :http-request="uploadHttpRequest"
                            drag
                            @click.stop="()=>{}"
                            @keydown.enter.stop="imgEnter"
                        >
                        <!-- 覆盖点击事件 -->
                        <div class="upload-overlay" @click.stop >
                            <div class="imgBox" @click.stop="changeFocus">
                                <el-image class="bigImg" :src="currentImg.url" :class="imgClass" ref="bigImg"
                                :preview-src-list="[currentImg.url]" />
                            </div>
                        </div>
                        </el-upload>
                        <div class="imgInfoBox">
                            <div class="imgInfoInput">
                                <span class="imgInfoField">{
  
  {$getText('产地')}}:</span>
                                <el-input v-model="currentImg.fa" size="small" placeholder="" disabled/>
                            </div>
                            <div class="imgInfoInput">
                                <span class="imgInfoField">{
  
  { $getText('零件说明') }}:</span>
                                <el-input v-model="currentImg.note" size="small" placeholder="" disabled/>
                            </div>
                        </div>
                    </div>
                    <div class="rightBox">
                        <el-select v-model="imgClass" @keyup.enter.stop="() => { }"
                            size="small"
                            ref="imgSelectClass"
                            class="imgSelectClass"
                            placeholder="">
                            <el-option v-for="item in imgClassOptions"
                                :key="item.label" :label="item.label" :value="item.value" />
                        </el-select>
                        <el-select v-model="imgSelect.faSelect" @keyup.enter.stop="() => { }"
                            size="small"
                            ref="faSelect"
                            placeholder="">
                            <el-option v-for="item in faOptionData"
                                :key="item.name" :label="item.name" :value="item.name" />
                        </el-select>
                        <span class="imgTotal">{
  
  { $getText('图片总数') }}:{
  
  {imgList.length}}</span>
                        <el-button icon="ArrowUpBold" type="primary" size="small" plain @click="upPage"  :disabled="this.currentPage==1">{
  
  {$getText('上一页')}}</el-button>
                            <div class="flex1 smallImgBox" ref="smallImgBox">
                                <div v-for="item in pageImgList" :key="item" class="smallImgItem" :class="{'activeImg':item.id==currentImg.id}" @click="selectSmallImg(item)">
                                    <el-image  :src="item.url" >
                                    </el-image>
                                </div>
                            </div>
                        <el-button icon="ArrowDownBold" type="primary" size="small" plain @click="downPage" :disabled="(currentPage*smallPageSize)>=imgList.length" >{
  
  {$getText('下一页')}}</el-button>
                    </div>
                </div>
                
            </div>
        </el-dialog>
    </div>
</template>

 2.js方法

<script>
export default {
    inheritAttrs:false,
    name: 'UploadImg',
    props:{
        uploadImgShow:{
            type:Boolean,
            default:false,
        },
        //以下参数根据实际情况来,非必要
        nno:{
            type:String,
            default:''
        },
        imgType:{
            type:String,
            default:''
        },
        fa:{
            type:String,
            default:''
        },
        addImgBtnId:{
            type:String,
            default:''
        },
        delImgBtnId:{
            type:String,
            default:''
        },
    },
    emits:['uploadShow','getImgList'],
    computed:{
        smallPageSize(){
            return parseInt(this.smallImgBoxHeight/75)-this.fract(this.smallImgBoxHeight/75)>0?parseInt(this.smallImgBoxHeight/75)-this.fract(this.smallImgBoxHeight/75):6;
        },
        pageImgList(){
            return this.imgList.slice((this.currentPage-1)*this.smallPageSize,this.currentPage*this.smallPageSize)
        },
    },
    data () {
        return {
            imgDialogShow:false,
            imgurl: 'https://picsum.photos/seed/picsum/400/400',//底部图片
            bigShowImg: ['https://picsum.photos/seed/picsum/400/400'],//大图展示
            imgSelect:{
                faSelect:'全部产地'
            },
            faOptionData:[
                {name:'全部产地'},
                {name:'当前产地'}
            ],
            currentImg:{
                id:0,
                url:'https://picsum.photos/seed/picsum/400/400',
                fa:'',
                note:'',
            },
            fileList:[],
            imgList:[],
            currentPage:1,
            imgBase64:'',
            imgClassOptions:[
                {label:'原始大小',value:'originalImg'},
                {label:'等比缩放',value:'containImg'},
                {label:'变比缩放',value:'fixImg'},
            ],
            imgClass:'containImg',
            smallImgBoxHeight: 0, 
            resizeListenerAdded: false, // 用于标记是否已经添加了窗口大小变化的事件监听器
        }
    },
    watch: {
        uploadImgShow(newVal) {
            if (this.imgDialogShow !== newVal) {
                this.imgDialogShow = newVal;
            }
            if(this.imgDialogShow){
                this.$nextTick(()=>{
                    this.handleResize()
                })
                this.currentPage=1
                this.getImgList()
                this.addResizeListener();
            }else{
                this.removeResizeListener();
            }
        },
        smallPageSize(newValue){
            if (newValue) this.currentPage=Math.ceil((this.imgList.findIndex(img => img.id === this.currentImg.id) + 1) / this.smallPageSize);
            if(this.currentPage<=0) this.currentPage=1;
        }

    },
    mounted(){
        this.currentImg.fa=this.$props.fa//传参自定义
    },
    methods:{
        fract(num){//边缘计算
            if(num - Math.trunc(num)>0.2)return 0;
            else return 1;
        },
        handleResize() {
            setTimeout(()=>{
                if (document.getElementsByClassName('smallImgBox')) {
                    this.smallImgBoxHeight = document.getElementsByClassName('smallImgBox')[0].clientHeight;
                }
            },500)
        },
        addResizeListener() {
            if (!this.resizeListenerAdded) {
                window.addEventListener('resize', this.handleResize);
                this.resizeListenerAdded = true;
            }
        },
    
        removeResizeListener() {
            if (this.resizeListenerAdded) {
                window.removeEventListener('resize', this.handleResize);
                this.resizeListenerAdded = false;
            }
        },
        changeFocus(){
            document.getElementsByClassName("uploadBigImg")[0].children[0].blur();
        },
        imgEnter(e){
            console.log(e)
            //阻止默认事件
            e.preventDefault();
            //阻止事件冒泡
            e.stopPropagation();
            return;
        },
        uploadHttpRequest(data){
            this.toBase64(data.file)
        },
        suofang(base64,callback) {//图片压缩函数
            //处理缩放,转格式
            var _img = new Image();
            _img.src = base64;
            _img.onload = function () {
                var quality=1;
                var _canvas = document.createElement("canvas");
                var w = this.width / 1.5;
                var h = this.height / 1.5;
                _canvas.setAttribute("width", w);
                _canvas.setAttribute("height", h);
                _canvas.getContext("2d").drawImage(this, 0, 0, w, h);
                var base64 = _canvas.toDataURL("image/jpeg",quality);
                while(base64.length>1024*1024*3){
                    quality-=0.05;
                    base64=_canvas.toDataURL("image/jpeg",quality);
                }
                base64=base64.substring(base64.indexOf (',')+1)
                callback(base64);//将压缩后的图片传入最终回调函数中执行
            }
        },
        toBase64(file){//转base64
            let base64=''
            const reader = new FileReader();  
            reader.readAsDataURL(file); 
            reader.onload = (e) => {  
                // 转换完成,获取Base64编码  
                this.imgBase64 = e.target.result;
                if(this.imgBase64){
                    this.suofang(this.imgBase64,this.getImgList)
                }  
                // 这里可以根据需要对base64进行处理,比如存储到Vuex、Data等 
                return base64 
            }
        },
        getImgList(base64=''){
            console.log(base64?'上传':'获取')
            //此处为获取或上传图片,获取到的数据赋值到this.imgList
        },
        selectSmallImg(data){
            this.currentImg=data;
        },
        upPage(){
            if(this.currentPage==1) return;
            this.currentPage--;
        },
        downPage(){
            if(this.currentPage*this.smallPageSize>=this.imgList.length) return;
            this.currentPage++;
        },
        closeUpload(status){
            this.$emit('uploadShow',false)
            this.$emit('getImgList',this.imgList)
        },
        uploadImg(){
            this.$refs.uploadFolder.$el.querySelector('.upload-button').click();
        },
        delImg(){

        }
    },
}
</script>

3.css样式

<style scoped>
.insideBox{
    display: flex;
    flex-direction: column;
    height: 100%;
}

.btns {
    padding: 0px 8px;
    box-sizing: border-box;
    display: flex;
    height: 32px;
    gap: 8px;
}
.imgBox{
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.flexBox{
    display:flex;
}
.flex1{
    flex:1;
}
.rightBox{
    display: flex;
    flex-direction: column;
    padding: 0 10px;
    width:100px;
    max-width: 100px;
    overflow: hidden;
    
}

.smallImgBox{
    display: flex;
    gap: 4px;
    flex-direction: column;
    height: 470px;
    overflow: hidden;
    padding: 4px 0px;
}
.smallImgItem{
    width: 100%;
    height: 75px;
    min-height:75px;
    overflow: hidden;
    box-sizing: border-box;
    border: 1px solid #b1b0b0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.imgInfoBox{
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-top: 10px;
}
.imgInfoField{
    min-width:5rem;
    white-space:nowrap;
}
.imgInfoInput{
    display:flex;
    white-space:nowrap;
}
.activeImg{
    border: 2px solid #409EFF;
}
.imgTotal{
    white-space: nowrap;
}
:deep(.el-image__placeholder) {
    background: url('../../../public/image/loading.gif') no-repeat 50% 50% !important;
    background-size: 25px !important;
}
:deep(.uploadBigImg .el-upload-dragger){
    border:none;
    padding:0px;
    height:100%;
    border-radius: 0px;
}

.upload-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1; /* 确保覆盖在内容之上 */
    pointer-events: auto; /* 默认情况下,这个值可能是 none,但我们需要设置为 auto 来接收点击事件,然后通过 @click.stop 来阻止它冒泡 */
    /* 可以添加透明背景或其他样式来避免影响用户体验 */
    background-color: rgba(0, 0, 0, 0); /* 透明背景 */
  }
.uploadBigImg{
    width: 100%;
    height:calc(100% - 64px);
    border: 1px dashed lightgray;
}
:deep(.uploadBigImg .el-upload){
    height:100%;
}

.originalImg{
    position: relative;
    width: 100%;
    height: 100%;
}

:deep(.originalImg img){
    position: absolute;
    width:auto;
    height:auto;
    left:0px;
    top:0px;
}
.containImg{
    max-width: 100%;
    max-height: 100%;
    height: 100%;
    display: flex;
    align-items: center;
}
:deep(.containImg img){
    object-fit: contain;
    max-height: 100%;
    height: auto !important;
    max-width: 100%;
    width: auto !important;
}
.fixImg{
    width:100%;
    height: 100%;
}
:deep(.fixImg img){
    width:100%;
    height: 100%;
}
:deep(.el-image-viewer__canvas img){
    position: relative;
}
:deep(.smallImgItem .el-image img){
    max-height:75px;
    object-fit: contain;
}
.allImgBox{
    height:calc(100% - 24px)
}
.imgSelectClass{
    margin-bottom: 6px;
}
</style>

3.如何使用

1.在父组件中,引入子组件(在scrpit中使用import UploadImg from  '子组件文件位置')

2.在模版标签(template)中使用(带:的为参数,可自定义,根据实际情况来)

<UploadImg v-model:uploadImgShow="uploadImgShow" :nno="''" :imgType="''" :fa="''" @uploadShow="closeUploadImgShow" @getImgList="getImgList"></UploadImg>

 3.创建新变量uploadImgShow,默认值为false,控制弹窗显示

 4.最后在父组件中添加方法:

getImgList(data){
    console.log(data);//获取子组件返回的图片数据
},
closeUploadImgShow(status){
    this.uploadImgShow=status
},
imgUpload() {//上传图片
   console.log('上传')
   this.uploadImgShow = true
},

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值