This commit is contained in:
zpc 2025-04-06 04:25:46 +08:00
parent db6ae0837f
commit 475dc60cbd
6 changed files with 499 additions and 37 deletions

View File

@ -0,0 +1,95 @@
# DetailPopup 弹窗组件
这是一个简单的弹窗组件,支持自定义内容,提供开启和关闭方法。
## 使用方法
### 1. 导入组件
```vue
<script>
import DetailPopup from '@/components/detail-popup';
export default {
components: {
DetailPopup
}
}
</script>
```
### 2. 在模板中使用
```vue
<template>
<view>
<!-- 按钮触发弹窗 -->
<button @click="openPopup">打开弹窗</button>
<!-- 弹窗组件 -->
<detail-popup ref="popup">
<!-- 自定义弹窗内容 -->
<view class="custom-content">
<view class="title">这是弹窗标题</view>
<view class="content">这是弹窗内容</view>
<button @click="closePopup">确定</button>
</view>
</detail-popup>
</view>
</template>
<script>
export default {
methods: {
// 打开弹窗
openPopup() {
this.$refs.popup.open();
},
// 关闭弹窗
closePopup() {
this.$refs.popup.close();
}
}
}
</script>
<style>
.custom-content {
padding: 20rpx;
}
.title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 20rpx;
text-align: center;
}
.content {
font-size: 28rpx;
margin-bottom: 30rpx;
}
</style>
```
### 3. 事件监听
组件会在打开和关闭时触发相应的事件:
```vue
<detail-popup ref="popup" @open="onPopupOpen" @close="onPopupClose">
<!-- 内容 -->
</detail-popup>
<script>
export default {
methods: {
onPopupOpen() {
console.log('弹窗已打开');
},
onPopupClose() {
console.log('弹窗已关闭');
}
}
}
</script>
```

View File

@ -0,0 +1,111 @@
<template>
<uni-popup ref="popupbase" type="center" maskBackgroundColor="rgba(0,0,0,0.8)">
<view v-if="visible" class="detail-popup" :style="{
width: width,
backgroundColor: bgColor,
maxHeight: '80vh',
overflowY: 'auto'
}">
<!-- 默认插槽可以设置弹窗的内容 -->
<slot></slot>
<!-- 关闭按钮 -->
<view class="close-btn" @click="close" v-if="showClose">
<image :src="$img1('common/close.png')" mode="aspectFit"></image>
</view>
</view>
</uni-popup>
</template>
<script>
export default {
name: 'DetailPopup',
props: {
//
bgColor: {
type: String,
default: '#FFFFFF'
},
//
width: {
type: String,
default: '570rpx'
},
//
showClose: {
type: Boolean,
default: true
}
},
data() {
return {
visible: false
}
},
created() {
//
},
methods: {
//
open() {
this.visible = true;
// 使nextTickDOM
this.$nextTick(() => {
if (this.$refs.popupbase) {
//
setTimeout(() => {
this.$refs.popupbase.open();
}, 50);
} else {
console.error('popup ref不存在请检查组件是否正确挂载');
//
setTimeout(() => {
if (this.$refs.popupbase) {
this.$refs.popupbase.open();
}
}, 300);
}
});
// open
this.$emit('open');
},
//
close() {
this.visible = false;
if (this.$refs.popupbase) {
this.$refs.popupbase.close();
}
// close
this.$emit('close');
}
}
}
</script>
<style lang="scss" scoped>
.detail-popup {
position: relative;
border-radius: 16rpx;
padding: 30rpx;
box-sizing: border-box;
//
.close-btn {
width: 48rpx;
height: 48rpx;
position: absolute;
left: 50%;
bottom: -80rpx;
transform: translateX(-50%);
image {
width: 100%;
height: 100%;
}
}
}
</style>

View File

