|
|
@ -2,32 +2,61 @@ |
|
|
|
<view class="container"> |
|
|
|
<!-- 顶部banner --> |
|
|
|
<view class="banner"> |
|
|
|
<!-- <swiper |
|
|
|
<swiper |
|
|
|
:indicator-dots="false" |
|
|
|
:autoplay="true" |
|
|
|
:interval="3000" |
|
|
|
:duration="500" |
|
|
|
circular |
|
|
|
style="width: 100%; height: 320rpx;" |
|
|
|
> --> |
|
|
|
<!-- <swiper-item v-for="(item, index) in bannerList" :key="item.id || index"> |
|
|
|
<video |
|
|
|
v-if="item.type == 1" |
|
|
|
:src="item.voUrl" |
|
|
|
autoplay |
|
|
|
muted |
|
|
|
loop |
|
|
|
:controls="false" |
|
|
|
:show-play-btn="false" |
|
|
|
:show-center-play-btn="false" |
|
|
|
object-fit="cover" |
|
|
|
style="width: 100%; height: 100%;" |
|
|
|
></video> |
|
|
|
> |
|
|
|
<swiper-item v-for="(item, index) in bannerList" :key="item.id || index"> |
|
|
|
<view v-if="item.type == 1" class="video-container"> |
|
|
|
<!-- 预览状态:显示封面图 --> |
|
|
|
<image |
|
|
|
v-if="!videoPlayingStates[index]" |
|
|
|
:src="item.image || ''" |
|
|
|
mode="aspectFill" |
|
|
|
style="width: 100%; height: 100%;" |
|
|
|
class="video-poster" |
|
|
|
@click="playVideoFullscreen(item, index)" |
|
|
|
/> |
|
|
|
<!-- 播放状态:显示视频 --> |
|
|
|
<video |
|
|
|
v-else |
|
|
|
:id="`recycle-video-${index}`" |
|
|
|
:src="item.voUrl" |
|
|
|
:autoplay="true" |
|
|
|
:muted="false" |
|
|
|
:loop="false" |
|
|
|
:controls="true" |
|
|
|
:show-play-btn="true" |
|
|
|
:show-center-play-btn="false" |
|
|
|
:show-fullscreen-btn="true" |
|
|
|
:show-progress="true" |
|
|
|
:show-mute-btn="true" |
|
|
|
:enable-progress-gesture="true" |
|
|
|
:enable-play-gesture="true" |
|
|
|
object-fit="cover" |
|
|
|
style="width: 100%; height: 100%;" |
|
|
|
@fullscreenchange="onFullscreenChange" |
|
|
|
@play="onVideoPlay(index)" |
|
|
|
@pause="onVideoPause(index)" |
|
|
|
@ended="onVideoEnded(index)" |
|
|
|
></video> |
|
|
|
<!-- 播放按钮覆盖层 --> |
|
|
|
<view v-if="!videoPlayingStates[index]" class="video-overlay" @click="playVideoFullscreen(item, index)"> |
|
|
|
<view class="play-button-large"> |
|
|
|
<view class="play-triangle"></view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
<image v-else :src="item.image" mode="aspectFill" style="width: 100%; height: 100%;" /> |
|
|
|
</swiper-item> |
|
|
|
</swiper> --> |
|
|
|
</swiper> |
|
|
|
|
|
|
|
<image v-if="recycle_banner" :src="recycle_banner" mode="aspectFill" style="width: 100%; height: 100%;" /> |
|
|
|
<!-- 备用静态图片,当没有轮播图数据时显示 --> |
|
|
|
<image v-if="!bannerList || bannerList.length === 0" :src="recycle_banner" mode="aspectFill" style="width: 100%; height: 100%;" /> |
|
|
|
</view> |
|
|
|
|
|
|
|
|
|
|
@ -70,11 +99,12 @@ |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
<view class="goods-info"> |
|
|
|
<view class="price-info"> |
|
|
|
<text class="price-symbol">¥</text> |
|
|
|
<text class="price-value">{{item.price}}</text> |
|
|
|
<text class="price-unit">/{{ item.unit || '件' }}</text> |
|
|
|
</view> |
|
|
|
<view class="price-info"> |
|
|
|
<text class="price-symbol">¥</text> |
|
|
|
<text class="price-value" v-if="!item.maxPrice || item.maxPrice == item.price">{{item.price}}</text> |
|
|
|
<text class="price-value" v-else>{{item.price}}-{{item.maxPrice}}</text> |
|
|
|
<text class="price-unit">/{{ item.unit || '件' }}</text> |
|
|
|
</view> |
|
|
|
<view class="quantity-control"> |
|
|
|
<button class="btn-minus" @click="updateQuantity(index, -1)">-</button> |
|
|
|
<text class="quantity">{{getItemTotalQuantity(item)}}</text> |
|
|
@ -116,7 +146,8 @@ |
|
|
|
</view> |
|
|
|
<view class="amount-row"> |
|
|
|
<uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e" style="margin-right: 8rpx;vertical-align: middle;" @click="toggleDetailPanel" /> |
|
|
|
<text class="amount">¥{{priceRange.min}}-{{priceRange.max}}</text> |
|
|
|
<text class="amount" v-if="priceRange.min === priceRange.max">¥{{priceRange.min}}</text> |
|
|
|
<text class="amount" v-else>¥{{priceRange.min}}-{{priceRange.max}}</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
<button class="submit-btn" @click="submitOrder">预约上门取件</button> |
|
|
@ -142,7 +173,8 @@ |
|
|
|
<text class="panel-item-name">{{item.name}}</text> |
|
|
|
<text class="panel-item-desc" v-if="item.brandName">品牌:{{item.brandName}}</text> |
|
|
|
<text class="panel-item-desc" v-else>{{item.service}}</text> |
|
|
|
<text class="panel-item-price">¥{{item.price}}/{{ item.unit || '件' }}</text> |
|
|
|
<text class="panel-item-price" v-if="!item.maxPrice || item.maxPrice == item.price">¥{{item.price}}/{{ item.unit || '件' }}</text> |
|
|
|
<text class="panel-item-price" v-else>¥{{item.price}}-{{item.maxPrice}}/{{ item.unit || '件' }}</text> |
|
|
|
</view> |
|
|
|
<view class="panel-quantity-control"> |
|
|
|
<button class="btn-minus" @click="updateQuantityByProduct(item, -1)">-</button> |
|
|
@ -159,7 +191,8 @@ |
|
|
|
</view> |
|
|
|
<view class="amount-row"> |
|
|
|
<uni-icons :type="showDetailPanel ? 'up' : 'down'" size="18" color="#5e5e5e" style="margin-right: 8rpx;vertical-align: middle;" @click="toggleDetailPanel" /> |
|
|
|
<text class="amount">¥{{priceRange.min}}-{{priceRange.max}}</text> |
|
|
|
<text class="amount" v-if="priceRange.min === priceRange.max">¥{{priceRange.min}}</text> |
|
|
|
<text class="amount" v-else>¥{{priceRange.min}}-{{priceRange.max}}</text> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
<button class="submit-btn" @click="submitOrder">预约上门取件</button> |
|
|
@ -330,6 +363,7 @@ export default { |
|
|
|
pageSize: 10, |
|
|
|
currentCategory: 0, |
|
|
|
tabbarHeight: 0, |
|
|
|
videoPlayingStates: {}, // 记录每个视频的播放状态 |
|
|
|
showDetailPanel: false, |
|
|
|
showBrandPopup: false, |
|
|
|
showRulePopup: false, |
|
|
@ -341,6 +375,7 @@ export default { |
|
|
|
name: '' |
|
|
|
}, |
|
|
|
brandList: [], |
|
|
|
brandCache: {}, // 为每个商品缓存品牌信息 { productId: [brandList] } |
|
|
|
brandIndexList: ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'], |
|
|
|
currentLetter: 'A', |
|
|
|
scrollToView: '', |
|
|
@ -677,6 +712,14 @@ export default { |
|
|
|
}, |
|
|
|
// 获取品牌信息 |
|
|
|
getBrandInfo(brandId) { |
|
|
|
// 从所有商品的品牌缓存中查找品牌信息 |
|
|
|
for (const productId in this.brandCache) { |
|
|
|
const brandInfo = this.brandCache[productId].find(brand => brand.id === brandId) |
|
|
|
if (brandInfo) { |
|
|
|
return brandInfo |
|
|
|
} |
|
|
|
} |
|
|
|
// 如果缓存中没有找到,也从当前brandList中查找(兼容性) |
|
|
|
return this.brandList.find(brand => brand.id === brandId) |
|
|
|
}, |
|
|
|
// 获取商品的总数量(所有品牌) |
|
|
@ -763,7 +806,7 @@ export default { |
|
|
|
icon: item.image, |
|
|
|
quantity: item.quantity, |
|
|
|
unitPrice: item.price, |
|
|
|
maxPrice : item.maxPrice, |
|
|
|
maxPrice: item.maxPrice, |
|
|
|
desc: item.brandName ? `品牌:${item.brandName}` : '允许脏破烂,160码以上' |
|
|
|
} |
|
|
|
|
|
|
@ -824,6 +867,9 @@ export default { |
|
|
|
// 清空已查看规则的记录 |
|
|
|
this.viewedRuleItems.clear() |
|
|
|
|
|
|
|
// 清空品牌缓存 |
|
|
|
this.brandCache = {} |
|
|
|
|
|
|
|
// 模拟网络请求延迟 |
|
|
|
await new Promise(resolve => setTimeout(resolve, 1000)) |
|
|
|
|
|
|
@ -1017,7 +1063,7 @@ export default { |
|
|
|
this.$api('getGoodsBrandList', params, res => { |
|
|
|
// console.log(res,'res') |
|
|
|
if (res && res.success && res.result && res.result.records) { |
|
|
|
this.brandList = res.result.records.map(item => { |
|
|
|
const brandList = res.result.records.map(item => { |
|
|
|
// 获取品牌名称的拼音首字母 |
|
|
|
const firstChar = this.getPinyinFirstLetter(item.name) |
|
|
|
return { |
|
|
@ -1027,6 +1073,13 @@ export default { |
|
|
|
letter: firstChar |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
// 缓存到对应商品的品牌列表中 |
|
|
|
this.$set(this.brandCache, productId, brandList) |
|
|
|
|
|
|
|
// 同时更新当前显示的brandList(用于弹窗显示) |
|
|
|
this.brandList = brandList |
|
|
|
|
|
|
|
// console.log(this.brandList,'this.brandList') |
|
|
|
} |
|
|
|
}) |
|
|
@ -1068,6 +1121,54 @@ export default { |
|
|
|
}, 500) |
|
|
|
}, |
|
|
|
|
|
|
|
// 视频相关方法 |
|
|
|
playVideoFullscreen(item, index) { |
|
|
|
if (!this.videoPlayingStates[index]) { |
|
|
|
// 第一次点击:显示视频并开始播放 |
|
|
|
this.$set(this.videoPlayingStates, index, true); |
|
|
|
|
|
|
|
// 等待视频元素渲染后再进行操作 |
|
|
|
this.$nextTick(() => { |
|
|
|
setTimeout(() => { |
|
|
|
const videoContext = uni.createVideoContext(`recycle-video-${index}`, this); |
|
|
|
if (videoContext) { |
|
|
|
// 直接进入全屏播放 |
|
|
|
videoContext.requestFullScreen({ |
|
|
|
direction: -1 // 自动选择方向 |
|
|
|
}); |
|
|
|
} |
|
|
|
}, 200); |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
onVideoPlay(index) { |
|
|
|
this.$set(this.videoPlayingStates, index, true); |
|
|
|
}, |
|
|
|
|
|
|
|
onVideoPause(index) { |
|
|
|
this.$set(this.videoPlayingStates, index, false); |
|
|
|
}, |
|
|
|
|
|
|
|
onVideoEnded(index) { |
|
|
|
// 视频播放结束,回到预览状态 |
|
|
|
this.$set(this.videoPlayingStates, index, false); |
|
|
|
}, |
|
|
|
|
|
|
|
onFullscreenChange(e) { |
|
|
|
console.log('全屏状态改变:', e.detail); |
|
|
|
const videoIndex = e.target.id.replace('recycle-video-', ''); |
|
|
|
|
|
|
|
if (e.detail.fullScreen) { |
|
|
|
// 进入全屏时,不做任何操作 |
|
|
|
console.log('进入全屏模式,方向:', e.detail.direction); |
|
|
|
} else { |
|
|
|
// 退出全屏时,回到预览状态 |
|
|
|
console.log('退出全屏模式,回到预览状态'); |
|
|
|
this.$set(this.videoPlayingStates, videoIndex, false); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 初始化页面数据的方法 |
|
|
|
initializePageData() { |
|
|
|
const options = this.loadOptions || {} |
|
|
@ -1102,6 +1203,7 @@ export default { |
|
|
|
// 清空已查看规则的记录 |
|
|
|
this.viewedRuleItems.clear() |
|
|
|
this.showDetailPanel = false |
|
|
|
this.brandCache = {} // 清空品牌缓存 |
|
|
|
this.$forceUpdate() |
|
|
|
getApp().globalData.shouldClearRecycle = false |
|
|
|
} |
|
|
@ -1197,6 +1299,7 @@ export default { |
|
|
|
// 清空已查看规则的记录 |
|
|
|
this.viewedRuleItems.clear() |
|
|
|
this.showDetailPanel = false |
|
|
|
this.brandCache = {} // 清空品牌缓存 |
|
|
|
this.$forceUpdate() |
|
|
|
getApp().globalData.shouldClearRecycle = false |
|
|
|
} |
|
|
@ -1213,6 +1316,8 @@ export default { |
|
|
|
}) |
|
|
|
// 清空已查看规则的记录 |
|
|
|
this.viewedRuleItems.clear() |
|
|
|
// 清空品牌缓存 |
|
|
|
this.brandCache = {} |
|
|
|
// 重置其他相关数据 |
|
|
|
this.showDetailPanel = false |
|
|
|
this.$forceUpdate() |
|
|
@ -1256,13 +1361,80 @@ export default { |
|
|
|
width: 100%; |
|
|
|
height: 90%; |
|
|
|
} |
|
|
|
|
|
|
|
.video-container { |
|
|
|
position: relative; |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
|
|
|
|
.video-poster { |
|
|
|
cursor: pointer; |
|
|
|
transition: transform 0.2s ease; |
|
|
|
|
|
|
|
&:active { |
|
|
|
transform: scale(0.98); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.video-overlay { |
|
|
|
position: absolute; |
|
|
|
top: 0; |
|
|
|
left: 0; |
|
|
|
right: 0; |
|
|
|
bottom: 0; |
|
|
|
background: rgba(0, 0, 0, 0.4); |
|
|
|
z-index: 10; |
|
|
|
cursor: pointer; |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
|
|
|
|
.play-button-large { |
|
|
|
width: 120rpx; |
|
|
|
height: 120rpx; |
|
|
|
background: transparent; |
|
|
|
border-radius: 50%; |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
backdrop-filter: blur(10rpx); |
|
|
|
transition: all 0.3s ease; |
|
|
|
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.3); |
|
|
|
|
|
|
|
&:active { |
|
|
|
transform: scale(0.9); |
|
|
|
background: rgba(255, 255, 255, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
.play-triangle { |
|
|
|
width: 0; |
|
|
|
height: 0; |
|
|
|
border-left: 24rpx solid #fff; |
|
|
|
border-top: 18rpx solid transparent; |
|
|
|
border-bottom: 18rpx solid transparent; |
|
|
|
margin-left: 8rpx; |
|
|
|
transition: all 0.3s ease; |
|
|
|
filter: drop-shadow(0 2rpx 4rpx rgba(0, 0, 0, 0.3)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
&:hover .play-button-large { |
|
|
|
transform: scale(1.1); |
|
|
|
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.4); |
|
|
|
|
|
|
|
.play-triangle { |
|
|
|
border-left-color: #ff8917; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.goods-list { |
|
|
|
// flex: 1; |
|
|
|
display: flex; |
|
|
|
position: relative; |
|
|
|
height: calc(100vh - 320rpx - 120rpx - env(safe-area-inset-bottom)); /* 减去banner和底部栏的高度 */ |
|
|
|
height: calc(110vh - 320rpx - 160rpx - env(safe-area-inset-bottom)); /* 减去banner、底部栏和绿色提示条的高度 */ |
|
|
|
margin-top: -10rpx; |
|
|
|
z-index: 2; |
|
|
|
border-radius: 20rpx 20rpx 0 0; |
|
|
@ -1336,13 +1508,14 @@ export default { |
|
|
|
.goods-content { |
|
|
|
flex: 1; |
|
|
|
height: 100%; |
|
|
|
padding: 0 30rpx; |
|
|
|
padding: 0 30rpx 180rpx 30rpx; /* 添加底部padding,为固定底部栏预留空间 */ |
|
|
|
background: #ffffff; |
|
|
|
width: 70%; |
|
|
|
margin: 1rpx; |
|
|
|
margin-left: 0; |
|
|
|
border-radius: 0 20rpx 0 0; |
|
|
|
overflow-y: auto; |
|
|
|
box-sizing: border-box; |
|
|
|
scrollbar-width: none; /* Firefox */ |
|
|
|
-ms-overflow-style: none; /* IE and Edge */ |
|
|
|
&::-webkit-scrollbar { |
|
|
@ -1432,11 +1605,15 @@ export default { |
|
|
|
display: flex; |
|
|
|
justify-content: space-between; |
|
|
|
align-items: center; |
|
|
|
flex-wrap: nowrap; |
|
|
|
gap: 10rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.price-info { |
|
|
|
display: flex; |
|
|
|
align-items: baseline; |
|
|
|
white-space: nowrap; |
|
|
|
flex-shrink: 0; |
|
|
|
|
|
|
|
.price-symbol { |
|
|
|
font-size: 24rpx; |
|
|
@ -1448,17 +1625,21 @@ export default { |
|
|
|
color: #ff7a0e; |
|
|
|
font-weight: bold; |
|
|
|
margin: 0 4rpx; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
|
|
|
|
.price-unit { |
|
|
|
font-size: 24rpx; |
|
|
|
color: #999; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.quantity-control { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
flex-shrink: 0; |
|
|
|
white-space: nowrap; |
|
|
|
|
|
|
|
button { |
|
|
|
width: 60rpx; |
|
|
@ -1484,7 +1665,7 @@ export default { |
|
|
|
} |
|
|
|
|
|
|
|
.quantity { |
|
|
|
width: 80rpx; |
|
|
|
width: 50rpx; |
|
|
|
text-align: center; |
|
|
|
font-size: 32rpx; |
|
|
|
color: #333; |
|
|
@ -1531,15 +1712,16 @@ export default { |
|
|
|
flex-direction: column; |
|
|
|
justify-content: center; |
|
|
|
min-width: 0; |
|
|
|
overflow: hidden; |
|
|
|
} |
|
|
|
.other-unrecycle-title { |
|
|
|
font-size: 30rpx; |
|
|
|
color: #222; |
|
|
|
font-weight: bold; |
|
|
|
margin-bottom: 8rpx; |
|
|
|
word-break: break-all; |
|
|
|
white-space: normal; |
|
|
|
overflow: visible; |
|
|
|
white-space: nowrap; |
|
|
|
overflow: hidden; |
|
|
|
text-overflow: ellipsis; |
|
|
|
} |
|
|
|
.other-unrecycle-desc { |
|
|
|
font-size: 24rpx; |
|
|
@ -1552,11 +1734,13 @@ export default { |
|
|
|
.other-unrecycle-price-row { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
.other-unrecycle-price { |
|
|
|
font-size: 28rpx; |
|
|
|
color: #ff9c00; |
|
|
|
font-weight: bold; |
|
|
|
white-space: nowrap; |
|
|
|
} |
|
|
|
.other-unrecycle-btn { |
|
|
|
width: 60rpx; |
|
|
@ -1578,7 +1762,7 @@ export default { |
|
|
|
right: 0; |
|
|
|
bottom: calc(v-bind('tabbarHeight + "px"') + env(safe-area-inset-bottom)); |
|
|
|
width: 100vw; |
|
|
|
z-index: 1001; |
|
|
|
z-index: 100; |
|
|
|
background: transparent; |
|
|
|
box-sizing: border-box; |
|
|
|
pointer-events: auto; |
|
|
@ -1597,13 +1781,14 @@ export default { |
|
|
|
background: #eaffea; |
|
|
|
color: #13ac47; |
|
|
|
font-size: 20rpx; |
|
|
|
// padding: 16rpx 30rpx 0 30rpx; |
|
|
|
padding: 8rpx 30rpx; |
|
|
|
box-sizing: border-box; |
|
|
|
text-align: left; |
|
|
|
text-align: center; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
// flex-direction: r; |
|
|
|
line-height: 1.4; |
|
|
|
min-height: 40rpx; |
|
|
|
.tip-highlight { |
|
|
|
color: #ff9c00; |
|
|
|
font-weight: bold; |
|
|
@ -1796,6 +1981,8 @@ export default { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
margin-left: 20rpx; |
|
|
|
margin-right: 30rpx; |
|
|
|
flex-shrink: 0; |
|
|
|
} |
|
|
|
.panel-quantity-control button { |
|
|
|
width: 48rpx; |
|
|
@ -1818,7 +2005,7 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
.panel-quantity-control .quantity { |
|
|
|
width: 40rpx; |
|
|
|
width: 30rpx; |
|
|
|
text-align: center; |
|
|
|
font-size: 28rpx; |
|
|
|
color: #333; |
|
|
|