1 弹窗左右切换图片自动切换,底下缩略图自动切换
2 点击底下缩略图展示中间大图
3 点击icon图标区域右侧的折叠按钮底下缩略图,再次点击显示
4 弹窗由于是在外部缩略图点击以后打开,所以每次打开的时候弹窗缩略图都会定位到当前图片的位置
我们采用teleport+element-plusUI来实现这个效果 先看基础代码:
//父组件
<div v-if="isViewBigPic">
<RenderImgDialog
:data="curImgData"
:planId="planId"
:index="curImgIndx"
:imgList="curImgList"
@close="isViewBigPic = false"
@del="delImgByDialog"
/>
</div>
const isViewBigPic = ref<boolean>(false);
const curImgIndx = ref<number>(0);
const curImgList = computed(() => {
return list.value.filter(k => k.stateCode == 100);
});
const handlerViewBigPic = data => {
console.log("查看大图数据", data);
isViewBigPic.value = true;
curImgData.value = data;
let tempIndex = 0;
curImgList.value.forEach((item, index): void => {
if (item.id == data.id) {
tempIndex = index;
}
});
curImgIndx.value = tempIndex;
};
为什么要通过v-if,是因为想每次打开的时候都能走onMounted()逻辑,所以每次关闭都让其销毁。再看弹窗核心内容
<teleport to="#app">
<!-- <transition name="el-fade-in-linear"> -->
<div>
<div class="mask"></div>
<div class="modal" v-if="imgList&&imgList?.length > 0">
<div class="container">
<div class="modal-head">
<!-- <div>{{data}}</div> -->
<div class="modal-title">{{ curData && curData?.name }}</div>
<div class="close-icon" @click="close">
<el-icon><CloseBold /></el-icon>
</div>
</div>
<div class="fullscreen" :style="{ backgroundImage: `url( ${curSrc})` }" v-if="isFold">
<div :class="[idx == 0 ? 'no-hover' : '', 'full-lt-arrow']" @click="prev">
<div class="btn">
<el-icon><ArrowLeftBold /></el-icon>
</div>
</div>
<div class="full-content">
<!-- <el-image :src="curSrc" style="border-radius: 4px;"></el-image> -->
</div>
<div :class="[idx == imgList?.length - 1 ? 'no-hover' : '', 'full-rt-arrow']" @click="next">
<div class="btn">
<el-icon><ArrowRightBold /></el-icon>
</div>
</div>
</div>
<div class="content" v-else>
<div class="lt-arrow" @click="prev">
<div :class="[idx == 0 ? 'no-hover' : '', 'lt-arrow-box']">
<el-icon><ArrowLeftBold /></el-icon>
</div>
</div>
<div class="mid-imgWrap" ref="mainImgRef">
<el-image :src="curSrc" fit="contain" style="width: 812px; height: 534px; border-radius: 4px">
<template #placeholder>
<div class="image-slot">
<el-icon><Loading /></el-icon>
<!-- <el-progress :percentage="30" /> -->
</div>
</template>
</el-image>
</div>
<div class="rt-arrow" @click="next">
<div :class="[idx == imgList?.length - 1 ? 'no-hover' : '', 'rt-arrow-box']" @mouseover="move" @mouseleave="leave">
<el-icon><ArrowRightBold /></el-icon>
</div>
</div>
</div>
<div class="desc-wrap">
<span>
<el-tooltip placement="top" effect="light" >
<template #content>
<h2 class="tip-title">图片信息</h2>
<div class="block-wrap">
<div class="block-item">
<div class="label">图片类型</div>
<div class="text">{{ parseImgType(curData && curData?.imageType) }}</div>
</div>
<div class="block-item">
<div class="label">分辨率</div>
<div class="text"></div>
</div>
<div class="block-item">
<div class="label">空间</div>
<div class="text" v-if="curData&&curData?.combinationName">
<span class="space-item" >{{curData&&curData?.combinationName}}</span>
</div>
<div class="text" v-else="curData&&curData?.name">{{curData?.name}}</div>
</div>
<div class="block-item">
<div class="label">创建时间</div>
<div class="text" >{{ curData && curData?.createTime }}</div>
</div>
<div class="block-item" v-show="curData?.imageType == 2 || curData?.imageType == 3">
<div class="label">包含商品</div>
<div class="text">{{ curData && curData?.goodsHotspotNum }}</div>
</div>
<div class="block-item" v-show="curData.imageType == 2 || curData.imageType == 3">
<div class="label">文本热点</div>
<div class="text">{{ curData && curData?.textHotspotNum }}</div>
</div>
<div class="block-item" v-show="curData?.imageType == 2 || curData.imageType == 3">
<div class="label">有效期</div>
<div class="text">{{ curData && curData?.expireTime }}</div>
</div>
</div>
</template>
<el-icon class="item-icon"><InfoFilled /></el-icon>
</el-tooltip>
<el-tooltip
placement="top-start"
effect="light"
>
<template #content>
<span class="white" >查看</span>
</template>
<span>
<el-icon class="item-icon"
v-show="(curData && curData?.imageType == 2) || (curData && curData?.imageType == 3)" @click="viewHandler"><FullScreen />
</el-icon>
</span>
</el-tooltip>
<span class="share-wrap">
<el-tooltip
popper-class="pop-wrap"
placement="top-start"
effect="light"
>
<template #content>
<div class="share-content">
<div class="qr-img">
<img :src="curData && curData?.qrCodeUrl" />
</div>
<div class="share-btns-wrap">
<span class="share-btn" @click="shareByLink">链接分享</span>
<span class="share-btn" @click="shareByMessage">短信分享</span>
</div>
</div>
</template>
<span>
<el-icon class="item-icon"
v-show="(curData && curData?.imageType == 2) || (curData && curData?.imageType == 3)"><Share />
</el-icon>
</span>
</el-tooltip>
</span>
<el-tooltip placement="top-start" effect="light">
<template #content><span class="white">删除</span></template>
<span @click="del"
><el-icon class="item-icon"><Delete /></el-icon
></span>
</el-tooltip>
<el-tooltip placement="top-start" effect="light">
<template #content><span class="white">下载</span></template>
<span @click="downLoad"><el-icon class="item-icon"><Download /></el-icon></span>
</el-tooltip>
<el-tooltip
content="设置"
placement="top-start"
effect="light"
>
<template #content><span class="white">设置</span></template>
<span @click="setHandler">
<el-icon class="item-icon"
v-show="(curData && curData?.imageType == 2) || (curData && curData?.imageType == 3)"> <Setting />
</el-icon>
</span>
</el-tooltip>
</span>
<!-- </slot> -->
<span class="line">|</span>
<span @click="toggleThumb" class="fold-span">
<!-- <el-icon :class="isFold ? 'arrow-down' : 'arrow-up'" style="color:#fff"><ArrowDownBold /></el-icon> -->
<img class="arrow-fold-img" :src="isFold ? upIcon : downIcon" />
<span class="fold">{{ isFold ? "展开收缩图" : "收起缩略图" }}</span>
</span>
</div>
<el-scrollbar wrap-style="margin-left:20px;" ref="refScrollbar" >
<div class="thumn-inner-wrap" :style="{ height: isFold ? '0px': '' , width : imgList?.length*204+'px'}">
<div
@click="handlerThumb(item)"
:class="[{ active: k == idx }, 'thumb-img']"
v-for="(item, k) in imgList"
:key="item.id"
>
<img :src="item?.image" class="imgObj" />
</div>
</div>
</el-scrollbar>
</div>
</div>
<div class="empty" v-else>
<div class="empty-title">
<div class="close-icon empty-icon-close" @click="close">
<el-icon><CloseBold /></el-icon>
</div>
</div>
<div class="empty-content">
<el-empty
:image-size="56"
image="https://baj-dabanjiz-conf.oss-cn-hangzhou.aliyuncs.com/promotion/logo/empty.png"
description="请重新选择渲染图查看"
/>
</div>
</div>
</div>
import upIcon from "@/assets/images/up.png";
import downIcon from "@/assets/images/down.png";
import { getPanoToken } from "@/api/modules/renderImg";
interface RenderProsp {
// isShow?: boolean;
index?: number;
planId: string | number;
imgList?: any[];
data?: any;
}
const props = withDefaults(defineProps<RenderProsp>(), {
index: 0,
planId: "",
data: {},
imgList: () => []
});
const emits = defineEmits(["close", "del"]);
const idx = ref<number>(0);
const isFold = ref<boolean>(false);
const scrollDis = ref<number>(0);
// const isShowLinkShare = ref<boolean>(false);
let msgShareRef = ref<InstanceType<typeof MsgShareDialog> | null>(null);
let linkShareRef = ref<InstanceType<typeof LinkShareRef> | null>(null);
const instance = getCurrentInstance() as any;
const curSrc = computed(() => {
let val = props.imgList[idx.value]?.image ?? "";
// console.log("props.imgList:", props);
return val;
});
//当前的数据
const curData = computed(() => {
let data = {};
if (props.imgList.length == 1) {
idx.value = props.index;
data = props.imgList[idx.value];
} else if (props.imgList.length > 1) {
data = props.imgList[idx.value];
}
console.log("curData:", data);
return data;
});
const refScrollbar = ref<any>(null);
const mainImgRef = ref<any>(null);
onMounted(() => {
idx.value = props.index;
if(idx.value == props.imgList.length-1){
console.log("最后一个")
let space= (props.imgList.length-1)*20
scrollDis.value = props.imgList.length * (200 + space) ;
}else{
let marginSpaces=(idx.value-1)*20
scrollDis.value = props.index * 200 - marginSpaces ;
}
nextTick(() => {
refScrollbar?.value?.setScrollLeft?.(scrollDis.value);
mainImgRef?.value?.addEventListener("selectstart", () => {
return false;
});
});
});
watch(
() => props.index,
n => {
console.log("n---:", n);
idx.value = n;
if(idx.value==props.imgList?.length-1){
let space= (props.imgList?.length-1)*20
scrollDis.value = props.imgList.length * (200 + space);
}else{
scrollDis.value = n * 200;
}
console.log("wathc----idx:", idx);
setTimeout(()=>{
refScrollbar?.value?.setScrollLeft?.(scrollDis.value);
},0)
}
);
const next = (): void => {
let imgMaxIndex=props?.imgList?.length - 1
if (idx.value >= imgMaxIndex) {
let space=(props?.imgList?.length-1)*20
idx.value = props?.imgList?.length - 1
scrollDis.value = 200 * (imgMaxIndex)+space
return;
} else {
let curSpaces=(idx.value-1)*20
idx.value++;
scrollDis.value = (200*idx.value)-curSpaces;
}
setTimeout(()=>{
refScrollbar?.value?.setScrollLeft?.(scrollDis.value);
},0)
}
const prev = (): void => {
let space=(idx.value-1)*20;
let imgMaxIndex=props?.imgList?.length - 1
if (idx.value <= 0) {
scrollDis.value = 0;
nextTick(() => {
refScrollbar?.value?.setScrollLeft(0);
});
return;
} else {
idx.value--;
scrollDis.value = (idx.value*200)-space
console.log("idx.value:",idx.value)
console.log("scrollDis.value:",scrollDis.value)
setTimeout(()=>{
refScrollbar.value?.setScrollLeft?.(scrollDis.value);
},0)
}
}
const parseImgType = (type: string | number): void => {
// imgType :1 效果图 2 全景图 3 全屋漫游
switch (type) {
case 1:
return "效果图";
case 2:
return "全景图";
case 3:
return "全屋漫游";
default:
return "效果图";
}
};
const handlerThumb = item => {
let index = props.imgList.findIndex(k => k.id == item.id);
console.log("index:", index);
idx.value = index;
let marginSpace=(idx.value-1)*20
scrollDis.value = 204 * index - marginSpace;
if (refScrollbar?.value) {
refScrollbar?.value?.setScrollLeft(scrollDis.value);
}
};
const close = () => {
emits("close", false);
// emit("update:isShow", false);
};
const toggleThumb = (): void => {
isFold.value = !isFold.value;
};
这里代码有点多。其实就是2个核心逻辑,点击底下折叠icon,折叠的时候显示A模板,打开的时候显示B模板,通过v-if做判断,而图片的定位通过索引值去定位,这样通过缩略图点击打开弹窗的时候,就知道当前这张图片在imgList中的位置。然后把索引交给当前弹窗中的一个值idx,左右切换的逻辑通过更改idx的值,切换图片。 每次点击缩略图打开弹窗的时候,监听下当前的索引值。然后模板中的数据都取自curData,而curData则是通过idx的值获取到的,这样能保证每次idx的值变化,curData就自动更新。最后说一说底下缩略图滚动的位置问题, 缩略图位置滚动是通过element-plus的el-scroll组件的setScrollLeft来实现的。这里特别要注意的由于设计稿中每个缩略图中间的margin是10,所以这里每次调用setScrollLeft的时候都计算了下中间的间隙。否则会出现缩略图定位半截的情况, 最后一点要注意的是:这里折叠的问题。之前我是通过添加class: 通过设置display:none/block来实现。但是发现在某些其他环境中会出现InsertBefore的报错问题,(因为这个项目是web嵌套到qt环境中)。所以最后采用设置高度来解决,也就是在缩略图折叠的把缩略图的整体告诉设置为0