@ -0,0 +1,131 @@
<template>
<view class="example-container">
<view class="btn-group">
<button class="btn" @click="openBasicPopup">基础弹窗</button>
<button class="btn" @click="openCustomPopup">自定义弹窗</button>
</view>
<!-- 基础弹窗 -->
<detail-popup ref="basicPopup">
<view class="popup-content">
<view class="popup-title">基础弹窗</view>
<view class="popup-text">这是一个基础弹窗示例使用了默认配置</view>
<button class="popup-btn" @click="$refs.basicPopup.close()">关闭</button>
</view>
</detail-popup>
<!-- 自定义弹窗 -->
<detail-popup
ref="customPopup"
bgColor="#F5F5F5"
width="650rpx"
@open="onPopupOpen"
@close="onPopupClose"
>
<view class="popup-content custom">
<view class="popup-title">自定义弹窗</view>
<view class="popup-text">这是一个自定义弹窗示例修改了背景色和宽度</view>
<image class="popup-image" :src="$img1('common/chouTitle.png')" mode="aspectFit"></image>
<button class="popup-btn custom-btn" @click="$refs.customPopup.close()">确定</button>
</view>
</detail-popup>
</view>
</template>
<script>
import DetailPopup from './index';
export default {
components: {
DetailPopup
},
methods: {
//
openBasicPopup() {
this.$refs.basicPopup.open();
},
//
openCustomPopup() {
this.$refs.customPopup.open();
},
//
onPopupOpen() {
console.log('自定义弹窗已打开');
},
//
onPopupClose() {
console.log('自定义弹窗已关闭');
}
}
}
</script>
<style lang="scss" scoped>
.example-container {
padding: 30rpx;
.btn-group {
display: flex;
justify-content: space-around;
.btn {
width: 280rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: #3b3941;
color: #FFFFFF;
border-radius: 40rpx;
}
}
}
.popup-content {
padding: 20rpx;
.popup-title {
font-size: 32rpx;
font-weight: bold;
text-align: center;
margin-bottom: 20rpx;
}
.popup-text {
font-size: 28rpx;
color: #333333;
line-height: 1.6;
margin-bottom: 30rpx;
}
.popup-image {
width: 100%;
height: 150rpx;
margin-bottom: 30rpx;
}
.popup-btn {
width: 80%;
height: 80rpx;
line-height: 80rpx;
background: #3b3941;
color: #FFFFFF;
border-radius: 40rpx;
margin: 0 auto;
}
&.custom {
padding: 30rpx;
.popup-title {
color: #333333;
}
.custom-btn {
background: linear-gradient(90deg, #2dcbff 0%, #ff873a 100%);
}
}
}
</style>

View File

@ -0,0 +1,3 @@
import DetailPopup from './detail-popup.vue';
export default DetailPopup;

View File

@ -1,24 +1,43 @@
<template>
<uni-popup ref="popup" type="center" maskBackgroundColor="rgba(0,0,0,0.9)">
<view v-if="visible" class="preview-popup">
<!-- 商品图片 -->
<view class="pic center relative">
<image :src="innerImgUrl" lazy-load></image>
<view class="type-tag justify-center" v-if="innerTipTitle">{{ innerTipTitle }}</view>
<!-- 赏品列表 -->
<view class="list" v-if="showList">
<scroll-view :scroll-y="true">
<view class="res-list">
<view class="res-item" v-for="(item, i) in dataItem.children" :key="i">
<view class="pic center">
<image :src="item.imgurl" lazy-load></image>
<view class="tag center"
:style="{ backgroundColor: item.shang_info ? item.shang_info.color : '#000000' }">{{ item.shang_info ?
item.shang_info.title : "" }}</view>
<view class="num center">{{ item.surplus_stock }}/{{ item.stock }}</view>
</view>
<view class="title hang1 flex center " style="padding: 0px;margin: 0px;text-align: center;width: 100%;">
<text> {{ item.title }}</text>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 商品图片 -->
<view class="pic center relative" v-if="!showList">
<image :src="innerImgUrl" lazy-load></image>
<view class="type-tag justify-center"
:style="{ backgroundColor: dataItem.shang_info ? dataItem.shang_info.color : '#000000' }"
v-if="innerTipTitle">{{ innerTipTitle }}</view>
</view>
<!-- 商品标题 -->
<view class="title">
<text class="hang1">{{ innerTitle }}</text>
<text class="hang1">{{ !showList?innerTitle:'抽中宝箱后,会在奖品列表中随机抽取一个奖品' }}</text>
</view>
<!-- 商品信息列表 -->
<view class="info-list">
<view class="info-item" v-if="innerProbability">{{ innerProbability }}</view>
<view class="info-item" v-if="isTips" @click="showList = !showList">{{ showList ? '收起' : '查看奖品' }}</view>
<view class="info-item" v-if="innerProductType">产品类型: {{ innerProductType }}</view>
<view class="info-item" v-for="(item, index) in innerExtraInfo" :key="index">{{ item }}</view>
</view>
<!-- 关闭按钮 -->
<view class="close icon" @click="closePopup">
<image :src="$img('/static/img/close.png')" lazy-load></image>
@ -28,8 +47,10 @@
</template>
<script>
export default {
name: 'DetailPreviewPopup',
props: {
//
title: {
@ -61,6 +82,7 @@ export default {
type: Array,
default: () => []
}
},
data() {
return {
@ -71,7 +93,10 @@ export default {
innerTipTitle: '',
innerProductType: '',
innerProbability: '',
innerExtraInfo: []
innerExtraInfo: [],
dataItem: {},
showList: false,
isTips: false
}
},
watch: {
@ -103,7 +128,7 @@ export default {
this.innerProductType = this.productType;
this.innerProbability = this.probability;
this.innerExtraInfo = this.extraInfo;
//
uni.$on('global-preview-event', this.handleGlobalEvent);
},
@ -118,11 +143,12 @@ export default {
this.setPreviewData(options);
},
//
setPreviewData(data) {
console.log('设置预览数据', data);
if (!data) return;
//
if (data.title !== undefined) this.innerTitle = data.title;
if (data.imgUrl !== undefined) this.innerImgUrl = data.imgUrl;
@ -130,19 +156,28 @@ export default {
if (data.productType !== undefined) this.innerProductType = data.productType;
if (data.probability !== undefined) this.innerProbability = data.probability;
if (data.extraInfo !== undefined) this.innerExtraInfo = data.extraInfo;
this.showList = false;
this.isTips = false;
if (data.dataItem !== undefined) {
this.dataItem = data.dataItem;
if (data.dataItem != null && data.dataItem.children != null && data.dataItem.children.length > 0) {
this.showList = false;
this.isTips = true;
}
}
//
this.open();
},
//
open() {
console.log('尝试打开弹窗', this.$refs.popup);
this.visible = true;
console.log('打开弹窗', this.dataItem);
// 使nextTickDOM
this.$nextTick(() => {
console.log('DOM已更新准备打开弹窗');
console.log(' ');
if (this.$refs.popup) {
//
setTimeout(() => {
@ -161,7 +196,7 @@ export default {
}
});
},
//
closePopup() {
this.visible = false;
@ -177,25 +212,96 @@ export default {
<style lang="scss" scoped>
.preview-popup {
position: relative;
width: 570rpx;
width: 80vw;
box-sizing: border-box;
height: 850rpx;
.list {
width: 100%;
height: 598rpx;
background-color: #ffffff;
border-radius: 16rpx;
margin: 0rpx auto;
.res-list {
padding: 20rpx 10rpx;
display: grid;
grid-template-columns: repeat(3, 33%);
gap: 24rpx 10rpx;
.res-item {
width: 180rpx;
height: 240rpx;
background-color: #f8f8f8;
border-radius: 16rpx;
.pic {
width: 100%;
height: 180rpx;
position: relative;
background: #d8d8d8;
border-radius: 16rpx 16rpx 0rpx 0rpx;
>image {
width: 100%;
height: 180rpx;
border-radius: 16rpx 16rpx 0rpx 0rpx;
}
.num {
position: absolute;
z-index: 2;
left: 50%;
transform: translateX(-50%);
bottom: 10rpx;
min-width: 90rpx;
height: 28rpx;
font-weight: 400;
font-size: 20rpx;
color: #333333;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 18rpx;
}
}
.title {
padding: 22rpx 10rpx 0;
font-weight: 400;
font-size: 16rpx;
color: #333333;
}
.exchange {
padding: 10rpx 10rpx;
font-weight: 400;
font-size: 20rpx;
color: #ffffff;
}
}
}
}
//
.pic {
width: 570rpx;
width: 100%;
height: 598rpx;
background: #FFFFFF;
border-radius: 16rpx;
image {
width: 548rpx;
height: 549rpx;
width: 100%;
height: 100%;
border-radius: 16rpx;
}
//
.type-tag {
width: 68rpx;
height: 32rpx;
width: 100rpx;
height: 40rpx;
position: absolute;
display: flex;
align-items: center;
@ -203,17 +309,18 @@ export default {
top: 12rpx;
left: 24rpx;
font-weight: 400;
font-size: 14rpx;
line-height: 40rpx;
font-size: 20rpx;
color: #FFFFFF;
box-sizing: border-box;
background: url($imgurl + 'common/chouLabel.png') no-repeat 0 0 / 100% 100%;
border-radius: 25rpx;
}
}
//
.title {
margin: 40rpx auto 0;
width: 564rpx;
width: 100%;
height: 60rpx;
box-sizing: border-box;
display: flex;
@ -248,17 +355,32 @@ export default {
//
.close {
width: 48rpx;
height: 48rpx;
position: absolute;
left: 50%;
bottom: 0;
transform: translate(-50%, 160%);
width: 100%;
margin-top:40rpx;
display: flex;
justify-content: center;
align-items: center;
// transform: translate(-50%, 160%);
image {
width: 100%;
height: 100%;
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;
}
}
</style>
</style>

View File

@ -164,7 +164,6 @@
<view class="title hang1 flex center">
<text> {{ item.goodslist_title }}</text>
</view>
</view>
</view>
</scroll-view>
@ -332,7 +331,8 @@
imgUrl: item.imgurl,
tipTitle: item.shang_info ? item.shang_info.title : '',
productType: this.optData.type_text,
probability: item.pro
probability: item.pro,
dataItem: item
}).then(() => {
console.log('预览弹窗打开成功');
});