288 lines
6.0 KiB
Vue
288 lines
6.0 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<Header :showBack="true" backIconColor="#e6e6e6" :title="pageTitle" />
|
||
|
||
<!-- 加载中 -->
|
||
<view v-if="loading && page === 1" class="loading-container">
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view v-else-if="!hasData" class="empty-container">
|
||
<text class="empty-text">暂无藏品</text>
|
||
</view>
|
||
|
||
<!-- 藏品网格 -->
|
||
<view v-else class="nft-grid-container">
|
||
<view
|
||
v-for="item in items"
|
||
:key="item.asset_id"
|
||
class="nft-grid-item"
|
||
@click="handleCardClick(item)"
|
||
>
|
||
<NftCard
|
||
:cover-image="item.cover_url_signed"
|
||
:width="cardSize"
|
||
:height="cardSize"
|
||
:locked="false"
|
||
:custom-style="cardCustomStyle"
|
||
/>
|
||
<view class="nft-info">
|
||
<text class="nft-name">{{ item.name }}</text>
|
||
<text class="nft-likes">★{{ item.like_count }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 分页提示 -->
|
||
<view v-if="hasMore && !loading" class="load-more" @click="loadMore">
|
||
<text class="load-more-text">加载更多</text>
|
||
</view>
|
||
|
||
<!-- 加载中(加载更多) -->
|
||
<view v-if="loading && page > 1" class="loading-more">
|
||
<text class="loading-more-text">加载中...</text>
|
||
</view>
|
||
|
||
<!-- 没有更多了 -->
|
||
<view v-if="!hasMore && items.length > 0" class="no-more">
|
||
<text class="no-more-text">— 没有更多了 —</text>
|
||
</view>
|
||
|
||
<BottomNav :activeTab="1" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import Header from "../components/Header.vue";
|
||
import BottomNav from "../components/BottomNav.vue";
|
||
import NftCard from "../components/NftCard.vue";
|
||
import { getStarbookItemsApi } from '@/utils/api.js';
|
||
|
||
// 屏幕宽度
|
||
const screenWidth = ref(0);
|
||
|
||
// 路由参数
|
||
const type = ref('regular');
|
||
const category = ref('castlove');
|
||
const grade = ref(null);
|
||
|
||
// 分页参数
|
||
const page = ref(1);
|
||
const pageSize = ref(20);
|
||
const total = ref(0);
|
||
|
||
// 藏品列表
|
||
const items = ref([]);
|
||
|
||
// 加载状态
|
||
const loading = ref(false);
|
||
|
||
// 计算卡片尺寸
|
||
const cardSize = computed(() => {
|
||
if (screenWidth.value === 0) return 200;
|
||
const rpxToPx = screenWidth.value / 750;
|
||
const padding = 30 * rpxToPx; // 左右各30rpx
|
||
const gap = 15 * rpxToPx; // 卡片间距15rpx(3列有2个间距)
|
||
const availableWidth = screenWidth.value - (padding * 2) - (gap * 2);
|
||
return Math.floor(availableWidth / 3);
|
||
});
|
||
|
||
// 卡片自定义样式
|
||
const cardCustomStyle = {
|
||
position: 'absolute',
|
||
top: '0',
|
||
left: '0'
|
||
};
|
||
|
||
// 页面标题
|
||
const pageTitle = computed(() => {
|
||
if (type.value === 'regular') {
|
||
return `普通 · ${formatGrade(grade.value)}`;
|
||
}
|
||
return category.value;
|
||
});
|
||
|
||
// 判断是否有数据
|
||
const hasData = computed(() => {
|
||
return items.value.length > 0;
|
||
});
|
||
|
||
// 判断是否有更多
|
||
const hasMore = computed(() => {
|
||
return items.value.length < total.value;
|
||
});
|
||
|
||
// grade 中文转换
|
||
const gradeMap = { 1: '一', 2: '二', 3: '三', 4: '四', 5: '五' };
|
||
function formatGrade(g) {
|
||
return `等级${gradeMap[g] || g}`;
|
||
}
|
||
|
||
// 加载数据
|
||
const loadData = async (append = false) => {
|
||
loading.value = true;
|
||
try {
|
||
const response = await getStarbookItemsApi(
|
||
type.value,
|
||
category.value,
|
||
grade.value,
|
||
page.value,
|
||
pageSize.value
|
||
);
|
||
if (response.code === 200 && response.data) {
|
||
if (append) {
|
||
items.value = [...items.value, ...(response.data.items || [])];
|
||
} else {
|
||
items.value = response.data.items || [];
|
||
}
|
||
total.value = response.data.total || 0;
|
||
}
|
||
} catch (error) {
|
||
console.error('获取藏品列表失败:', error);
|
||
uni.showToast({
|
||
title: error.message || '获取藏品列表失败',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 加载更多
|
||
const loadMore = () => {
|
||
if (!hasMore.value || loading.value) return;
|
||
page.value++;
|
||
loadData(true);
|
||
};
|
||
|
||
// 点击卡片跳转到详情页
|
||
const handleCardClick = (item) => {
|
||
if (item.asset_id) {
|
||
uni.navigateTo({
|
||
url: `/pages/asset-detail/asset-detail?asset_id=${item.asset_id}`
|
||
});
|
||
}
|
||
};
|
||
|
||
// 监听页面滚动到底部
|
||
onMounted(() => {
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
screenWidth.value = systemInfo.windowWidth;
|
||
|
||
// 获取路由参数
|
||
const pages = getCurrentPages();
|
||
const currentPage = pages[pages.length - 1];
|
||
const options = currentPage.options || currentPage.$page?.options || {};
|
||
|
||
type.value = options.type || 'regular';
|
||
category.value = options.category || 'castlove';
|
||
grade.value = options.grade ? parseInt(options.grade) : null;
|
||
page.value = parseInt(options.page) || 1;
|
||
|
||
// 加载数据
|
||
loadData();
|
||
});
|
||
|
||
// 触底加载更多
|
||
onReachBottom(() => {
|
||
if (hasMore.value && !loading.value) {
|
||
loadMore();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page-container {
|
||
position: relative;
|
||
width: 100vw;
|
||
min-height: 100vh;
|
||
overflow: hidden;
|
||
background: #0d0820;
|
||
}
|
||
|
||
.loading-container,
|
||
.empty-container {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding-top: 300rpx;
|
||
}
|
||
|
||
.loading-text,
|
||
.empty-text {
|
||
color: rgba(255, 255, 255, 0.6);
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
/* 藏品网格容器 */
|
||
.nft-grid-container {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
column-gap: 15rpx;
|
||
row-gap: 20rpx;
|
||
padding: 250rpx 30rpx 180rpx;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.nft-grid-item {
|
||
position: relative;
|
||
width: 100%;
|
||
padding-top: 133.33%;
|
||
}
|
||
|
||
.nft-grid-item:active {
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
/* 藏品信息 */
|
||
.nft-info {
|
||
padding: 10rpx 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.nft-name {
|
||
display: block;
|
||
font-size: 22rpx;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.nft-likes {
|
||
font-size: 20rpx;
|
||
color: rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
/* 加载更多 */
|
||
.load-more {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.load-more-text {
|
||
font-size: 26rpx;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.loading-more,
|
||
.no-more {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.loading-more-text,
|
||
.no-more-text {
|
||
font-size: 24rpx;
|
||
color: rgba(255, 255, 255, 0.4);
|
||
}
|
||
</style>
|