需要添加直播接口
This commit is contained in:
320
uni_modules/tuikit-atomic-x/components/GiftPicker.nvue
Normal file
320
uni_modules/tuikit-atomic-x/components/GiftPicker.nvue
Normal file
@@ -0,0 +1,320 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user