Files
dinner.tootaio.com/20251108/sponsorList/mobile/index.html
xiaomai 871e66a13a feat(event): add mobile sponsor list and refactor landing pages
This commit introduces a new mobile-first sponsor list and restructures the event landing pages. Key changes include:

- A new responsive sponsor list page built with Vue.js and Tailwind CSS, optimized for SEO and on-site viewing.
- The main event index page is now a QR code display for easy access to the mobile list.
- The original landing page is refactored with Tailwind CSS and moved to a new `/landing` directory.
- Umami analytics script is integrated across all pages for usage tracking.
2025-11-08 16:21:30 +08:00

512 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>永平捷兔会 30 周年庆赞助商 | 永平捷兔会大跑晚宴名单</title>
<script src="/analysis.js"></script>
<!-- ✅ SEO 基本信息 -->
<meta
name="description"
content="永平捷兔会 30 周年庆赞助商名单与赞助金额统计,包括特别赞助、各级别赞助人数与总额分布。感谢所有支持这场盛会的朋友与企业!"
/>
<meta
name="keywords"
content="永平捷兔会, 捷兔会, 30周年, 赞助商, 大跑晚宴, 永平, 马来西亚活动, TooTaio Studio"
/>
<meta name="author" content="TooTaio Studio" />
<meta name="robots" content="index, follow" />
<!-- ✅ Open Graph 社交分享 -->
<meta property="og:type" content="website" />
<meta
property="og:title"
content="永平捷兔会 30 周年庆赞助商名单 | 永平捷兔会大跑晚宴"
/>
<meta
property="og:description"
content="查看永平捷兔会 30 周年庆大跑晚宴赞助商名单与金额统计,感谢所有慷慨支持的赞助者!"
/>
<meta
property="og:image"
content="https://tootaio.com/assets/yphs30-share.jpg"
/>
<meta property="og:url" content="https://tootaio.com/yphs30" />
<meta property="og:site_name" content="TooTaio Studio" />
<!-- ✅ Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="永平捷兔会 30 周年庆赞助商名单" />
<meta
name="twitter:description"
content="感谢所有支持永平捷兔会 30 周年庆大跑晚宴的赞助者!"
/>
<meta
name="twitter:image"
content="https://tootaio.com/assets/yphs30-share.jpg"
/>
<!-- ✅ Favicon -->
<link rel="icon" type="image/png" href="https://tootaio.com/favicon.png" />
<!-- ✅ Canonical URL -->
<link rel="canonical" href="https://tootaio.com/yphs30" />
<!-- ✅ Schema.org JSON-LD 结构化数据 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Event",
"name": "永平捷兔会 30 周年庆大跑晚宴",
"description": "永平捷兔会 30 周年庆典,展示所有赞助商名单、赞助级别与感谢名单。",
"image": "https://tootaio.com/assets/yphs30-share.jpg",
"startDate": "2025-11-30T19:00",
"location": {
"@type": "Place",
"name": "永平",
"address": {
"@type": "PostalAddress",
"addressLocality": "Yong Peng",
"addressCountry": "Malaysia"
}
},
"organizer": {
"@type": "Organization",
"name": "永平捷兔会",
"url": "https://tootaio.com"
}
}
</script>
<!-- icons + tailwind + vue -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
/>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
/* font + tiny custom animation kept (Tailwind 用于绝大多数样式) */
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap");
body {
font-family: "Noto Sans SC", sans-serif;
}
/* animated gradient footer */
@keyframes glow {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.glow-bg {
background: linear-gradient(90deg, #ffff00, #bbbb00, #ffff00);
background-size: 200% 100%;
animation: glow 3s linear infinite;
}
</style>
</head>
<body class="bg-gray-50">
<div
id="app"
class="container mx-auto px-4 py-8 pb-24 md:py-10 md:max-w-6xl"
>
<!-- header -->
<header class="bg-[#2c5aa0] text-white rounded-lg p-5 text-center mb-6">
<h1 class="text-2xl md:text-3xl font-bold">永平捷兔会 30 周年庆</h1>
<p class="text-md md:text-lg mt-1">大跑晚宴赞助商名单</p>
</header>
<!-- stats -->
<section class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div
class="bg-white rounded-lg shadow-sm border border-gray-100 p-5 text-center"
>
<div class="text-2xl md:text-3xl font-bold text-[#2c5aa0]">
{{ totalSponsors }}
</div>
<div class="text-gray-600 mt-2">赞助人数</div>
</div>
<div
class="bg-white rounded-lg shadow-sm border border-gray-100 p-5 text-center"
>
<div class="text-2xl md:text-3xl font-bold text-[#2c5aa0]">
{{ totalAmount }}
</div>
<div class="text-gray-600 mt-2">赞助总额</div>
</div>
</section>
<!-- main content: sponsors + sidebar -->
<main class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- sponsors list (wide) -->
<section class="lg:col-span-2">
<h2 class="text-xl font-bold mb-4 text-gray-800">赞助商列表</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
<article
v-for="(category, key) in sponsors"
:key="key"
class="bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden"
>
<header
class="flex items-center justify-between px-4 py-3 bg-[#2c5aa0] text-white"
>
<h3 :class="category.titleFontSize || 'text-lg'">
{{ formatKey(key) }}
</h3>
<span class="text-sm bg-white bg-opacity-20 px-2 py-1 rounded"
>{{ category.list.length }} 人</span
>
</header>
<div class="p-4 flex flex-wrap gap-2">
<span
v-for="sponsor in category.list"
:key="sponsor"
class="text-sm bg-gray-100 px-3 py-1 rounded border border-gray-200"
>{{ sponsor }}</span
>
</div>
</article>
</div>
<!-- special sponsors -->
<h3 class="text-xl font-bold mt-8 mb-4 text-gray-800">特别赞助</h3>
<div class="space-y-3">
<div
v-for="sponsor in specialSponsors"
:key="sponsor"
class="bg-[#fff9e6] border-l-4 border-[#ffc107] px-4 py-3 rounded-r"
>
<i class="fas fa-star text-yellow-500 mr-2"></i>
<span>{{ sponsor }}</span>
</div>
</div>
</section>
<!-- sidebar stats -->
<aside>
<!-- level stats -->
<div
class="bg-white rounded-lg shadow-sm border border-gray-100 p-5 mb-6"
>
<h4 class="text-lg font-bold mb-4 text-center text-gray-800">
赞助级别统计
</h4>
<div v-for="level in levelStats" :key="level.name" class="mb-4">
<div class="flex justify-between text-sm mb-2">
<span>{{ level.name }}</span>
<span>{{ level.count }}人 ({{ level.percentage }}%)</span>
</div>
<div class="h-2 bg-gray-200 rounded overflow-hidden">
<div
class="h-full bg-[#2c5aa0]"
:style="{ width: level.percentage + '%' }"
></div>
</div>
</div>
</div>
<!-- amount distribution chart -->
<div class="bg-white rounded-lg shadow-sm border border-gray-100 p-5">
<h4 class="text-lg font-bold mb-4 text-center text-gray-800">
赞助金额分布
</h4>
<div
v-for="level in levelStats"
:key="level.name"
class="flex items-center mb-3"
>
<div class="w-24 text-sm">{{ level.name }}</div>
<div
class="flex-1 h-6 bg-gray-200 rounded overflow-hidden mx-3 relative"
>
<div
class="h-full rounded flex items-center justify-end pr-2 font-medium text-white bg-[#2c5aa0]"
:style="{ width: level.amountPercentage + '%' }"
>
<span v-if="level.amountPercentage > 10"
>{{ level.amountPercentage }}%</span
>
</div>
</div>
<div class="w-20 text-right font-medium text-sm">
{{ level.amount }}
</div>
</div>
</div>
</aside>
</main>
<!-- footer -->
<footer class="fixed bottom-0 left-0 w-full z-50">
<div class="glow-bg py-3 text-center">
<a
href="https://tootaio.com"
target="_blank"
class="text-black font-extrabold text-lg"
>
Designed by
<span class="font-extrabold"
><span class="text-red-500">TooTaio</span> Studio</span
>
</a>
</div>
</footer>
</div>
<script>
const { createApp, ref, computed } = Vue;
createApp({
setup() {
const eventTitle = ref("永平捷兔会 30 周年庆大跑晚宴");
const formatKey = (key) =>
key
.replace(/([A-Z])/g, " $1")
.replace(/^./, (s) => s.toUpperCase());
const sponsors = ref({
Rm5000: { list: ["Top Gan"] },
Rm2000: {
list: [
"Ketua Kampung",
"校长",
"Natural 9",
"明盛弟",
"联合周",
"新成酒家",
],
},
Rm1600: { list: ["Moon"] },
Rm1200: { list: ["Labis Bon"] },
Rm1000: { list: ["Angel"] },
Rm800: {
list: [
"拿督",
"Mari Chan",
"霖主席",
"MDL阿德",
"Cool Lo",
"刘薇薇",
],
},
Rm500: { list: ["Hun Shap Tou", "Good Man", "学生妹", "三太子"] },
Rm400: {
showTogether: true,
list: [
"Founder Koh",
"Founder Ang",
"Founder Koid",
"Superman",
"榴梿",
"阳阳",
"Lawyer",
"小老板",
"富婆",
"Farmer",
"Farmer嫂",
"Durian King",
"大傻",
"Datin",
"Mohamad Ali",
"Wireman",
"Ki Ka Poh",
"TV",
"Sexy",
"Uncle Low",
"小黑",
"黑夫人",
"Jimmy",
"Ah Boon",
"米桶",
"Pasar Malam",
"Public Ong",
"Pet pet",
"梅惠",
"910",
"伟哥",
"仙女",
"Boss",
"彩虹",
"花瓶",
"黑珍珠",
"木薯老板",
"美国佬",
"三公子",
"来",
"宝强",
"Steven",
"老二",
"车斗Lau",
"海南Huat",
"龙门铁宝",
"山竹祥",
"旺庆",
"福承",
"立家",
"鸿兴",
"江老板",
"Wong Long",
"健芳",
"杨文德",
"古早味",
"猪笼",
"Bangkali Pusing",
"999",
"012",
"Wu Wei Xiong",
"Fan Shu",
"肥福",
"Kulai:阿祥",
"Kulai:Robert",
"国宝",
"Puki Ayam",
"爱情鸟",
"William Soh",
"Darren",
"林总",
"High More",
"Tiger",
"Lim Kopi",
"林董",
"Lighting Chan",
"Jag",
"Corina",
"Jiu Xiao",
"Love Bird",
"Sepuluh Dua",
"哈哈",
"狗爷",
],
},
Rm200: {
showTogether: true,
list: [
"兰总",
"Kampopo",
"Lim Kee Meng",
"003",
"Tan Brother",
"E-Sun",
"Kami",
"Kami嫂",
"美发师",
"美女",
"Oong Lai",
"乃乃",
"Naluri",
"妹子",
"Joan",
"Kai De Tan",
"企鹅",
"老二",
"二娘",
"DJ Yap",
"菜头",
"Ketam",
"Roket",
"土豪",
"天鹅",
"老板娘",
"Momo",
"Happyman",
"阿琳",
"花木兰",
"财政",
"财政夫人",
"走火",
"表妹",
"鸡脚老大",
"阿锦",
"维哥",
"牛车轮",
"陈进平",
],
},
});
const specialSponsors = ref([
"HEINEKEN MARKETING MALAYSIA SDN.BHD.: 100 件 T-Shirt & 500 件小毛巾",
"花奇 Nou: 大蛋糕一个",
"绝世旅游 500 - 550 环保袋",
]);
const totalSponsors = computed(() => {
let count = 0;
Object.values(sponsors.value).forEach(
(cat) => (count += cat.list.length)
);
return count;
});
const totalAmount = computed(() => {
let total = 0;
Object.entries(sponsors.value).forEach(([key, value]) => {
const amount = parseFloat(key.replace(/[^\d.]/g, "")) || 0;
total += amount * value.list.length;
});
return "RM" + total.toLocaleString("en-MY");
});
const levelStats = computed(() => {
const stats = [];
let totalCount = 0;
let totalAmountNum = 0;
Object.entries(sponsors.value).forEach(([key, value]) => {
totalCount += value.list.length;
const amount = parseFloat(key.replace(/[^\d.]/g, "")) || 0;
totalAmountNum += amount * value.list.length;
});
Object.entries(sponsors.value).forEach(([key, value]) => {
const amount = parseFloat(key.replace(/[^\d.]/g, "")) || 0;
const levelAmount = amount * value.list.length;
const percentage = (
(value.list.length / (totalCount || 1)) *
100
).toFixed(1);
const amountPercentage = (
(levelAmount / (totalAmountNum || 1)) *
100
).toFixed(1);
stats.push({
name: formatKey(key),
count: value.list.length,
amount: "RM" + levelAmount.toLocaleString("en-MY"),
percentage,
amountPercentage,
});
});
return stats;
});
return {
eventTitle,
formatKey,
sponsors,
specialSponsors,
totalSponsors,
totalAmount,
levelStats,
};
},
}).mount("#app");
</script>
</body>
</html>