Files
RentWeAppFront/components/gallery/AssetGallery.vue
2025-12-25 08:26:09 +08:00

296 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="house-gallery">
<!-- 图片轮播 -->
<u-swiper
:list="swiperList"
:current="current"
height="1200rpx"
>
<template #default="{ item, index }">
<view class="slide-item">
<image :src="item.url" mode="aspectFill" class="slide-img" />
<!-- VR 按钮 -->
<view v-if="item.type === 'vr'" class="vr-btn" @click.stop="openVR">
VR
</view>
<!-- 视频按钮 -->
<view v-if="item.type === 'video'" class="video-btn" @click.stop="playVideo">
</view>
<!-- 图片索引 -->
<view class="slide-index">{{ index + 1 }}/{{ swiperList.length }}</view>
</view>
</template>
</u-swiper>
<!-- 广告遮罩栏 -->
<view v-if="adVisible && adImage" class="ad-mask" @click="onAdClick">
<image :src="adImage" mode="aspectFill" class="ad-img" />
<view class="ad-close" @click.stop="adVisible = false">×</view>
</view>
<!-- 底部 Tab -->
<view class="gallery-tab">
<view
class="gallery-tab-item"
v-for="(item, i) in tabs"
:key="i"
:id="'tab-' + i"
:class="{ active: activeTab === i }"
@click="onTabClick(i)"
>
{{ item.label }}
<text v-if="item.count">({{ item.count }})</text>
</view>
<!-- 滑块动画 -->
<view
class="tab-line"
:style="{ width: lineStyle.width + 'px', left: lineStyle.left + 'px' }"
></view>
</view>
<!-- 视频弹窗 -->
<u-popup v-model="videoVisible" mode="center" border-radius="20">
<video
:src="videoUrl"
autoplay
controls
style="width: 600rpx; height: 400rpx;"
></video>
</u-popup>
</view>
</template>
<script>
export default {
name: "AssetGallery",
props: {
vrImage: String,
videoCover: String,
videoUrl: String,
insideImages: Array,
planImages: Array,
adImage: String
},
data() {
return {
current: 0,
videoVisible: false,
adVisible: true,
tabs: [],
indexMap: [],
lineStyle: { width: 0, left: 0 }
};
},
mounted() {
this.$nextTick(() => {
setTimeout(() => this.updateTabLine(), 50);
});
},
watch: {
activeTab() {
this.$nextTick(() => this.updateTabLine());
}
},
computed: {
swiperList() {
let list = [];
this.tabs = [];
this.indexMap = [];
let idx = 0;
if (this.vrImage) {
this.tabs.push({ key: "vr", label: "VR", count: 1 });
this.indexMap.push(idx);
list.push({ type: "vr", url: this.vrImage });
idx++;
}
if (this.videoCover) {
this.tabs.push({ key: "video", label: "视频", count: 1 });
this.indexMap.push(idx);
list.push({ type: "video", url: this.videoCover });
idx++;
}
if (this.insideImages?.length > 0) {
this.tabs.push({ key: "inside", label: "内部图", count: this.insideImages.length });
this.indexMap.push(idx);
list.push(...this.insideImages.map((img) => ({ type: "inside", img })));
idx += this.insideImages.length;
}
if (this.planImages?.length > 0) {
this.tabs.push({ key: "plan", label: "平面图", count: this.planImages.length });
this.indexMap.push(idx);
list.push(...this.planImages.map((url) => ({ type: "plan", url })));
idx += this.planImages.length;
}
return list;
},
activeTab() {
for (let i = 0; i < this.indexMap.length; i++) {
const start = this.indexMap[i];
const end = this.indexMap[i + 1] ?? 99999;
if (this.current >= start && this.current < end) return i;
}
return 0;
}
},
methods: {
onAdClick() {
this.$emit("ad-click");
},
onSwiperChange(e) {
this.current = e.detail.current;
},
onTabClick(i) {
this.current = this.indexMap[i];
},
openVR() {
this.$emit("open-vr");
},
playVideo() {
this.videoVisible = true;
},
updateTabLine() {
const index = this.activeTab;
if (this.tabs.length === 0) return;
const id = `#tab-${index}`;
uni.createSelectorQuery()
.in(this)
.select(id)
.boundingClientRect((rect) => {
if (!rect) return;
this.lineStyle = { width: rect.width, left: rect.left };
})
.exec();
}
}
};
</script>
<style scoped>
.house-gallery {
width: 100%;
position: relative;
}
/* 轮播 */
.slide-item {
width: 100%;
height: 1200rpx;
position: relative;
}
.slide-img {
width: 100%;
height: 100%;
border-radius: 0;
}
.vr-btn,
.video-btn {
position: absolute;
bottom: 40rpx;
right: 40rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.6);
color: #fff;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
}
.video-btn {
font-size: 50rpx;
}
.slide-index {
position: absolute;
bottom: 20rpx;
right: 20rpx;
padding: 4rpx 8rpx;
background: rgba(0,0,0,0.5);
color: #fff;
font-size: 24rpx;
border-radius: 10rpx;
}
/* 广告遮罩 */
.ad-mask {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 120rpx;
background: rgba(0,0,0,0.35);
padding: 0 20rpx;
z-index: 9;
}
.ad-img {
width: 100%;
height: 100%;
border-radius: 12rpx;
}
.ad-close {
position: absolute;
right: 20rpx;
top: 20rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: rgba(0,0,0,0.55);
color: #fff;
text-align: center;
line-height: 40rpx;
font-size: 30rpx;
}
/* Tab bar */
.gallery-tab {
position: absolute;
bottom: 0;
width: 100%;
display: flex;
background: rgba(255,255,255,0.9);
border-top: 1rpx solid #eee;
height: 90rpx;
align-items: center;
justify-content: space-around;
z-index: 10;
}
.gallery-tab-item {
padding: 20rpx 0;
font-size: 28rpx;
color: #666;
}
.gallery-tab-item.active {
color: #2979ff;
font-weight: bold;
}
.tab-line {
position: absolute;
bottom: 0;
height: 4rpx;
background: #2979ff;
transition: all 0.25s ease;
}
</style>