600 lines
14 KiB
Vue
600 lines
14 KiB
Vue
<template>
|
||
<uni-popup ref="popup" type="center" maskBackgroundColor="rgba(0,0,0,0.9)">
|
||
<view v-if="visible" class="preview-popup" @touchmove.stop.prevent="moveHandle">
|
||
<!-- 翻转卡片容器 -->
|
||
<view class="flip-container" :class="{ 'flipped': showList }">
|
||
<view class="flipper">
|
||
<!-- 正面:商品图片 -->
|
||
<view class="front">
|
||
<view class="pic center relative" @click="toggleList">
|
||
<image :src="innerImgUrl" lazy-load mode="aspectFill"></image>
|
||
<view class="type-tag justify-center"
|
||
:style="{ backgroundColor: dataItem.shang_info ? dataItem.shang_info.color : '#000000' }"
|
||
v-if="innerTipTitle">{{ innerTipTitle }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 背面:赏品列表 -->
|
||
<view class="back">
|
||
<view class="listt">
|
||
<scroll-view class="list" :scroll-y="true">
|
||
<!-- 加载中状态 -->
|
||
<view class="loading-container center" v-if="loading">
|
||
<view class="loading-icon">
|
||
<image :src="$img('/static/img/loading.gif')" mode="aspectFit" v-if="hasLoadingGif"></image>
|
||
<view class="loading-dot" v-else></view>
|
||
</view>
|
||
<view class="loading-text">加载中...</view>
|
||
</view>
|
||
<!-- 空列表状态 -->
|
||
<view class="empty-container center" v-else-if="children.length === 0 && !loading">
|
||
<view class="empty-text">暂无商品</view>
|
||
</view>
|
||
<!-- 赏品列表内容 -->
|
||
<view class="res-list" v-else>
|
||
<detail-list-item v-for="(item, i) in children" :key="item.id || i" :item="item" imageHeight="180rpx"
|
||
@click="handleItemClick" />
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 商品标题 -->
|
||
<view class="title" @click="toggleList">
|
||
<text class="hang1">{{ !showList ? innerTitle : '抽中宝箱后,会在奖品列表中随机抽取一个奖品' }}</text>
|
||
</view>
|
||
|
||
<!-- 商品信息列表 -->
|
||
<view class="info-list">
|
||
<view class="info-item" v-if="innerProbability">{{ innerProbability }}</view>
|
||
<view class="info-item highlight" v-if="isTips" @click="toggleList">
|
||
{{ showList ? '收起' : '查看奖品' }}
|
||
<!-- <text class="arrow-icon">{{ showList ? '↑' : '↓' }}</text> -->
|
||
</view>
|
||
<view class="info-item" v-if="innerProductType">产品类型: {{ innerProductType }}</view>
|
||
<view class="info-item" v-for="(item, index) in innerExtraInfo" :key="'info-' + index">{{ item }}</view>
|
||
</view>
|
||
|
||
<!-- 关闭按钮 -->
|
||
<view class="close icon" @click="closePopup">
|
||
<image :src="$img('/static/img/close.png')" lazy-load></image>
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
</template>
|
||
|
||
<script>
|
||
import DetailListItem from '@/components/detail-list-item/detail-list-item.vue';
|
||
|
||
export default {
|
||
name: 'DetailPreviewPopup',
|
||
|
||
components: {
|
||
DetailListItem
|
||
},
|
||
|
||
props: {
|
||
// 商品标题
|
||
title: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 商品图片地址
|
||
imgUrl: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 商品提示标签标题
|
||
tipTitle: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 商品类型
|
||
productType: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 商品概率
|
||
probability: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
// 额外信息(可选)
|
||
extraInfo: {
|
||
type: Array,
|
||
default: () => []
|
||
}
|
||
},
|
||
|
||
data() {
|
||
return {
|
||
visible: false,
|
||
// 内部数据,用于显示
|
||
innerTitle: '',
|
||
innerImgUrl: '',
|
||
innerTipTitle: '',
|
||
innerProductType: '',
|
||
innerProbability: '',
|
||
innerExtraInfo: [],
|
||
dataItem: {},
|
||
showList: false,
|
||
isTips: false,
|
||
children: [],
|
||
goods: null,
|
||
loading: false,
|
||
hasLoadingGif: false,
|
||
loadError: false
|
||
}
|
||
},
|
||
|
||
watch: {
|
||
// 监听props变化,更新内部数据
|
||
title(val) {
|
||
this.innerTitle = val;
|
||
},
|
||
imgUrl(val) {
|
||
this.innerImgUrl = val;
|
||
},
|
||
tipTitle(val) {
|
||
this.innerTipTitle = val;
|
||
},
|
||
productType(val) {
|
||
this.innerProductType = val;
|
||
},
|
||
probability(val) {
|
||
this.innerProbability = val;
|
||
},
|
||
extraInfo(val) {
|
||
this.innerExtraInfo = val;
|
||
}
|
||
},
|
||
|
||
created() {
|
||
// 初始化内部数据
|
||
this.innerTitle = this.title;
|
||
this.innerImgUrl = this.imgUrl;
|
||
this.innerTipTitle = this.tipTitle;
|
||
this.innerProductType = this.productType;
|
||
this.innerProbability = this.probability;
|
||
this.innerExtraInfo = this.extraInfo;
|
||
|
||
// 检查是否有loading图片资源
|
||
this.checkLoadingResource();
|
||
|
||
// 监听全局预览事件
|
||
uni.$on('global-preview-event', this.handleGlobalEvent);
|
||
},
|
||
|
||
beforeDestroy() {
|
||
// 组件销毁前移除事件监听
|
||
uni.$off('global-preview-event', this.handleGlobalEvent);
|
||
},
|
||
|
||
methods: {
|
||
moveHandle(e) {
|
||
return true;
|
||
},
|
||
// 检查loading图片资源是否存在
|
||
checkLoadingResource() {
|
||
try {
|
||
const loadingPath = this.$img('/static/img/loading.gif');
|
||
uni.getImageInfo({
|
||
src: loadingPath,
|
||
success: () => {
|
||
this.hasLoadingGif = true;
|
||
},
|
||
fail: () => {
|
||
this.hasLoadingGif = false;
|
||
}
|
||
});
|
||
} catch (e) {
|
||
this.hasLoadingGif = false;
|
||
}
|
||
},
|
||
|
||
// 处理列表项点击
|
||
handleItemClick(item) {
|
||
console.log('点击了', item);
|
||
this.$emit('itemClick', item);
|
||
},
|
||
|
||
// 处理全局预览事件
|
||
handleGlobalEvent(options) {
|
||
console.log('收到全局预览事件', options);
|
||
this.setPreviewData(options);
|
||
},
|
||
|
||
// 切换列表显示状态,添加动画效果
|
||
toggleList() {
|
||
if (this.isTips) {
|
||
if (this.children.length === 0 && !this.loading && !this.loadError) {
|
||
this.loadChildren();
|
||
}
|
||
this.showList = !this.showList;
|
||
}
|
||
},
|
||
|
||
// 设置预览数据并打开弹窗
|
||
setPreviewData(data) {
|
||
if (!data) return;
|
||
|
||
// 重置数据状态
|
||
this.loadError = false;
|
||
this.showList = false;
|
||
this.isTips = false;
|
||
this.dataItem = data.dataItem;
|
||
// 更新商品基本信息
|
||
if (data.dataItem) {
|
||
console.log(data.dataItem.shang_info.color);
|
||
|
||
const item = data.dataItem;
|
||
this.innerTitle = item.title || '';
|
||
this.innerImgUrl = item.imgurl || '';
|
||
this.innerTipTitle = item.shang_info ? item.shang_info.title : '';
|
||
this.innerProbability = item.pro || '';
|
||
}
|
||
|
||
// 更新其他信息
|
||
if (data.productType) this.innerProductType = data.productType;
|
||
if (data.extraInfo) this.innerExtraInfo = data.extraInfo;
|
||
console.log('data.goods', data.goods, data.dataItem);
|
||
|
||
// 设置商品和数据项
|
||
if (data.dataItem && data.goods && data.dataItem.goods_type == 4) {
|
||
this.goods = data.goods;
|
||
|
||
this.isTips = true;
|
||
this.children = [];
|
||
}
|
||
|
||
// 打开弹窗
|
||
this.open();
|
||
},
|
||
|
||
// 打开弹窗
|
||
open() {
|
||
this.visible = true;
|
||
|
||
this.$nextTick(() => {
|
||
if (this.$refs.popup) {
|
||
// 小程序环境下需要额外延迟
|
||
setTimeout(() => {
|
||
this.$refs.popup.open();
|
||
}, 50);
|
||
} else {
|
||
console.error('popup ref不存在,请检查组件是否正确挂载');
|
||
// 延迟后再次尝试
|
||
setTimeout(() => {
|
||
if (this.$refs.popup) {
|
||
this.$refs.popup.open();
|
||
}
|
||
}, 300);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 关闭弹窗
|
||
closePopup() {
|
||
this.visible = false;
|
||
if (this.$refs.popup) {
|
||
this.$refs.popup.close();
|
||
}
|
||
this.$emit('close');
|
||
},
|
||
|
||
// 加载子商品数据
|
||
async loadChildren() {
|
||
if (this.loading) return;
|
||
|
||
this.loading = true;
|
||
this.loadError = false;
|
||
|
||
try {
|
||
const res = await this.req({
|
||
url: "goods_children",
|
||
method: "POST",
|
||
Loading: true, // 已经有自己的loading状态,不需要全局loading
|
||
data: {
|
||
goods_id: this.goods.id,
|
||
goods_num: this.goods.num,
|
||
goods_list_id: this.dataItem.id
|
||
}
|
||
});
|
||
|
||
if (res && res.status === 1) {
|
||
this.children = res.data || [];
|
||
} else {
|
||
this.loadError = true;
|
||
this.children = [];
|
||
}
|
||
} catch (error) {
|
||
console.error('加载数据出错', error);
|
||
this.loadError = true;
|
||
this.children = [];
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.preview-popup {
|
||
position: relative;
|
||
width: 80vw;
|
||
box-sizing: border-box;
|
||
height: 850rpx;
|
||
|
||
// 翻转卡片相关样式
|
||
.flip-container {
|
||
perspective: 1000px;
|
||
width: 100%;
|
||
height: 620rpx;
|
||
|
||
&.flipped .flipper {
|
||
transform: rotateY(180deg);
|
||
}
|
||
|
||
.flipper {
|
||
transition: 0.6s;
|
||
transform-style: preserve-3d;
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.front,
|
||
.back {
|
||
backface-visibility: hidden;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.front {
|
||
z-index: 2;
|
||
transform: rotateY(0deg);
|
||
}
|
||
|
||
.back {
|
||
transform: rotateY(180deg);
|
||
}
|
||
}
|
||
|
||
.listt {
|
||
width: 100%;
|
||
padding-top: 5rpx;
|
||
height: 620rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
margin: 0rpx auto;
|
||
}
|
||
|
||
.list {
|
||
width: 100%;
|
||
height: 610rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
margin: 0rpx auto;
|
||
|
||
.res-list {
|
||
padding: 5rpx 15rpx;
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 33%);
|
||
gap: 20rpx 8rpx;
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
// 商品图片区域
|
||
.pic {
|
||
width: 100%;
|
||
height: 620rpx;
|
||
background: #FFFFFF;
|
||
border-radius: 16rpx;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
// 商品标签
|
||
.type-tag {
|
||
width: 100rpx;
|
||
height: 40rpx;
|
||
position: absolute;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
top: 12rpx;
|
||
left: 24rpx;
|
||
font-weight: 400;
|
||
line-height: 40rpx;
|
||
font-size: 20rpx;
|
||
color: #FFFFFF;
|
||
box-sizing: border-box;
|
||
border-radius: 25rpx;
|
||
}
|
||
}
|
||
|
||
// 商品标题
|
||
.title {
|
||
margin: 40rpx auto 0;
|
||
width: 100%;
|
||
height: 60rpx;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20rpx;
|
||
border-radius: 12rpx;
|
||
background: #F5F5F5;
|
||
|
||
text {
|
||
font-weight: 600;
|
||
font-size: 24rpx;
|
||
color: #333333;
|
||
text-align: center;
|
||
line-height: 1.4;
|
||
}
|
||
}
|
||
|
||
// 商品信息列表
|
||
.info-list {
|
||
display: flex;
|
||
flex-flow: row wrap;
|
||
justify-content: space-between;
|
||
padding: 20rpx 0 0;
|
||
|
||
.info-item {
|
||
margin-top: 14rpx;
|
||
font-size: 24rpx;
|
||
font-family: Source Han Sans CN;
|
||
font-weight: 400;
|
||
color: #ffffff;
|
||
cursor: pointer;
|
||
|
||
&.highlight {
|
||
color: #f39205;
|
||
font-weight: 500;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.arrow-icon {
|
||
margin-left: 4rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 关闭按钮
|
||
.close {
|
||
width: 100%;
|
||
margin-top: 40rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
image {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
}
|
||
}
|
||
|
||
.tag {
|
||
position: absolute;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
top: 5%;
|
||
left: 5%;
|
||
font-weight: 400;
|
||
font-size: 20rpx;
|
||
color: #fff;
|
||
box-sizing: border-box;
|
||
padding: 5rpx 15rpx;
|
||
border-radius: 25rpx;
|
||
}
|
||
|
||
// 售罄遮罩
|
||
.sold-out-mask {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.6);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
border-radius: 16rpx 16rpx 0 0;
|
||
z-index: 10;
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||
}
|
||
}
|
||
|
||
// 加载中样式
|
||
.loading-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
|
||
.loading-icon {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.loading-dot {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background-color: #f39205;
|
||
border-radius: 50%;
|
||
animation: bounce 1.5s infinite ease-in-out;
|
||
}
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
padding: 10rpx 40rpx;
|
||
border-radius: 40rpx;
|
||
}
|
||
}
|
||
|
||
// 空状态样式
|
||
.empty-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
|
||
.empty-text {
|
||
font-size: 28rpx;
|
||
color: #999999;
|
||
padding: 20rpx 40rpx;
|
||
background-color: rgba(0, 0, 0, 0.05);
|
||
border-radius: 40rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes bounce {
|
||
|
||
0%,
|
||
100% {
|
||
transform: scale(0);
|
||
opacity: 0.5;
|
||
}
|
||
|
||
50% {
|
||
transform: scale(1);
|
||
opacity: 1;
|
||
}
|
||
|
||
}
|
||
</style> |