topfans/frontend/pages/dashboard/components/LikeIncomeBoard.vue

351 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="like-income-board">
<text class="section-title">点赞收益看板</text>
<image
class="chart-bg"
src="/static/dashboard/liked-bj.png"
mode="scaleToFill"
/>
<!-- 错误/骨架态 -->
<view v-if="error" class="error-box" @tap="$emit('retry')">
<text class="error-text">加载失败,点击重试</text>
</view>
<view v-else-if="loading || !stats" class="skeleton-board">
<view class="skeleton-stats"></view>
<view class="skeleton-list"></view>
</view>
<!-- 正常态 -->
<view v-else class="board-row">
<!-- 左侧统计 -->
<view class="left-stats">
<view class="stat-block">
<text class="stat-num">{{ stats.total_like_count }}</text>
<text class="stat-text">累积点赞</text>
</view>
<view class="stat-block">
<text class="stat-num">{{ stats.total_income }}</text>
<text class="stat-text">累计收益</text>
</view>
</view>
<!-- 右侧等级列表 -->
<view class="right-list">
<view class="level-header">
<text class="th th-thumb">藏品</text>
<text class="th th-name">等级</text>
<text class="th th-income">累计收益</text>
</view>
<view v-for="(item, idx) in levels" :key="idx" class="level-row">
<view class="level-thumb">
<image
v-if="item.thumb"
class="thumb-asset-img"
:src="item.thumb"
mode="aspectFill"
/>
<text v-else class="thumb-emoji">🎨</text>
</view>
<view class="level-name">
<image
class="level-badge-img"
:src="getGradeBadge(item.level)"
mode="aspectFit"
/>
</view>
<text class="level-income">{{ item.total_income }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
defineProps({
stats: { type: Object, default: null }, // { total_like_count, total_income }
levels: { type: Array, default: () => [] },
loading: { type: Boolean, default: false },
error: { type: String, default: null },
});
defineEmits(["retry"]);
// 等级徽章图片映射item.level 是字符串 ('UR'/'SSR'/'SR'/'R'/'N')
// 注意 UR 的文件名是 URengji.png没有 d与其他不同
const GRADE_BADGE_MAP = {
N: "/static/starbookcontent/grade/Ndengji.png",
R: "/static/starbookcontent/grade/Rdengji.png",
SR: "/static/starbookcontent/grade/SRdengji.png",
SSR: "/static/starbookcontent/grade/SSRdengji.png",
UR: "/static/starbookcontent/grade/URengji.png",
};
const getGradeBadge = (level) => GRADE_BADGE_MAP[level] || GRADE_BADGE_MAP.N;
</script>
<style lang="scss" scoped>
.like-income-board {
background:
linear-gradient(
106.77deg,
rgba(255, 223, 119, 0.13) -9.76%,
rgba(132, 255, 210, 0.13) 44.65%,
rgba(255, 129, 131, 0.13) 117.82%
),
linear-gradient(0deg, rgba(249, 69, 69, 0.5), rgba(249, 69, 69, 0.5));
// bj.png 移到 ::before单独控制 opacity
border-top-left-radius: 17px;
border-top-right-radius: 14px;
border-bottom-right-radius: 14px;
border-bottom-left-radius: 14px;
opacity: 1;
position: relative;
padding: 12rpx;
margin: 12rpx 0;
box-shadow: 0px 4px 4px 0px rgba(189, 50, 50, 0.25);
overflow: hidden;
// [方案3] 伪元素承载 bj.png对图片单独设 opacity
&::before {
content: "";
position: absolute;
inset: 0;
background: url("/static/dashboard/bj.png") center / cover no-repeat;
opacity: 0.2; // ⬅ 调这个数控制图片透明度0=完全透明1=完全不透明)
pointer-events: none;
z-index: 0;
}
}
// 所有子内容上浮到 ::before 之上
.board-row,
.error-box,
.skeleton-board {
position: relative;
z-index: 1;
}
.chart-bg {
position: absolute;
top: -24rpx;
left: -16rpx;
width: 224rpx;
height: 224rpx;
z-index: 1; // 在 ::before(z:0) 之上,在标题(z:2) 和内容(z:1) 之间
pointer-events: none; // 不拦截 tap事件穿透到 canvas
opacity: 0.44;
// transform: rotate(60deg);
}
.section-title {
display: block;
font-size: 36rpx;
font-weight: 700;
color: #ffffff;
margin-bottom: 24rpx;
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.3);
position: relative;
z-index: 2;
}
.board-row {
display: flex;
gap: 16rpx;
}
.left-stats {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 24rpx;
padding-right: 16rpx;
border-right: 1px solid rgba(255, 255, 255, 0.1);
}
.stat-block {
min-width: 264rpx;
height: 98rpx;
width: 147;
border-top-left-radius: 14px;
border-top-right-radius: 12px;
border-bottom-right-radius: 12px;
border-bottom-left-radius: 12px;
position: relative;
box-sizing: border-box;
padding: 8rpx 16rpx;
background: linear-gradient(
106.77deg,
rgba(255, 223, 119, 0.43) -9.76%,
rgba(185, 132, 255, 0.43) 44.65%,
rgba(255, 129, 131, 0.43) 117.82%
);
box-shadow: 0px 4px 4px 0px rgba(189, 50, 50, 0.25);
}
.stat-num {
position: absolute;
bottom: 4rpx;
right: 16rpx;
font-size: 48rpx;
font-weight: 700;
color: #fffabd;
font-family: "Baloo Bhai", sans-serif;
text-shadow: -1px 1px 4px rgba(206, 9, 9, 0.84);
}
.stat-text {
position: absolute;
top: 6rpx;
left: 16rpx;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.85);
}
.right-list {
width: 320rpx;
display: flex;
flex-direction: column;
gap: 12rpx;
}
// 表头行:列宽 / gap / padding 与 .level-row 对齐
.level-header {
display: flex;
align-items: center;
padding: 4rpx 12rpx;
gap: 16rpx;
}
.th {
font-size: 20rpx;
font-weight: 500;
color: rgba(255, 255, 255, 0.7);
text-shadow: 0px 0px 4px rgba(164, 60, 60, 1);
display: flex;
justify-content: center;
}
.th-thumb {
width: 56rpx;
flex-shrink: 0; // 占位,对齐 .level-thumb
text-align: center;
}
.th-name {
flex: 0 0 100rpx; // 对齐 .level-name
text-align: center;
}
.th-income {
flex: 1; // 对齐 .level-income
text-align: center; // 跟数据行一起居中
}
.level-row {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.06);
border-radius: 12rpx;
padding: 12rpx;
gap: 16rpx;
}
.level-thumb {
width: 40rpx;
height: 56rpx;
flex-shrink: 0;
border-radius: 6rpx;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 2px 2px 4.5px 2px rgba(230, 46, 46, 0.46);
transform: rotate(-10deg);
// backdrop-filter: blur(0px);
}
// 藏品缩略图:填满 .level-thumb 容器
.thumb-asset-img {
width: 100%;
height: 100%;
display: block;
}
// 空数据兜底 emoji
.thumb-emoji {
font-size: 28rpx;
}
.level-name {
flex: 0 0 96rpx; // 与 .th-name 同宽
display: flex;
align-items: center;
justify-content: flex-end; // 徽章图水平居中
}
// 等级徽章图:保持比例,限制最大尺寸贴合行高
.level-badge-img {
width: 64rpx;
height: 64rpx;
}
.level-income {
flex: 0 0 120rpx;
background: linear-gradient(
90deg,
rgba(27, 175, 238, 0.19) 0%,
rgba(255, 204, 20, 0.19) 100%
);
font-size: 28rpx;
font-weight: 700;
border-radius: 7px;
color: #fffabd;
font-family: "Baloo Bhai", sans-serif;
text-align: center; // 居中
}
/* 骨架/错误 */
.skeleton-board {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.skeleton-stats,
.skeleton-list {
height: 200rpx;
border-radius: 17rpx;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0.05) 25%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.05) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
.error-box {
height: 240rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 100, 100, 0.15);
border: 2rpx solid rgba(255, 100, 100, 0.4);
border-radius: 17rpx;
}
.error-text {
color: #ff8080;
font-size: 28rpx;
}
@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
</style>