Files
dinner.tootaio.com/20251115/sponsorList/index.html
xiaomai f1fe8eb559 feat(sponsorList): implement sponsor grouping and redesign UI
This commit introduces a major update to the sponsor list display.

- Implements a feature to group cash sponsors below a certain amount threshold. This helps to declutter the display when there are many smaller donations.
- Adds a new `cash-group` display type to handle the rendering of these grouped sponsors.
- Completely redesigns the UI with a new color scheme, gradients, and improved typography for a more polished and visually engaging presentation.
- Updates the styling for individual sponsor cards to improve readability and visual hierarchy.
2025-11-12 20:45:13 +08:00

248 lines
8.2 KiB
HTML
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.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sponsor list - 汕河</title>
<script src="/analysis.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
.sponsor-marquee-track {
animation: sponsor-marquee-move-text 120s linear infinite;
}
.special-marquee-track {
animation: special-marquee-move-text 30s linear infinite;
}
@keyframes sponsor-marquee-move-text {
to {
transform: translateY(-50%);
}
}
@keyframes special-marquee-move-text {
to {
transform: translateX(-50%);
}
}
</style>
</head>
<body>
<div id="app" class="h-screen flex flex-col select-none">
<div class="text-center text-6xl font-bold p-8 bg-red-700 text-white">
{{eventTitle}}
</div>
<!-- 特别赞助 -->
<div class="py-6 bg-yellow-300 text-red-500 shadow-yellow-500 shadow-lg">
<div class="overflow-clip mask-x-from-95% mask-x-to-100%">
<div class="flex pl-4 gap-4 w-max special-marquee-track text-4xl">
<div v-for="sponsor in specialSponsorDouble" :key="sponsor">
{{sponsor}}
</div>
</div>
</div>
</div>
<!-- 下半部分二八分,左侧放 image右侧走马灯 -->
<div
class="flex-1 flex min-h-0 overflow-clip bg-linear-to-b from-red-500 to-red-900"
>
<div class="flex-2 flex flex-col items-center justify-around">
<img
v-for="logo in logos"
:src="`../assets/${logo.imgSrc}`"
:alt="logo.imgSrc"
class="w-[80%] drop-shadow-2xl"
/>
</div>
<div class="flex-8 inset-red-lg">
<div class="flex flex-col pt-4 gap-4 px-4 sponsor-marquee-track">
<div
v-for="sponsor in sponsorListDouble"
:key="sponsor"
class="bg-linear-to-br from-white/20 to-white/10 px-16 py-4 rounded-2xl border-2 border-white/40"
>
<div v-if="sponsor.type == 'cash'" class="text-center py-8">
<div class="text-7xl text-yellow-400 font-bold">
RM {{sponsor.amount}}
</div>
<div
class="text-7xl text-white text-shadow-amber-400 text-shadow-lg"
>
{{sponsor.name}}
</div>
</div>
<div
v-else-if="sponsor.type == 'cash-group'"
class="text-center py-8"
>
<div class="text-7xl text-yellow-400 font-bold mb-8">
RM {{sponsor.amount}}
</div>
<div class="flex flex-wrap gap-4">
<div v-for="child in sponsor.children" class="text-3xl px-4 py-2 bg-white/40 rounded-2xl">{{child}}</div>
</div>
</div>
<div
v-else-if="sponsor.type == 'table'"
class="flex items-center"
>
<div
class="text-3xl bg-red-500 text-white font-bold px-4 py-2 border-2 border-white rounded-full"
>
{{tableToSeats(sponsor.amount)}}
</div>
<div class="text-4xl font-bold flex-1 ml-4 text-white">
{{sponsor.name}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const { createApp, ref, computed, onMounted } = Vue;
createApp({
setup() {
const eventTitle = ref("");
const logos = ref([]);
const sponsorList = ref([]); // Load from JSON
const specialSponsor = ref(
"由 V World2.0 特别赞助本场晚宴,现在下载 APP 并进行实名认证即可获得 RM50 的登录奖励!"
);
const typePriority = {
table: 2, // 优先级最高
cash: 3,
items: 1,
};
const groupByCashLessThan = ref(600);
function sortSponsors(list) {
return list.sort((a, b) => {
// 先按 type 优先级排序table > cash > items
const typeDiff = typePriority[b.type] - typePriority[a.type];
if (typeDiff !== 0) return typeDiff;
// 若 type 相同,再按 amount 从大到小
if (b.amount !== a.amount) return b.amount - a.amount;
// 若 amount 相同,最后按名称排序(可选)
return a.name.localeCompare(b.name, "zh");
});
}
const loadData = async () => {
try {
const [sponsorListResult] = await Promise.all([
fetch("../sponsorList.json"),
]);
if (!sponsorListResult.ok) {
throw new Error(
"Error while loading sponsorList: " + sponsorListResult.status
);
}
const sponsorListJsonData = await sponsorListResult.json();
eventTitle.value = sponsorListJsonData.eventTitle || "活动名称";
logos.value = sponsorListJsonData.logos || [];
sponsorList.value = (
sponsorListJsonData.sponsorList || []
).reduce((acc, s, idx) => {
const sponsor = {
...s,
amount:
s.type == "cash"
? s.amount.toLocaleString("en-MY")
: s.amount,
_uid: `s-${idx}`,
};
// 如果是现金赞助且金额小于阈值
if (s.type === "cash" && s.amount < groupByCashLessThan.value) {
// 查找是否已存在该金额的分组
const groupId = `group-${s.amount}`;
let amountGroup = acc.find((item) => item._uid === groupId);
if (!amountGroup) {
// 创建新的金额分组
amountGroup = {
name: "其他赞助商",
type: "cash-group",
amount: s.amount, // 保持原始数字,不格式化
children: [],
_uid: groupId,
};
acc.push(amountGroup);
}
// 将赞助商名称添加到分组的children中
amountGroup.children.push(s.name);
} else {
// 其他赞助商直接添加到结果中
acc.push(sponsor);
}
return acc;
}, []);
// Sort SponsorList by type and amount
sponsorList.value = sortSponsors(sponsorList.value);
console.log(sponsorList.value);
} catch (err) {
console.error(err);
}
};
onMounted(async () => {
await loadData();
});
const sponsorListDouble = computed(() => {
const a = sponsorList.value.map((s) => ({
...s,
_uid: s._uid + "-a",
}));
const b = sponsorList.value.map((s) => ({
...s,
_uid: s._uid + "-b",
}));
return [...a, ...b];
});
const specialSponsorDouble = computed(() => [
specialSponsor.value,
specialSponsor.value,
specialSponsor.value,
]);
const tableToSeats = (amount) => {
switch (amount) {
case 1:
return "一席";
case 0.5:
return "半席";
default:
console.error("Error while converting table amount: ", amount);
}
};
return {
eventTitle,
logos,
sponsorListDouble,
specialSponsorDouble,
tableToSeats,
};
},
}).mount("#app");
</script>
</body>
</html>