257 lines
5.8 KiB
Vue
257 lines
5.8 KiB
Vue
<template>
|
||
<view class="starbook-content">
|
||
<!-- 背景图片 -->
|
||
<image class="background-image" src="/static/background/starbook.jpg" mode="aspectFill"></image>
|
||
|
||
<!-- 内容区域 -->
|
||
<view class="content-wrapper">
|
||
<!-- NFT卡片网格容器 -->
|
||
<view class="nft-grid-container">
|
||
<view
|
||
v-for="(item, index) in nftList"
|
||
:key="index"
|
||
class="nft-grid-item"
|
||
>
|
||
<NftCard
|
||
:cover-image="item.image"
|
||
:width="cardSize"
|
||
:height="cardSize"
|
||
:locked="item.locked"
|
||
:show-add-button="!item.locked && !item.image"
|
||
operation="place"
|
||
:custom-style="cardCustomStyle"
|
||
@add="handleAddNft"
|
||
@click="handleCardClick(item, index)"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted, onActivated, watch } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import NftCard from './NftCard.vue';
|
||
import { getMyAssetsApi } from '@/utils/api.js';
|
||
import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js';
|
||
|
||
// 屏幕宽度
|
||
const screenWidth = ref(0);
|
||
|
||
// 加载状态
|
||
const loading = ref(false);
|
||
|
||
// NFT列表(初始为15个卡片,将通过API填充)
|
||
const nftList = ref([]);
|
||
|
||
|
||
|
||
// 计算卡片尺寸
|
||
const cardSize = computed(() => {
|
||
if (screenWidth.value === 0) return 200;
|
||
// 每行3个卡片,留出padding和间距
|
||
// rpx转px:1rpx = screenWidth / 750
|
||
const rpxToPx = screenWidth.value / 750;
|
||
const padding = 40 * rpxToPx; // 左右各40rpx
|
||
const gap = 15 * rpxToPx; // 卡片间距15rpx(3列有2个间距)
|
||
// 可用宽度 = 屏幕宽度 - 左右padding - 2个间距
|
||
const availableWidth = screenWidth.value - (padding * 2) - (gap * 2);
|
||
return Math.floor(availableWidth / 3);
|
||
});
|
||
|
||
// 卡片自定义样式
|
||
const cardCustomStyle = {
|
||
position: 'absolute',
|
||
top: '0',
|
||
left: '0'
|
||
};
|
||
|
||
// 添加NFT处理 - 跳转到铸爱页面
|
||
const handleAddNft = () => {
|
||
// 跳转到铸爱商城页面
|
||
uni.navigateTo({
|
||
url: '/pages/castlove/mall'
|
||
});
|
||
};
|
||
|
||
// 加载藏品列表
|
||
const loadAssetsList = async () => {
|
||
loading.value = true;
|
||
try {
|
||
const response = await getMyAssetsApi(1, 20);
|
||
if (response.code === 200 && response.data && response.data.items) {
|
||
// 映射后端数据并解析封面URL
|
||
const assetsPromises = response.data.items.map(async item => {
|
||
const realCoverUrl = await getAssetCoverRealUrl(item.cover_url);
|
||
return {
|
||
asset_id: item.asset_id,
|
||
name: item.name,
|
||
image: realCoverUrl,
|
||
cover_url: item.cover_url || '/static/nft/collection.png',
|
||
tx_hash: item.tx_hash,
|
||
like_count: item.like_count,
|
||
status: item.status,
|
||
locked: false
|
||
};
|
||
});
|
||
|
||
const assets = await Promise.all(assetsPromises);
|
||
|
||
// 添加一个空白卡片用于添加新藏品
|
||
assets.push({ image: '', locked: false });
|
||
|
||
// 填充剩余位置为锁定卡片(总共15个卡片)
|
||
const totalCards = 15;
|
||
const remainingCards = totalCards - assets.length;
|
||
for (let i = 0; i < remainingCards; i++) {
|
||
assets.push({ image: '', locked: true });
|
||
}
|
||
|
||
nftList.value = assets;
|
||
}
|
||
} catch (error) {
|
||
console.error('获取藏品列表失败:', error);
|
||
uni.showToast({
|
||
title: error.message || '获取藏品列表失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
|
||
// 失败时使用默认布局(1个空白卡片 + 14个锁定卡片)
|
||
const defaultList = [{ image: '', locked: false }];
|
||
for (let i = 0; i < 14; i++) {
|
||
defaultList.push({ image: '', locked: true });
|
||
}
|
||
nftList.value = defaultList;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 点击卡片跳转到详情页
|
||
const handleCardClick = (item) => {
|
||
if (item.image && !item.locked && item.asset_id) {
|
||
uni.navigateTo({
|
||
url: `/pages/asset-detail/asset-detail?asset_id=${item.asset_id}`
|
||
});
|
||
}
|
||
};
|
||
|
||
// 定义 props 用于接收父组件的显示状态
|
||
const props = defineProps({
|
||
isActive: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
});
|
||
|
||
onMounted(() => {
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
screenWidth.value = systemInfo.windowWidth;
|
||
|
||
// 加载藏品列表
|
||
loadAssetsList();
|
||
});
|
||
|
||
// 每次组件激活时重新加载数据(keep-alive场景)
|
||
onActivated(() => {
|
||
loadAssetsList();
|
||
});
|
||
|
||
// 监听页面显示事件(页面级切换场景)
|
||
onShow(() => {
|
||
loadAssetsList();
|
||
});
|
||
|
||
// 监听 isActive prop 变化(tab切换场景)
|
||
watch(() => props.isActive, (newValue, oldValue) => {
|
||
if (newValue && !oldValue) {
|
||
loadAssetsList();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.starbook-content {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 1;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
background: #0d0820;
|
||
}
|
||
|
||
/* 滚动条样式 - 自动隐藏 */
|
||
.starbook-content::-webkit-scrollbar {
|
||
width: 6rpx;
|
||
}
|
||
|
||
.starbook-content::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.starbook-content::-webkit-scrollbar-thumb {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
border-radius: 10rpx;
|
||
transition: background 0.3s ease;
|
||
}
|
||
|
||
.starbook-content::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
.background-image {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 0;
|
||
object-fit: cover;
|
||
min-width: 100%;
|
||
min-height: 100%;
|
||
}
|
||
|
||
.content-wrapper {
|
||
position: relative;
|
||
z-index: 1;
|
||
width: 100%;
|
||
min-height: 100%;
|
||
padding: 250rpx 30rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* NFT网格容器 */
|
||
.nft-grid-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
/* 列间距(与计算逻辑保持一致) */
|
||
column-gap: 15rpx;
|
||
/* 行间距 */
|
||
row-gap: 10rpx;
|
||
width: 100%;
|
||
max-width: 100%;
|
||
/* 确保网格项靠上对齐 */
|
||
align-items: start;
|
||
justify-items: center;
|
||
}
|
||
|
||
.nft-grid-item {
|
||
position: relative;
|
||
width: 100%;
|
||
/* 保持3:4比例 */
|
||
padding-top: 133.33%;
|
||
/* 确保内容靠上对齐 */
|
||
display: block;
|
||
}
|
||
</style>
|