320 lines
8.1 KiB
Plaintext
320 lines
8.1 KiB
Plaintext
<template>
|
|
<view class="bottom-drawer-container" v-if="modelValue">
|
|
<view class="drawer-overlay" @tap="close"></view>
|
|
<view class="bottom-drawer" :class="{ 'drawer-open': modelValue }">
|
|
<view class="gift-header">
|
|
<view class="header-content">
|
|
<text class="gift-title">礼物</text>
|
|
</view>
|
|
</view>
|
|
|
|
<swiper class="gift-content" :current="currentPage" @change="handleSwiperChange" style="height: 710rpx;">
|
|
<swiper-item v-for="(page, pageIndex) in giftPages" :key="pageIndex" class="gift-page">
|
|
<view class="gift-container">
|
|
<view class="gift-item" v-for="(giftInfo, index) in page" :key="giftInfo.giftID"
|
|
:class="{ 'selected': selectedGiftIndex === (pageIndex * itemsPerPage + index) }"
|
|
@tap="selectGift(pageIndex * itemsPerPage + index)">
|
|
<view class="gift-image-container">
|
|
<image class="gift-image" :src="giftInfo.iconURL" mode="aspectFit" />
|
|
</view>
|
|
<view class="gift-action" v-if="selectedGiftIndex === (pageIndex * itemsPerPage + index)">
|
|
<view class="send-btn selected-btn" @tap.stop="handleSendGift(pageIndex * itemsPerPage + index)">
|
|
<text class="send-text">赠送</text>
|
|
</view>
|
|
</view>
|
|
<text
|
|
class="gift-name">{{ selectedGiftIndex === (pageIndex * itemsPerPage + index) ? '' : (giftInfo.name || '') }}</text>
|
|
<text class="gift-price">{{ giftInfo.coins }}</text>
|
|
</view>
|
|
</view>
|
|
</swiper-item>
|
|
</swiper>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
ref
|
|
} from 'vue';
|
|
import {
|
|
downloadAndSaveToPath
|
|
} from '@/uni_modules/tuikit-atomic-x/components/GiftPlayer/giftService';
|
|
import {
|
|
useGiftState
|
|
} from "@/uni_modules/tuikit-atomic-x/state/GiftState";
|
|
const {
|
|
usableGifts,
|
|
latestGift,
|
|
sendGift,
|
|
refreshUsableGifts
|
|
} = useGiftState(uni?.$liveID);
|
|
|
|
export default {
|
|
name: 'GiftPanel',
|
|
props: {
|
|
modelValue: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
onGiftSelect: {
|
|
type: Function,
|
|
default: null
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
scrollTop: 0,
|
|
selectedGiftIndex: 0,
|
|
giftLists: usableGifts,
|
|
currentPage: 0,
|
|
itemsPerPage: 8,
|
|
};
|
|
},
|
|
methods: {
|
|
close() {
|
|
this.$emit('update:modelValue', false);
|
|
},
|
|
handleSwiperChange(e) {
|
|
this.currentPage = e.detail.current;
|
|
},
|
|
selectGift(index) {
|
|
this.selectedGiftIndex = index;
|
|
},
|
|
handleSendGift(index) {
|
|
const gift = (this.flattenedGifts || [])[index];
|
|
if (this.selectedGiftIndex !== index) return;
|
|
if (this.onGiftSelect) {
|
|
this.onGiftSelect(gift);
|
|
}
|
|
this.selectedGiftIndex = -1;
|
|
},
|
|
handleRecharge() {
|
|
this.$emit('recharge');
|
|
}
|
|
},
|
|
computed: {
|
|
// 兼容新的分类结构与旧的扁平结构
|
|
flattenedGifts() {
|
|
const list = this.giftLists || [];
|
|
if (!Array.isArray(list)) return [];
|
|
// 新结构:[{ categoryID, name, giftList: [...] }, ...]
|
|
if (list.length > 0 && list[0] && Array.isArray(list[0].giftList)) {
|
|
const merged = [];
|
|
for (let i = 0; i < list.length; i++) {
|
|
const category = list[i];
|
|
const gifts = Array.isArray(category.giftList) ? category.giftList : [];
|
|
for (let j = 0; j < gifts.length; j++) {
|
|
merged.push(gifts[j]);
|
|
}
|
|
}
|
|
return merged;
|
|
}
|
|
// 旧结构:直接为礼物数组
|
|
return list;
|
|
},
|
|
giftPages() {
|
|
const pages = [];
|
|
const list = this.flattenedGifts;
|
|
for (let i = 0; i < list.length; i += this.itemsPerPage) {
|
|
pages.push(list.slice(i, i + this.itemsPerPage));
|
|
}
|
|
return pages;
|
|
}
|
|
},
|
|
watch: {
|
|
async giftLists(newVal) {
|
|
const flatten = () => {
|
|
if (!Array.isArray(newVal)) return [];
|
|
if (newVal.length > 0 && newVal[0] && Array.isArray(newVal[0].giftList)) {
|
|
// 分类结构
|
|
const out = [];
|
|
for (let i = 0; i < newVal.length; i++) {
|
|
const gifts = Array.isArray(newVal[i].giftList) ? newVal[i].giftList : [];
|
|
for (let j = 0; j < gifts.length; j++) out.push(gifts[j]);
|
|
}
|
|
return out;
|
|
}
|
|
return newVal;
|
|
};
|
|
const flatList = flatten();
|
|
if (!flatList || flatList.length === 0) return;
|
|
for (let i = 0; i < flatList.length; i++) {
|
|
const giftData = flatList[i];
|
|
if (giftData && giftData.resourceURL) {
|
|
const giftKey = `${(giftData.name || '').split(' ').join('')}-${giftData.giftID}`;
|
|
if (plus && plus.storage && plus.storage.getAllKeys && plus.storage.getAllKeys().includes(giftKey)) continue;
|
|
const svgaGiftSourceUrl = plus && plus.storage ? plus.storage.getItem(giftKey) : null;
|
|
if (!svgaGiftSourceUrl) {
|
|
const filePath = await downloadAndSaveToPath(`${giftData.resourceURL}`);
|
|
if (plus && plus.storage) plus.storage.setItem(giftKey, filePath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
if (!uni?.$liveID) {
|
|
return;
|
|
}
|
|
refreshUsableGifts({
|
|
liveID: uni.$liveID
|
|
})
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style>
|
|
.bottom-drawer-container {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
top: 0;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.drawer-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.bottom-drawer {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: rgba(34, 38, 46, 1);
|
|
border-top-left-radius: 32rpx;
|
|
border-top-right-radius: 32rpx;
|
|
transform: translateY(100%);
|
|
transition-property: transform;
|
|
transition-duration: 0.3s;
|
|
transition-timing-function: ease;
|
|
flex-direction: column;
|
|
height: 710rpx;
|
|
}
|
|
|
|
.drawer-open {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.gift-header {
|
|
padding: 40rpx 48rpx;
|
|
border-top-left-radius: 32rpx;
|
|
border-top-right-radius: 32rpx;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.header-content {
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.gift-title {
|
|
font-size: 36rpx;
|
|
color: #ffffff;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.gift-content {
|
|
flex: 1;
|
|
height: 710rpx;
|
|
}
|
|
|
|
.gift-page {
|
|
flex: 1;
|
|
height: 710rpx;
|
|
}
|
|
|
|
.gift-container {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
padding: 0 20rpx;
|
|
flex: 1;
|
|
height: 710rpx;
|
|
}
|
|
|
|
.gift-item {
|
|
width: 168rpx;
|
|
/* 4列布局 */
|
|
margin-bottom: 24rpx;
|
|
align-items: center;
|
|
border-radius: 20rpx;
|
|
padding: 10rpx 6rpx 6rpx 6rpx;
|
|
border: 2rpx solid transparent;
|
|
background-color: transparent;
|
|
box-sizing: border-box;
|
|
height: 230rpx;
|
|
}
|
|
|
|
.gift-item.selected {
|
|
border-color: #2B6AD6;
|
|
background-color: rgba(43, 106, 214, 0.12);
|
|
height: 230rpx;
|
|
}
|
|
|
|
.gift-image-container {
|
|
width: 110rpx;
|
|
height: 110rpx;
|
|
margin-bottom: 12rpx;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.gift-image {
|
|
width: 110rpx;
|
|
height: 110rpx;
|
|
}
|
|
|
|
.gift-action {
|
|
height: 56rpx;
|
|
/* 固定高度避免布局抖动 */
|
|
margin-bottom: 8rpx;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.send-btn {
|
|
padding: 8rpx 24rpx;
|
|
border-radius: 100rpx;
|
|
background-color: rgba(58, 60, 66, 1);
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.send-btn.selected-btn {
|
|
background-color: #2b6ad6;
|
|
}
|
|
|
|
.send-text {
|
|
color: #ffffff;
|
|
font-size: 24rpx;
|
|
}
|
|
|
|
/* 名称与价格样式 */
|
|
.gift-name {
|
|
color: #ffffff;
|
|
font-size: 26rpx;
|
|
line-height: 36rpx;
|
|
text-align: center;
|
|
max-width: 150rpx;
|
|
lines: 1;
|
|
text-overflow: ellipsis;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.gift-price {
|
|
font-size: 20rpx;
|
|
color: rgba(255, 255, 255, 0.7);
|
|
line-height: 28rpx;
|
|
text-align: center;
|
|
}
|
|
</style> |