邀请活动ui优化

This commit is contained in:
170-carry 2026-04-28 12:07:46 +08:00
parent 2507eae749
commit 2d0b637b14
9 changed files with 230 additions and 106 deletions

View File

@ -86,6 +86,7 @@ const els = {
navButtons: [...document.querySelectorAll("[data-nav-view]")],
copyButtons: [...document.querySelectorAll("[data-copy-target]")],
rankList: document.getElementById("rankList"),
rankLists: [...document.querySelectorAll("[data-rank-list]")],
giftTitle: document.getElementById("giftTitle"),
giftAvatarImage: document.getElementById("giftAvatarImage"),
downloadButton: document.getElementById("downloadButton"),
@ -694,10 +695,38 @@ function renderTasks() {
els.taskNote.textContent = DEFAULT_TASK_NOTES[state.taskSet] || "";
}
function renderRankList() {
function rankBadgeMarkup(item) {
const badgeMap = {
1: "./assets/rank-top1.png",
2: "./assets/rank-top2.png",
3: "./assets/rank-top3.png",
};
const badge = badgeMap[item.rank];
return badge
? `<img src="${badge}" alt="Rank ${item.rank}" />`
: escapeHtml(String(item.rank));
}
function renderRankRows(container) {
const list = state.rankItems;
const inline = container?.dataset?.rankVariant === "inline";
if (!Array.isArray(list) || list.length === 0) {
els.rankList.innerHTML = `
return inline
? `
<article
class="rank-row"
style="background-image:url('./assets/rank-row.svg')"
>
<div class="rank-badge">--</div>
<div class="avatar">
<img src="${DEFAULT_AVATAR}" alt="" />
</div>
<div class="rank-name">No ranking data yet</div>
<div class="rank-score">0</div>
<div class="rank-reward">0</div>
</article>
`
: `
<article
class="rank-row"
style="background-image:url('./assets/rank-row.svg')"
@ -710,29 +739,31 @@ function renderRankList() {
<div class="rank-score">0 people</div>
</article>
`;
return;
}
els.rankList.innerHTML = list.map((item) => {
const badgeMap = {
1: "./assets/rank-top1.png",
2: "./assets/rank-top2.png",
3: "./assets/rank-top3.png",
};
const badge = badgeMap[item.rank];
return list.map((item) => {
if (inline) {
return `
<article
class="rank-row"
style="background-image:url('./assets/rank-row.svg')"
>
<div class="rank-badge">${rankBadgeMarkup(item)}</div>
<div class="avatar">
<img src="${escapeHtml(item.avatar || DEFAULT_AVATAR)}" alt="${escapeHtml(item.name)}" />
</div>
<div class="rank-name">${escapeHtml(item.name)}</div>
<div class="rank-score">${escapeHtml(item.inviteCountText)}</div>
<div class="rank-reward">${escapeHtml(item.rewardText)}</div>
</article>
`;
}
return `
<article
class="rank-row"
style="background-image:url('./assets/rank-row.svg')"
>
<div class="rank-badge">
${
badge
? `<img src="${badge}" alt="Rank ${item.rank}" />`
: escapeHtml(String(item.rank))
}
</div>
<div class="rank-badge">${rankBadgeMarkup(item)}</div>
<div class="avatar">
<img src="${escapeHtml(item.avatar || DEFAULT_AVATAR)}" alt="${escapeHtml(item.name)}" />
</div>
@ -743,6 +774,15 @@ function renderRankList() {
}).join("");
}
function renderRankList() {
const containers = els.rankLists.length > 0
? els.rankLists
: [els.rankList].filter(Boolean);
containers.forEach((container) => {
container.innerHTML = renderRankRows(container);
});
}
function pad(number) {
return String(number).padStart(2, "0");
}
@ -899,6 +939,8 @@ function normalizeRankList(list) {
rank,
name: userName,
avatar: trimValue(item?.avatar) || DEFAULT_AVATAR,
inviteCountText: `${formatMetric(inviteCount)}`,
rewardText: formatMetric(rewardGoldCoins),
score: scoreParts.join(" · "),
};
});
@ -1087,9 +1129,13 @@ async function refreshPageData() {
if (state.view === "gift") {
await fetchLandingData();
} else {
const hasHome = await fetchHomeData();
if (hasHome) {
await fetchRankData();
const results = await Promise.allSettled([
fetchHomeData(),
fetchRankData(),
]);
const rejected = results.find((item) => item.status === "rejected");
if (rejected) {
throw rejected.reason;
}
}
} catch (error) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -28,8 +28,12 @@
--ornate-top: url("./assets/panel-top.png");
--ornate-middle: url("./assets/panel-middle.png");
--ornate-bottom: url("./assets/panel-bottom.png");
--button-active-bg: url("./assets/button-active-bg.png");
--button-inactive-bg: url("./assets/button-inactive-bg.png");
--monthly-title-bg: url("./assets/mt1.png");
--task-tab-active-bg: url("./assets/btnselect.png");
--task-tab-inactive-bg: url("./assets/btnunselect.png");
--countdown-card-bg: url("./assets/djs.png");
--button-active-bg: url("./assets/btnselect.png");
--button-inactive-bg: url("./assets/btnunselect.png");
}
* {
@ -143,12 +147,27 @@
.ornate-panel {
position: relative;
z-index: 0;
isolation: isolate;
background-color: #0b3a08;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.22);
}
.ornate-panel::before {
content: "";
position: absolute;
inset: 0;
z-index: 0;
background-image: var(--ornate-top), var(--ornate-bottom), var(--ornate-middle);
background-position: top center, bottom center, top center;
background-size: 100% auto, 100% auto, 100% auto;
background-repeat: no-repeat, no-repeat, repeat-y;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.22);
pointer-events: none;
}
.ornate-panel > * {
position: relative;
z-index: 1;
}
.timer-panel {
@ -163,22 +182,29 @@
}
.timer-box {
border: 1.5px solid rgba(249, 244, 120, 0.55);
border-radius: 10px;
padding: 10px 4px 9px;
display: flex;
flex-direction: column;
align-items: center;
padding: 0;
text-align: center;
background: rgba(4, 31, 7, 0.34);
}
.timer-value {
font-size: 24px;
width: 58px;
aspect-ratio: 113 / 131;
display: flex;
align-items: center;
justify-content: center;
padding-top: 1px;
background: center / 100% 100% no-repeat var(--countdown-card-bg);
font-size: 22px;
line-height: 1;
font-weight: 700;
color: #fff7dd;
}
.timer-label {
margin-top: 8px;
margin-top: 9px;
font-size: 10px;
line-height: 1;
color: var(--text-muted);
@ -245,10 +271,12 @@
.reward-orb img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
left: 50%;
top: 55%;
width: 66%;
height: 66%;
object-fit: contain;
transform: translate(-50%, -50%);
}
.reward-label {
@ -347,6 +375,16 @@
.copy-action-button {
width: 100%;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: center / 100% 100% no-repeat var(--button-active-bg);
color: #fff8d5;
font-size: 16px;
font-weight: 800;
line-height: 1;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
}
.meta-inline {
@ -416,50 +454,70 @@
height: auto;
}
.monthly-task-panel {
margin-top: 32px;
padding: 58px 18px 20px;
}
.inline-rank-panel {
margin-top: 20px;
padding: 44px 16px 18px;
}
.monthly-title {
position: absolute;
top: -24px;
left: 50%;
z-index: 2;
width: 140px;
aspect-ratio: 419 / 120;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10px;
transform: translateX(-50%);
background: center / 100% 100% no-repeat var(--monthly-title-bg);
color: #fff7d7;
font-size: 16px;
font-weight: 800;
line-height: 1;
text-align: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
}
.inline-rank-title {
top: -16px;
width: 88px;
font-size: 12px;
}
.task-tabs {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 7px;
gap: 10px;
}
.task-tab {
position: relative;
min-height: 0;
aspect-ratio: 430 / 130;
padding: 0;
aspect-ratio: 419 / 120;
padding: 0 6px;
border: 0;
background: transparent;
color: transparent;
font-size: 0;
background: center / 100% 100% no-repeat var(--task-tab-inactive-bg);
color: #fff4b9;
font-size: 12px;
font-weight: 700;
line-height: 1.05;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.55);
}
.task-tab::after {
content: "";
position: absolute;
inset: 0;
background-position: center;
background-repeat: no-repeat;
background-size: 100% 100%;
pointer-events: none;
}
.task-tab[data-task-set="transport"].passive::after {
background-image: url("./assets/tab-transport-inactive.png");
}
.task-tab[data-task-set="transport"].active::after {
background-image: url("./assets/tab-transport-active.png");
}
.task-tab[data-task-set="quota"].passive::after {
background-image: url("./assets/tab-quota-inactive.png");
}
.task-tab[data-task-set="quota"].active::after {
background-image: url("./assets/tab-quota-active.png");
.task-tab.active {
background-image: var(--task-tab-active-bg);
color: #fff8d5;
}
.task-list-panel {
@ -470,6 +528,7 @@
.task-list {
display: grid;
gap: 9px;
margin-top: 14px;
}
.task-card {
@ -513,9 +572,8 @@
}
.claim-button.is-claimable {
padding: 0;
background-image: var(--button-active-bg);
color: transparent;
color: #fff8d5;
}
.claim-button.disabled,
@ -526,14 +584,6 @@
color: rgba(246, 224, 178, 0.75);
}
.claim-button.is-claimable::after {
content: "";
position: absolute;
inset: 0;
background: center / 52px auto no-repeat url("./assets/claim-active.png");
pointer-events: none;
}
.task-note {
margin: 14px 4px 0;
font-size: 10px;
@ -542,14 +592,6 @@
color: var(--text-muted);
}
.footer-button {
width: min(100%, 269px);
margin: 16px auto 32px;
display: flex;
justify-content: center;
align-items: center;
}
.rank-view {
padding: 18px 12px 28px;
}
@ -597,6 +639,10 @@
gap: 8px;
}
.inline-rank-list {
gap: 4px;
}
.rank-row {
display: grid;
grid-template-columns: 30px 42px minmax(0, 1fr) auto;
@ -658,6 +704,41 @@
color: #f8ead2;
}
.inline-rank-panel .rank-row {
grid-template-columns: 22px 30px minmax(0, 1fr) 48px 48px;
min-height: 37px;
gap: 6px;
padding: 4px 10px;
}
.inline-rank-panel .rank-badge {
width: 20px;
height: 20px;
font-size: 10px;
}
.inline-rank-panel .rank-badge img {
width: 20px;
height: 20px;
}
.inline-rank-panel .avatar {
width: 30px;
height: 30px;
}
.inline-rank-panel .rank-name,
.inline-rank-panel .rank-score,
.inline-rank-panel .rank-reward {
font-size: 10px;
}
.inline-rank-panel .rank-score,
.inline-rank-panel .rank-reward {
text-align: right;
white-space: nowrap;
}
.gift-view {
position: relative;
min-height: 100vh;
@ -875,7 +956,7 @@
data-copy-target="invite-link"
aria-label="Copy invitation link"
>
<img data-src="./assets/copy-button.png" alt="" class="lazy-image" />
Copy
</button>
</div>
<div class="copy-row">
@ -889,7 +970,7 @@
data-copy-target="invite-code"
aria-label="Copy invitation code"
>
<img data-src="./assets/copy-button.png" alt="" class="lazy-image" />
Copy
</button>
</div>
</div>
@ -916,33 +997,30 @@
</div>
</div>
<div class="section-header-art">
<img
data-src="./assets/monthly-tasks-button.png"
alt="Monthly Tasks"
class="lazy-image"
/>
</div>
<div class="task-tabs">
<button type="button" class="task-tab passive" data-task-set="transport">
Total Transportation Volume
</button>
<button type="button" class="task-tab active" data-task-set="quota">
Invitation Quota Task
</button>
</div>
<div class="ornate-panel task-list-panel">
<div class="ornate-panel monthly-task-panel">
<div class="monthly-title">Monthly Tasks</div>
<div class="task-tabs">
<button type="button" class="task-tab passive" data-task-set="transport">
Total Transportation<br />Volume
</button>
<button type="button" class="task-tab active" data-task-set="quota">
Invitation Quota<br />Task
</button>
</div>
<div class="task-list" id="taskList"></div>
<p class="task-note" id="taskNote"></p>
</div>
<div class="footer-button">
<button type="button" class="art-button" data-nav-view="rank" aria-label="View rank">
<img data-src="./assets/rank-button.png" alt="" class="lazy-image" />
</button>
<div class="ornate-panel inline-rank-panel">
<div class="monthly-title inline-rank-title">Rank</div>
<div
class="rank-list inline-rank-list"
id="inlineRankList"
data-rank-list
data-rank-variant="inline"
></div>
</div>
</div>
</section>
@ -957,7 +1035,7 @@
<img src="./assets/rank-title-button.png" alt="Rank" />
</div>
</div>
<div class="rank-list" id="rankList"></div>
<div class="rank-list" id="rankList" data-rank-list></div>
</div>
</section>