h5周星优化

This commit is contained in:
170-carry 2026-04-30 23:13:48 +08:00
parent bb0a31f601
commit 89534f7bf3

View File

@ -62,8 +62,9 @@
} }
.page { .page {
width: min(100%, var(--page-width)); width: 100%;
min-height: 100vh; min-height: 100vh;
min-height: 100dvh;
margin: 0 auto; margin: 0 auto;
background: linear-gradient(180deg, #163a07 0%, #123708 38%, #0d2f08 100%); background: linear-gradient(180deg, #163a07 0%, #123708 38%, #0d2f08 100%);
overflow: hidden; overflow: hidden;
@ -113,7 +114,7 @@
width: min(72%, 230px); width: min(72%, 230px);
max-width: calc(100% - 44px); max-width: calc(100% - 44px);
aspect-ratio: 665 / 88; aspect-ratio: 665 / 88;
padding: 0 24px 6px; padding: 0 24px 2px;
background: center / 100% 100% no-repeat var(--subtitle-frame-image); background: center / 100% 100% no-repeat var(--subtitle-frame-image);
color: #f8e29b; color: #f8e29b;
font-size: 13px; font-size: 13px;
@ -158,6 +159,22 @@
object-fit: contain; object-fit: contain;
} }
.gift-card.gift-skeleton .gift-inner {
position: relative;
overflow: hidden;
border-radius: 16px;
}
.gift-card.gift-skeleton .gift-inner::before {
content: "";
width: 70%;
aspect-ratio: 1;
border-radius: 24px;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 248, 198, 0.35) 48%, rgba(255, 255, 255, 0.12) 100%);
background-size: 220% 100%;
animation: rank-skeleton-loading 1.1s ease-in-out infinite;
}
.gift-empty { .gift-empty {
opacity: 0.3; opacity: 0.3;
} }
@ -165,14 +182,16 @@
.tab-row { .tab-row {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px; gap: 12px;
padding: 14px 15px 0; padding: 12px 17px 14px;
} }
.tab-button { .tab-button {
position: relative; position: relative;
display: block;
width: 100%; width: 100%;
aspect-ratio: 336 / 126; aspect-ratio: 336 / 108;
padding: 0;
border: 0; border: 0;
appearance: none; appearance: none;
background: none; background: none;
@ -191,8 +210,8 @@
.tab-button.active::before { .tab-button.active::before {
background-image: var(--tab-active-image); background-image: var(--tab-active-image);
width: 103.4%; width: 103.57%;
height: 109%; height: 108.73%;
} }
.tab-button span { .tab-button span {
@ -216,15 +235,7 @@
.tab-button.active span { .tab-button.active span {
color: #c15f17; color: #c15f17;
} transform: translate(-50%, calc(-50% + 1px));
.period-strip {
padding: 18px 18px 16px;
color: #fff8c6;
text-align: center;
font-size: 18px;
line-height: 1.1;
font-weight: 700;
} }
.rank-stage { .rank-stage {
@ -271,6 +282,48 @@
color: transparent; color: transparent;
} }
.rank-row.skeleton {
color: transparent;
}
.rank-row.skeleton .rank-mark,
.rank-row.skeleton .rank-avatar,
.rank-row.skeleton .rank-name,
.rank-row.skeleton .rank-score {
position: relative;
overflow: hidden;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 248, 198, 0.28) 48%, rgba(255, 255, 255, 0.1) 100%);
background-size: 220% 100%;
animation: rank-skeleton-loading 1.1s ease-in-out infinite;
}
.rank-row.skeleton .rank-mark {
height: 18px;
border-radius: 999px;
}
.rank-row.skeleton .rank-name {
height: 16px;
max-width: 54%;
border-radius: 999px;
}
.rank-row.skeleton .rank-score {
width: 48px;
height: 16px;
border-radius: 999px;
}
@keyframes rank-skeleton-loading {
0% {
background-position: 120% 0;
}
100% {
background-position: -120% 0;
}
}
.rank-mark { .rank-mark {
width: 30px; width: 30px;
flex: 0 0 30px; flex: 0 0 30px;
@ -485,9 +538,10 @@
<div class="gift-grid"> <div class="gift-grid">
<template x-for="gift in giftSlots()" :key="gift.key"> <template x-for="gift in giftSlots()" :key="gift.key">
<div class="gift-card" :class="{ 'gift-empty': gift.placeholder }" :title="giftTitle(gift)"> <div class="gift-card" :class="{ 'gift-empty': gift.placeholder, 'gift-skeleton': gift.skeleton }"
:title="giftTitle(gift)">
<div class="gift-inner"> <div class="gift-inner">
<template x-if="!gift.placeholder"> <template x-if="!gift.placeholder && !gift.skeleton">
<img :src="giftCover(gift)" :alt="giftTitle(gift)" /> <img :src="giftCover(gift)" :alt="giftTitle(gift)" />
</template> </template>
</div> </div>
@ -497,37 +551,33 @@
<nav class="tab-row"> <nav class="tab-row">
<button type="button" class="tab-button" :class="{ active: activeTab === 'last' }" @click="activeTab = 'last'"> <button type="button" class="tab-button" :class="{ active: activeTab === 'last' }" @click="activeTab = 'last'">
<span>Previous Top 3 Records</span> <span>Previous Top 3</span>
</button> </button>
<button type="button" class="tab-button" :class="{ active: activeTab === 'current' }" <button type="button" class="tab-button" :class="{ active: activeTab === 'current' }"
@click="activeTab = 'current'"> @click="activeTab = 'current'">
<span>This Week's rank list</span> <span>This Week</span>
</button> </button>
<button type="button" class="tab-button" :class="{ active: activeTab === 'reward' }" <button type="button" class="tab-button" :class="{ active: activeTab === 'reward' }"
@click="activeTab = 'reward'"> @click="activeTab = 'reward'">
<span>Reward Description</span> <span>Reward</span>
</button> </button>
</nav> </nav>
</section> </section>
<template x-if="activeTab !== 'reward'">
<div class="period-strip" x-text="activePeriodText()"></div>
</template>
<section class="rank-stage" x-show="activeTab !== 'reward'"> <section class="rank-stage" x-show="activeTab !== 'reward'">
<ul class="rank-list"> <ul class="rank-list">
<template x-for="(item, index) in displayRows()" :key="item.key"> <template x-for="(item, index) in displayRows()" :key="item.key">
<li class="rank-row" :class="rowClass(index, item)"> <li class="rank-row" :class="rowClass(index, item)">
<div class="rank-mark"> <div class="rank-mark">
<template x-if="isTopRank(item.rank)"> <template x-if="!item.skeleton && isTopRank(item.rank)">
<img class="rank-badge-icon" :src="topRankIcon(item.rank)" :alt="'Top ' + item.rank" /> <img class="rank-badge-icon" :src="topRankIcon(item.rank)" :alt="'Top ' + item.rank" />
</template> </template>
<template x-if="!isTopRank(item.rank)"> <template x-if="!item.skeleton && !isTopRank(item.rank)">
<span class="plain" x-text="item.rank"></span> <span class="plain" x-text="item.rank"></span>
</template> </template>
</div> </div>
<div class="rank-avatar"> <div class="rank-avatar">
<template x-if="!item.placeholder"> <template x-if="!item.placeholder && !item.skeleton">
<img :src="avatarOf(item)" :alt="item.userNickname || 'avatar'" /> <img :src="avatarOf(item)" :alt="item.userNickname || 'avatar'" />
</template> </template>
</div> </div>
@ -537,11 +587,11 @@
</template> </template>
</ul> </ul>
<div class="empty-message" x-show="!hasRealRows() && !error"> <div class="empty-message" x-show="!loading && !hasRealRows() && !error">
No ranking data yet. Send the selected gifts and the leaderboard will update No ranking data yet. Send the selected gifts and the leaderboard will update
here. here.
</div> </div>
<div class="empty-message" x-show="error" x-text="error"></div> <div class="empty-message" x-show="!loading && error" x-text="error"></div>
</section> </section>
<section class="reward-stage" x-show="activeTab === 'reward'"> <section class="reward-stage" x-show="activeTab === 'reward'">
@ -602,16 +652,10 @@
function weekStarPage() { function weekStarPage() {
return { return {
activeTab: "last", activeTab: "current",
data: normalizePageData({}), data: normalizePageData({}),
error: "", error: "",
loading: true,
activePeriodText() {
if (this.activeTab === "last") {
return this.data.lastPeriodText || this.data.currentPeriodText || "--";
}
return this.data.currentPeriodText || this.data.lastPeriodText || "--";
},
avatarOf(item) { avatarOf(item) {
return item && item.userAvatar ? item.userAvatar : DEFAULT_AVATAR; return item && item.userAvatar ? item.userAvatar : DEFAULT_AVATAR;
@ -638,6 +682,9 @@
}, },
displayRows() { displayRows() {
if (this.loading) {
return this.skeletonRows();
}
if (this.activeTab === "current") { if (this.activeTab === "current") {
return this.buildRows(this.data.thisWeekTop10, 5, 10); return this.buildRows(this.data.thisWeekTop10, 5, 10);
} }
@ -646,6 +693,7 @@
async fetchPage() { async fetchPage() {
this.error = ""; this.error = "";
this.loading = true;
try { try {
const url = new URL("/go/public/h5/week-star/page", resolveWeekStarAPIBase()); const url = new URL("/go/public/h5/week-star/page", resolveWeekStarAPIBase());
const sysOrigin = new URLSearchParams(window.location.search).get("sysOrigin"); const sysOrigin = new URLSearchParams(window.location.search).get("sysOrigin");
@ -674,6 +722,8 @@
? error.message ? error.message
: "Please retry later."; : "Please retry later.";
this.data = normalizePageData({}); this.data = normalizePageData({});
} finally {
this.loading = false;
} }
}, },
@ -682,6 +732,12 @@
}, },
giftSlots() { giftSlots() {
if (this.loading) {
return Array.from({ length: 3 }, (_, index) => ({
key: "gift-skeleton-" + index,
skeleton: true,
}));
}
const slots = (this.data.qualifyingGifts || []).slice(0, 3).map((gift, index) => { const slots = (this.data.qualifyingGifts || []).slice(0, 3).map((gift, index) => {
return { return {
...gift, ...gift,
@ -699,6 +755,9 @@
}, },
giftTitle(gift) { giftTitle(gift) {
if (gift && gift.skeleton) {
return "Loading gift";
}
if (!gift || gift.placeholder) { if (!gift || gift.placeholder) {
return "Gift"; return "Gift";
} }
@ -706,7 +765,7 @@
}, },
hasRealRows() { hasRealRows() {
return this.displayRows().some((item) => !item.placeholder); return !this.loading && this.displayRows().some((item) => !item.placeholder && !item.skeleton);
}, },
heroPeriodText() { heroPeriodText() {
@ -761,22 +820,34 @@
rowClass(index, item) { rowClass(index, item) {
const stripe = index % 2 === 0 ? "light" : "dark"; const stripe = index % 2 === 0 ? "light" : "dark";
if (item && item.skeleton) {
return stripe + " skeleton";
}
return item.placeholder ? stripe + " placeholder" : stripe; return item.placeholder ? stripe + " placeholder" : stripe;
}, },
rowName(item) { rowName(item) {
if (!item || item.placeholder) { if (!item || item.placeholder || item.skeleton) {
return ""; return "";
} }
return item.userNickname || ("User " + item.userId); return item.userNickname || ("User " + item.userId);
}, },
rowScore(item) { rowScore(item) {
if (!item || item.placeholder) { if (!item || item.placeholder || item.skeleton) {
return ""; return "";
} }
return toScoreText(item.scoreGold); return toScoreText(item.scoreGold);
}, },
skeletonRows() {
const size = this.activeTab === "last" ? 4 : 5;
return Array.from({ length: size }, (_, index) => ({
key: "skeleton-" + this.activeTab + "-" + index,
skeleton: true,
rank: 0,
}));
},
}; };
} }