255 lines
5.1 KiB
Vue
255 lines
5.1 KiB
Vue
<template>
|
||
<!-- 只渲染背景图,卡片由父组件 BannerCarousel 渲染在 swiper 外层 -->
|
||
<view class="banner-top3-bg" >
|
||
<image class="banner-bg" src="/static/square/paihangbang.png" mode="aspectFill" />
|
||
|
||
<!-- 卡片层 -->
|
||
<view class="cards-overlay" @click.stop="$emit('top3Click')">
|
||
<view
|
||
v-for="(item, index) in top3Items"
|
||
:key="item.asset_id || index"
|
||
class="card-wrapper"
|
||
:class="`card-pos-${index}`"
|
||
>
|
||
<view class="card-frame">
|
||
<view class="card-image-wrap">
|
||
<image
|
||
class="card-image"
|
||
:src="item.cover_url || '/static/avatar/1.jpeg'"
|
||
mode="aspectFill"
|
||
/>
|
||
<!-- 点赞数叠在图片底部 -->
|
||
<view class="card-footer">
|
||
<image class="card-heart" src="/static/icon/heart-icon.png" mode="aspectFit" />
|
||
<view class="card-likes-wrap">
|
||
<text class="card-likes">{{ formatLikes(item.like_count) }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 边框叠在整张卡片上 -->
|
||
<image class="card-frame-border" src="/static/square/gerenzhongxincangpinkuang.png" mode="aspectFill" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 骨架屏 -->
|
||
<view v-if="loading" class="skeleton-wrap">
|
||
<view v-for="i in 3" :key="i" class="skeleton-card" :class="`skeleton-pos-${i - 1}`" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, onUnmounted } from 'vue';
|
||
import { getHotRankingApi } from '@/utils/api.js';
|
||
|
||
const emit = defineEmits(['dataLoaded', 'top3Click']);
|
||
|
||
const top3Items = ref([]);
|
||
const loading = ref(true);
|
||
|
||
const resolveOssUrl = async (fileName, type) => {
|
||
if (!fileName) return '';
|
||
// 直接使用后端返回的图片URL
|
||
return fileName;
|
||
};
|
||
|
||
const formatLikes = (n) => {
|
||
if (!n || isNaN(n)) return '0';
|
||
if (n >= 10000) return (n / 10000).toFixed(1) + 'w';
|
||
if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
|
||
return String(n);
|
||
};
|
||
|
||
const loadTop3 = async () => {
|
||
try {
|
||
const res = await getHotRankingApi('total', null, 1, 3);
|
||
if (res.code === 200 && res.data?.items) {
|
||
const items = res.data.items.slice(0, 3);
|
||
const resolved = items.map(item => {
|
||
return { ...item, cover_url: item.cover_url || '', avatar_url: item.avatar_url || '' };
|
||
});
|
||
top3Items.value = resolved;
|
||
loading.value = false;
|
||
emit('dataLoaded', resolved);
|
||
}
|
||
} catch (e) {
|
||
console.error('[BannerTop3] 加载失败', e?.message ?? e);
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// const onBannerTap = () => {
|
||
// uni.navigateTo({ url: '/pages/profile/hisWorks?userId=1&nickname=用户' });
|
||
// };
|
||
|
||
onMounted(loadTop3);
|
||
|
||
// 监听身份切换事件,切换后刷新排行榜数据
|
||
uni.$on('userInfoUpdated', () => {
|
||
loadTop3();
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
uni.$off('userInfoUpdated');
|
||
});
|
||
|
||
defineExpose({ reload: loadTop3 });
|
||
</script>
|
||
|
||
<style scoped>
|
||
.banner-top3-bg {
|
||
width: 100%;
|
||
height: 360rpx;
|
||
position: relative;
|
||
/* overflow: hidden; */
|
||
border-radius: 24rpx;
|
||
}
|
||
|
||
.banner-bg {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
/* 卡片层 */
|
||
.cards-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 8rpx;
|
||
width: 420rpx;
|
||
height: 360rpx;
|
||
pointer-events: auto;
|
||
z-index: 10;
|
||
}
|
||
|
||
.card-wrapper {
|
||
position: absolute;
|
||
width: 148rpx;
|
||
height: 220rpx;
|
||
}
|
||
|
||
.card-pos-0 {
|
||
left: 50rpx;
|
||
top: 40rpx;
|
||
transform: rotate(-6deg);
|
||
z-index: 3;
|
||
}
|
||
.card-pos-1 {
|
||
left: 140rpx;
|
||
top: -8rpx;
|
||
transform: rotate(6deg);
|
||
z-index: 4;
|
||
}
|
||
.card-pos-2 {
|
||
left: 240rpx;
|
||
top: 106rpx;
|
||
transform: rotate(16deg);
|
||
z-index: 5;
|
||
}
|
||
|
||
.card-frame {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 16rpx;
|
||
position: relative;
|
||
overflow: visible;
|
||
}
|
||
|
||
.card-image-wrap {
|
||
width: 90%;
|
||
height: 92%;
|
||
position: relative;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
z-index: 5;
|
||
padding: 8rpx;
|
||
}
|
||
|
||
.card-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.card-frame-border {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 2;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.card-footer {
|
||
position: absolute;
|
||
bottom: 8rpx;
|
||
left: 0;
|
||
right: 64rpx;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 4rpx;
|
||
z-index: 3;
|
||
}
|
||
|
||
.card-heart {
|
||
width: 22rpx;
|
||
height: 22rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.card-likes-wrap {
|
||
background: linear-gradient(to bottom right,
|
||
#F0E4B1 0%,
|
||
#F08399 50%,
|
||
#B94E73 100%
|
||
);
|
||
border-radius: 8rpx;
|
||
padding: 2rpx 8rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow:
|
||
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
|
||
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4);
|
||
}
|
||
|
||
.card-likes {
|
||
font-size: 12rpx;
|
||
color: #ffffff;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* 骨架屏 */
|
||
.skeleton-wrap {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 5;
|
||
}
|
||
|
||
.skeleton-card {
|
||
position: absolute;
|
||
width: 148rpx;
|
||
height: 220rpx;
|
||
border-radius: 16rpx;
|
||
background: linear-gradient(90deg,
|
||
rgba(255,255,255,0.06) 25%,
|
||
rgba(255,255,255,0.14) 50%,
|
||
rgba(255,255,255,0.06) 75%);
|
||
background-size: 200% 100%;
|
||
animation: shimmer 1.4s infinite;
|
||
}
|
||
|
||
.skeleton-pos-0 { left: 50rpx; top: 40rpx; transform: rotate(-6deg); }
|
||
.skeleton-pos-1 { left: 140rpx; top: -40rpx; transform: rotate(6deg); }
|
||
.skeleton-pos-2 { left: 240rpx; top: 10rpx; transform: rotate(16deg); }
|
||
|
||
@keyframes shimmer {
|
||
0% { background-position: 200% 0; }
|
||
100% { background-position: -200% 0; }
|
||
}
|
||
</style>
|