Ai_GirlFriend/xuniYou/pages/mine/store.vue

536 lines
12 KiB
Vue
Raw Normal View History

2026-01-31 19:15:41 +08:00
<template>
<view class="page fc">
<uni-nav-bar fixed statusBar left-icon="left" right-icon="gear" background-color="transparent" :border="false"
@clickLeft="back" title="金币商店"></uni-nav-bar>
<view class="back"></view>
<scroll-view scroll-y="true" class="scroll-view f1" show-scrollbar>
<view class="body">
<view class="list">
<image class="list_back"
src="https://nvlovers.oss-cn-qingdao.aliyuncs.com/uploads/20251226/efa43d6b415cb6acb4a801d92d59ca79.png"
mode="widthFix"></image>
<view class="list_content">
<view class="table fa">
<view class="table_item faj" v-for="(item, index) in tableList" :key="index"
@click="tableClick(index)" :class="tableIndex == index ? 'table_active' : ''">{{
item.name }}
<view class="table_tag" v-if="tableIndex == index"></view>
</view>
</view>
<view class="list_module fa" v-if="tableIndex == 0">
<view class="list_detail" v-for="(item, index) in outfitMallList.items" :key="index"
@click="outfitClick(item)">
<image class="list_image"
:src="item.image_url ? item.image_url : '/static/images/replacement_picture.png'"
mode="aspectFill"></image>
<image class="list_lock"
:src="isOutfitUnlocked(item.id) ? '' : '/static/images/replacement_lock.png'">
</image>
</view>
</view>
<view class="list_module fa" v-if="tableIndex == 1">
<view class="list_detail" v-for="(item, index) in outfitVoicesMallList.voices" :key="index"
@click="outfitVoicesClick(item)">
<image class="list_images"
:src="item.avatar_url ? global + item.avatar_url : '/static/images/replacement_picture.png'"
mode="aspectFill"></image>
<image class="list_voice" @click.stop="playVoice(item.sample_audio_url)"
src="/static/images/timbre_voice.png"></image>
<image class="list_lock"
:src="isVoiceUnlocked(item.id) ? '' : '/static/images/replacement_lock.png'">
</image>
</view>
</view>
</view>
</view>
</view>
<view class="outfit" v-if="outfitStats && tableIndex == 0">
<view class="outfit_content">
<view class="outfit_title">详情
<image src="/static/images/close.png" @click="outfitClick()"></image>
</view>
<view class="outfit_module fa">
<image class="outfit_image"
:src="outfitList.image_url ? outfitList.image_url : '/static/images/replacement_image.png'"
mode="aspectFill">
</image>
<view class="outfit_outfit f1">
<view class="outfit_item fa">
<image src="/static/images/star.png"></image>
<view class="outfit_name">{{ outfitList.name ? outfitList.name : '-' }}</view>
<image src="/static/images/star.png"></image>
</view>
<view class="outfit_money fx">
{{ outfitList.price_gold ? outfitList.price_gold : '0' }}<text>金币</text></view>
</view>
</view>
<view class="outfit_btn faj" v-if="!isOutfitUnlocked(outfitList.id)" @click="shoppingClick()">购买服装
</view>
<view class="outfit_btn faj" v-if="isOutfitUnlocked(outfitList.id)">已拥有
</view>
</view>
</view>
<view class="outfit" v-if="outfitVoicesStats && tableIndex == 1">
<view class="outfit_content">
<view class="outfit_title">详情
<image src="/static/images/close.png" @click="outfitVoicesClick()"></image>
</view>
<view class="outfit_module fa">
<image class="outfit_images"
:src="outfitVoicesList.avatar_url ? global + outfitVoicesList.avatar_url : '/static/images/replacement_image.png'"
mode="aspectFill">
</image>
<view class="outfit_outfit f1">
<view class="outfit_item fa">
<image src="/static/images/star.png"></image>
<view class="outfit_name">{{ outfitVoicesList.name ? outfitVoicesList.name : '-' }}
</view>
<image src="/static/images/star.png"></image>
</view>
<view class="outfit_money fx">
{{ outfitVoicesList.price_gold ? outfitVoicesList.price_gold : '0' }}<text>金币</text>
</view>
</view>
</view>
<view class="outfit_btn faj" v-if="!isVoiceUnlocked(outfitVoicesList.id)"
@click="shoppingVoiceClick()">购买语音包
</view>
<view class="outfit_btn faj" v-if="isVoiceUnlocked(outfitVoicesList.id)">已拥有
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import {
OutfitMall,
OutfitVoicesMall,
OutfitVoicesPurchase,
OutfitPurchase,
} from '@/utils/api.js'
import notHave from '@/components/not-have.vue';
import topSafety from '@/components/top-safety.vue';
import tabBar from '@/components/tab-bar.vue';
export default {
components: {
notHave,
topSafety,
tabBar,
},
data() {
return {
global: this.baseURL,
tableList: [{
name: '服装',
id: 0
}, {
name: '语音包',
id: 1
},],
tableIndex: 0,
outfitMallList: '',
outfitVoicesMallList: '',
currentAudioContext: null,
outfitStats: false,
outfitList: '',
outfitVoicesStats: false,
outfitVoicesList: '',
}
},
onLoad() {
},
onShow() {
this.outfitMall()
this.outfitVoicesMall()
},
methods: {
// 新增判断服装是否已解锁的方法
isOutfitUnlocked(outfitId) {
if (!this.outfitMallList || !this.outfitMallList.owned_outfit_ids) {
return false;
}
return this.outfitMallList.owned_outfit_ids.includes(outfitId);
},
isVoiceUnlocked(voiceId) {
if (!this.outfitVoicesMallList || !this.outfitVoicesMallList.owned_voice_ids) {
return false;
}
return this.outfitVoicesMallList.owned_voice_ids.includes(voiceId);
},
outfitMall() {
OutfitMall({}).then(res => {
if (res.code == 1) {
this.outfitMallList = res.data
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
})
},
outfitVoicesMall() {
OutfitVoicesMall({}).then(res => {
if (res.code == 1) {
this.outfitVoicesMallList = res.data
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
})
},
outfitPurchase(id) {
OutfitPurchase({ item_id: id }).then(res => {
if (res.code == 1) {
uni.showToast({
title: '购买成功',
icon: 'none'
})
setTimeout(() => {
this.outfitStats = false
this.outfitList = ''
this.outfitMall()
}, 1000)
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
})
},
outfitVoicesPurchase(id) {
OutfitVoicesPurchase({ voice_id: id }).then(res => {
if (res.code == 1) {
uni.showToast({
title: '购买成功',
icon: 'none'
})
setTimeout(() => {
this.outfitVoicesStats = false
this.outfitVoicesList = ''
this.outfitVoicesMall()
}, 1000)
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
})
},
tableClick(index) {
this.tableIndex = index
if (index == 0) {
this.outfitMall()
} else {
this.outfitVoicesMall()
}
},
outfitClick(item) {
if (this.outfitStats) {
this.outfitStats = false
} else {
this.outfitStats = true
this.outfitList = item
}
},
outfitVoicesClick(item) {
if (this.outfitVoicesStats) {
this.outfitVoicesStats = false
} else {
this.outfitVoicesStats = true
this.outfitVoicesList = item
console.log(this.outfitVoicesList)
}
},
shoppingClick() {
this.outfitPurchase(this.outfitList.id)
},
shoppingVoiceClick() {
this.outfitVoicesPurchase(this.outfitVoicesList.id)
},
playVoice(url) {
// 如果当前有正在播放的音频,先停止它
if (this.currentAudioContext) {
this.currentAudioContext.stop();
this.currentAudioContext.destroy(); // 释放资源
}
// 创建新的音频上下文
this.currentAudioContext = uni.createInnerAudioContext();
this.currentAudioContext.src = this.global + url; // 设置音频源
// 播放音频
this.currentAudioContext.play();
// 监听播放结束事件,播放结束后清空当前音频上下文
this.currentAudioContext.onEnded(() => {
console.log('音频播放结束');
if (this.currentAudioContext) {
this.currentAudioContext.destroy();
this.currentAudioContext = null;
}
});
// 监听播放错误事件
this.currentAudioContext.onError((err) => {
console.error('音频播放失败:', err);
if (this.currentAudioContext) {
this.currentAudioContext.destroy();
this.currentAudioContext = null;
}
uni.showToast({
title: '播放失败',
icon: 'none'
});
});
},
back() {
uni.navigateBack({
delta: 1,
});
},
}
}
</script>
<style>
page {
background: #F7F7F7;
}
</style>
<style>
.page {
position: relative;
height: 100vh;
overflow: hidden;
}
.scroll-view {
overflow: hidden;
}
.body {
position: relative;
padding: 70rpx 0 0 0;
}
.back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 596rpx;
background: linear-gradient(180deg, #FFE3E9 0%, #F7F7F7 100%);
}
.list {
position: relative;
padding: 0 30rpx;
}
.list_back {
position: absolute;
left: 0;
top: 0;
width: 100%;
display: block;
}
.table {
position: relative;
padding: 0 0 10rpx 0;
border-radius: 20rpx 20rpx 0 0;
z-index: 2;
}
.table_item {
position: relative;
margin: 0 84rpx 0 0;
font-weight: 400;
font-size: 28rpx;
color: #9E9E9E;
line-height: 50rpx;
padding: 30rpx 30rpx 10rpx 30rpx;
}
.table_tag {
position: absolute;
left: 0;
right: 0;
bottom: 0;
margin: 0 auto;
width: 60rpx;
height: 6rpx;
background: #8449FE;
border-radius: 122rpx;
}
.table_active {
font-weight: 500;
font-size: 32rpx;
color: #8449FE;
}
.list_module {
position: relative;
margin: 50rpx 0 0 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-column-gap: 20rpx;
grid-row-gap: 20rpx;
}
.list_image {
width: 220rpx;
height: 300rpx;
border-radius: 12rpx;
display: block;
}
.list_images {
width: 210rpx;
height: 210rpx;
border-radius: 12rpx;
display: block;
}
.list_detail {
position: relative;
}
.list_voice {
position: absolute;
bottom: 0;
right: 0;
width: 60rpx;
height: 60rpx;
display: block;
z-index: 3;
}
.list_lock {
position: absolute;
right: 0;
left: 0;
top: 0;
bottom: 0;
margin: auto;
width: 36rpx;
height: 36rpx;
z-index: 2;
}
.outfit {
position: fixed;
width: 100%;
height: 100%;
left: 0;
bottom: 0;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.2);
z-index: 5;
}
.outfit_content {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(180deg, #EDD6F5 0%, #FFFFFF 100%);
border-radius: 40rpx 40rpx 0rpx 0rpx;
padding: 46rpx 40rpx 68rpx 40rpx;
}
.outfit_title {
position: relative;
font-weight: 500;
font-size: 36rpx;
color: #333333;
line-height: 50rpx;
text-align: center;
}
.outfit_title image {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 22rpx;
height: 22rpx;
margin: auto 0;
}
.outfit_module {
position: relative;
margin: 30rpx 0 0 0;
padding: 0 30rpx;
}
.outfit_image {
width: 260rpx;
height: 300rpx;
display: block;
flex-shrink: 0;
}
.outfit_images {
width: 260rpx;
height: 260rpx;
display: block;
flex-shrink: 0;
}
.outfit_outfit {
position: relative;
margin: 0 0 0 60rpx;
}
.outfit_item {
position: relative;
}
.outfit_item image {
width: 36rpx;
height: 48rpx;
}
.outfit_name {
margin: 0 28rpx;
font-weight: 500;
font-size: 32rpx;
color: #9779FA;
line-height: 50rpx;
}
.outfit_money {
margin: 12rpx 0 0 0;
font-weight: 700;
font-size: 44rpx;
color: #FF0000;
line-height: 50rpx;
}
.outfit_money text {
font-size: 28rpx;
line-height: 58rpx;
}
.outfit_btn {
position: relative;
margin: 162rpx 0 0 0;
padding: 26rpx 112rpx;
font-weight: 500;
font-size: 30rpx;
line-height: 50rpx;
border-radius: 12rpx;
color: #FFFFFF;
background: linear-gradient(135deg, #9F47FF 0%, #0053FA 100%), #D8D8D8;
}
</style>