feat(sponsorList): add mobile view and statistics panel
This commit introduces a new mobile-first version of the sponsor list page. Key features: - A responsive layout optimized for mobile devices. - A statistics panel showing total sponsors, contribution amounts, and type distribution. - Category filtering via a bottom navigation bar. Additionally, the special sponsor message is now loaded from `sponsorList.json` to centralize data for both desktop and mobile views.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"eventTitle": "永平新港汕河体育协会 2 周年庆联欢晚宴",
|
"eventTitle": "永平新港汕河体育协会 2 周年庆联欢晚宴",
|
||||||
|
"specialSponsors": "感谢 V World2.0 的特别赞助,现在下载 APP 并进行实名认证即可获得 RM50 的登录奖励!",
|
||||||
"logos": [{ "imgSrc": "SamHor-HighRes.png" }, { "imgSrc": "关公文化-HighRes.png" }, { "imgSrc": "VWorld2 Logo.png" }],
|
"logos": [{ "imgSrc": "SamHor-HighRes.png" }, { "imgSrc": "关公文化-HighRes.png" }, { "imgSrc": "VWorld2 Logo.png" }],
|
||||||
"poems": ["汕水流长通四海", "河川万里泽邦家"],
|
"poems": ["汕水流长通四海", "河川万里泽邦家"],
|
||||||
"sponsorList": [
|
"sponsorList": [
|
||||||
|
|||||||
@@ -232,9 +232,7 @@
|
|||||||
const eventTitle = ref("");
|
const eventTitle = ref("");
|
||||||
const logos = ref([]);
|
const logos = ref([]);
|
||||||
const sponsorList = ref([]); // Load from JSON
|
const sponsorList = ref([]); // Load from JSON
|
||||||
const specialSponsor = ref(
|
const specialSponsor = ref("");
|
||||||
"感谢 V World2.0 的特别赞助,现在下载 APP 并进行实名认证即可获得 RM50 的登录奖励!"
|
|
||||||
);
|
|
||||||
|
|
||||||
const typePriority = {
|
const typePriority = {
|
||||||
cash: 4,
|
cash: 4,
|
||||||
@@ -271,6 +269,7 @@
|
|||||||
}
|
}
|
||||||
const sponsorListJsonData = await sponsorListResult.json();
|
const sponsorListJsonData = await sponsorListResult.json();
|
||||||
eventTitle.value = sponsorListJsonData.eventTitle || "活动名称";
|
eventTitle.value = sponsorListJsonData.eventTitle || "活动名称";
|
||||||
|
specialSponsor.value = sponsorListJsonData.specialSponsor || "";
|
||||||
logos.value = sponsorListJsonData.logos || [];
|
logos.value = sponsorListJsonData.logos || [];
|
||||||
|
|
||||||
sponsorList.value = (
|
sponsorList.value = (
|
||||||
|
|||||||
239
20251115/sponsorList/mobile/index.html
Normal file
239
20251115/sponsorList/mobile/index.html
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>永平新港汕河体育协会 2 周年庆联欢晚宴|电子征信录与赞助名单</title>
|
||||||
|
|
||||||
|
<!-- ✅ SEO 基础优化 -->
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="永平新港汕河体育协会 2 周年庆联欢晚宴电子征信录,完整收录所有赞助商与支持者名单,感谢社会各界的热心赞助与参与。"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="永平新港汕河体育协会, 永平, 汕河, 体育会, 赞助名单, 周年庆, 晚宴, 电子征信录, Malaysia, Yong Peng"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- ✅ Open Graph (社交分享预览) -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta
|
||||||
|
property="og:title"
|
||||||
|
content="永平新港汕河体育协会 2 周年庆联欢晚宴 - 电子征信录"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="感谢所有赞助商与嘉宾的支持,共襄盛举。查看完整赞助名单与活动记录。"
|
||||||
|
/>
|
||||||
|
<meta property="og:image" content="https://example.com/assets/cover.jpg" />
|
||||||
|
<meta property="og:url" content="https://example.com/sponsor-record" />
|
||||||
|
<meta property="og:site_name" content="永平新港汕河体育协会" />
|
||||||
|
|
||||||
|
<!-- ✅ Twitter Card -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta
|
||||||
|
name="twitter:title"
|
||||||
|
content="永平新港汕河体育协会 2 周年庆联欢晚宴"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="电子征信录|感谢所有赞助商与支持者"
|
||||||
|
/>
|
||||||
|
<meta name="twitter:image" content="https://example.com/assets/cover.jpg" />
|
||||||
|
|
||||||
|
<!-- ✅ Favicon -->
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
|
||||||
|
<!-- ✅ 性能优化 -->
|
||||||
|
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
|
||||||
|
<link rel="preconnect" href="https://unpkg.com" />
|
||||||
|
<link rel="preconnect" href="https://cdnjs.cloudflare.com" />
|
||||||
|
|
||||||
|
<!-- ✅ JSON-LD 结构化数据 (Google 识别为活动页面) -->
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Event",
|
||||||
|
"name": "永平新港汕河体育协会 2 周年庆联欢晚宴",
|
||||||
|
"startDate": "2025-11-22T19:00",
|
||||||
|
"location": {
|
||||||
|
"@type": "Place",
|
||||||
|
"name": "永平新港汕河体育协会会所",
|
||||||
|
"address": "Yong Peng, Johor, Malaysia"
|
||||||
|
},
|
||||||
|
"image": "https://example.com/assets/cover.jpg",
|
||||||
|
"description": "庆祝永平新港汕河体育协会成立两周年的晚宴活动,并公布电子征信录感谢所有赞助商与支持者。",
|
||||||
|
"organizer": {
|
||||||
|
"@type": "Organization",
|
||||||
|
"name": "永平新港汕河体育协会",
|
||||||
|
"url": "https://example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-50">
|
||||||
|
<div id="app" class="h-screen flex flex-col">
|
||||||
|
<!-- 头部 -->
|
||||||
|
<header
|
||||||
|
class="bg-radial from-orange-600 to-red-600 text-white p-4 shadow-md"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-center mb-2">
|
||||||
|
<h1 class="text-lg font-bold truncate">{{ eventTitle }}</h1>
|
||||||
|
<button
|
||||||
|
@click="showStats = !showStats"
|
||||||
|
class="bg-white/20 p-2 rounded-full"
|
||||||
|
>
|
||||||
|
<i class="fas fa-chart-bar"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Logo区域 -->
|
||||||
|
<div class="flex justify-center space-x-4 overflow-x-auto py-2">
|
||||||
|
<div v-for="(logo, index) in logos" :key="index" class="shrink-0">
|
||||||
|
<div
|
||||||
|
class="bg-white rounded-lg p-2 w-16 h-16 flex items-center justify-center"
|
||||||
|
>
|
||||||
|
<img :src="`../../assets/${logo.imgSrc}`" :alt="logo.imgSrc" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- 特别赞助商 -->
|
||||||
|
<div
|
||||||
|
v-if="specialSponsor"
|
||||||
|
class="bg-yellow-100 border-l-4 border-yellow-500 p-3 mx-4 mt-4 rounded-r"
|
||||||
|
>
|
||||||
|
<div class="flex items-start">
|
||||||
|
<i class="fas fa-star text-yellow-500 mt-1 mr-2"></i>
|
||||||
|
<p class="text-sm text-yellow-800">{{ specialSponsor }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 统计数据 -->
|
||||||
|
<div v-if="showStats" class="bg-white m-4 p-4 rounded-lg shadow-sm">
|
||||||
|
<h2 class="text-lg font-semibold mb-3 flex items-center">
|
||||||
|
<i class="fas fa-chart-pie mr-2 text-blue-500"></i>赞助统计
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-3">
|
||||||
|
<div class="bg-blue-50 p-3 rounded-lg">
|
||||||
|
<p class="text-xs text-blue-700">赞助商总数</p>
|
||||||
|
<p class="text-xl font-bold">{{ stats.totalSponsors }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-green-50 p-3 rounded-lg">
|
||||||
|
<p class="text-xs text-green-700">现金赞助总额</p>
|
||||||
|
<p class="text-xl font-bold">
|
||||||
|
RM {{ stats.totalCash.toLocaleString() }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-purple-50 p-3 rounded-lg">
|
||||||
|
<p class="text-xs text-purple-700">标品赞助数量</p>
|
||||||
|
<p class="text-xl font-bold">{{ stats.totalItems }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-orange-50 p-3 rounded-lg">
|
||||||
|
<p class="text-xs text-orange-700">赞助席位总数</p>
|
||||||
|
<p class="text-xl font-bold">
|
||||||
|
{{ tableToSeats(stats.totalTables) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<h3 class="text-sm font-medium mb-2">赞助类型分布</h3>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div
|
||||||
|
v-for="type in stats.typeDistribution"
|
||||||
|
:key="type.name"
|
||||||
|
class="flex items-center"
|
||||||
|
>
|
||||||
|
<div class="w-20 text-xs">{{ type.name }}</div>
|
||||||
|
<div class="flex-1 bg-gray-200 rounded-full h-2">
|
||||||
|
<div
|
||||||
|
:class="type.color"
|
||||||
|
class="h-2 rounded-full"
|
||||||
|
:style="{ width: type.percentage + '%' }"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div class="w-10 text-right text-xs">{{ type.count }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 赞助商分类 -->
|
||||||
|
<div class="flex-1 overflow-auto">
|
||||||
|
<!-- 赞助商列表 -->
|
||||||
|
<div class="p-4">
|
||||||
|
<div
|
||||||
|
v-if="filteredSponsors.length === 0"
|
||||||
|
class="text-center py-8 text-gray-500"
|
||||||
|
>
|
||||||
|
<i class="fas fa-inbox text-4xl mb-2"></i>
|
||||||
|
<p>暂无此类赞助商</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="space-y-4">
|
||||||
|
<!-- 现金赞助商 -->
|
||||||
|
<sponsor-card
|
||||||
|
v-for="s in filteredSponsors"
|
||||||
|
:key="s._uid"
|
||||||
|
:sponsor="s"
|
||||||
|
>
|
||||||
|
<!-- <template #extra>
|
||||||
|
<div class="mt-2 text-xs text-gray-500">自定义插槽内容</div>
|
||||||
|
</template> -->
|
||||||
|
</sponsor-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部导航 -->
|
||||||
|
<footer class="bg-white border-t p-3 flex justify-around">
|
||||||
|
<button
|
||||||
|
v-for="category in categories"
|
||||||
|
:key="category.type"
|
||||||
|
@click="activeCategory = category.type"
|
||||||
|
:class="['flex flex-col items-center text-xs', (activeCategory === category.type ? category.color : 'text-gray-400')]"
|
||||||
|
>
|
||||||
|
<i :class="['text-lg mb-1', category.icon]"></i>
|
||||||
|
<span>{{category.name}}</span>
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template id="sponsor-card-template">
|
||||||
|
<div class="bg-white rounded-lg shadow-sm p-4">
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<h3 class="font-medium text-gray-800">{{ sponsor.name }}</h3>
|
||||||
|
<span
|
||||||
|
:class="['text-sm px-2 py-1 rounded-full', sponsor.pillBg, sponsor.pillColor]"
|
||||||
|
>
|
||||||
|
<span v-if="sponsor.type == 'cash'" class="whitespace-nowrap">
|
||||||
|
RM {{ sponsor.displayAmount }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="sponsor.type == 'item'">
|
||||||
|
<i class="fas fa-gift mr-1"></i>标品赞助
|
||||||
|
</span>
|
||||||
|
<span v-else-if="sponsor.type == 'table'">{{sponsor.seats}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p v-if="sponsor.detail" class="text-sm text-gray-600 mt-2">
|
||||||
|
{{ sponsor.detail }}
|
||||||
|
</p>
|
||||||
|
<slot name="extra"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
295
20251115/sponsorList/mobile/script.js
Normal file
295
20251115/sponsorList/mobile/script.js
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
const { createApp, ref, computed, onMounted } = Vue;
|
||||||
|
|
||||||
|
const SponsorCard = {
|
||||||
|
props: {
|
||||||
|
sponsor: { type: Object, required: true },
|
||||||
|
},
|
||||||
|
template: "#sponsor-card-template",
|
||||||
|
};
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
setup() {
|
||||||
|
const eventTitle = ref("");
|
||||||
|
const logos = ref([]);
|
||||||
|
const sponsorList = ref([]); // Load from JSON
|
||||||
|
const specialSponsor = ref("");
|
||||||
|
const showStats = ref(false);
|
||||||
|
const activeCategory = ref("all");
|
||||||
|
|
||||||
|
const categories = [
|
||||||
|
{
|
||||||
|
type: "all",
|
||||||
|
name: "全部",
|
||||||
|
icon: "fas fa-list",
|
||||||
|
color: "text-blue-500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "cash",
|
||||||
|
name: "现金",
|
||||||
|
icon: "fas fa-money-bill-wave",
|
||||||
|
color: "text-green-500",
|
||||||
|
pillBg: "bg-green-100",
|
||||||
|
pillColor: "text-green-800",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "item",
|
||||||
|
name: "标品",
|
||||||
|
icon: "fas fa-gift",
|
||||||
|
color: "text-purple-500",
|
||||||
|
pillBg: "bg-purple-100",
|
||||||
|
pillColor: "text-purple-800",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "table",
|
||||||
|
name: "桌位",
|
||||||
|
icon: "fas fa-utensils",
|
||||||
|
color: "text-orange-500",
|
||||||
|
pillBg: "bg-orange-100",
|
||||||
|
pillColor: "text-orange-800",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const typePriority = {
|
||||||
|
cash: 4,
|
||||||
|
"cash-group": 3,
|
||||||
|
table: 2,
|
||||||
|
item: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupByCashLessThan = ref(0);
|
||||||
|
|
||||||
|
function sortSponsors(list) {
|
||||||
|
return list.sort((a, b) => {
|
||||||
|
// 先按 type 优先级排序
|
||||||
|
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 stats = computed(() => {
|
||||||
|
const result = {
|
||||||
|
totalSponsors: 0,
|
||||||
|
totalCash: 0,
|
||||||
|
totalItems: 0,
|
||||||
|
totalTables: 0,
|
||||||
|
typeDistribution: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算各类赞助商数量
|
||||||
|
const typeCounts = {
|
||||||
|
cash: 0,
|
||||||
|
"cash-group": 0,
|
||||||
|
item: 0,
|
||||||
|
table: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算总额
|
||||||
|
sponsorList.value.forEach((sponsor) => {
|
||||||
|
if (sponsor.type === "cash") {
|
||||||
|
result.totalCash += sponsor.amount;
|
||||||
|
typeCounts.cash++;
|
||||||
|
} else if (sponsor.type === "cash-group") {
|
||||||
|
result.totalCash += sponsor.amount * sponsor.children.length;
|
||||||
|
typeCounts["cash-group"]++;
|
||||||
|
} else if (sponsor.type === "item") {
|
||||||
|
result.totalItems++;
|
||||||
|
typeCounts.item++;
|
||||||
|
} else if (sponsor.type === "table") {
|
||||||
|
result.totalTables += sponsor.amount;
|
||||||
|
typeCounts.table++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算总赞助商数量
|
||||||
|
result.totalSponsors = sponsorList.value.length;
|
||||||
|
|
||||||
|
// 计算类型分布
|
||||||
|
const total = result.totalSponsors;
|
||||||
|
result.typeDistribution = [
|
||||||
|
{
|
||||||
|
name: "现金赞助",
|
||||||
|
count: typeCounts.cash + typeCounts["cash-group"],
|
||||||
|
percentage: Math.round(
|
||||||
|
((typeCounts.cash + typeCounts["cash-group"]) / total) * 100
|
||||||
|
),
|
||||||
|
color: "bg-green-500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "标品赞助",
|
||||||
|
count: typeCounts.item,
|
||||||
|
percentage: Math.round((typeCounts.item / total) * 100),
|
||||||
|
color: "bg-purple-500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "桌位赞助",
|
||||||
|
count: typeCounts.table,
|
||||||
|
percentage: Math.round((typeCounts.table / total) * 100),
|
||||||
|
color: "bg-orange-500",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 过滤赞助商
|
||||||
|
const filteredSponsors = computed(() => {
|
||||||
|
if (activeCategory.value === "all") {
|
||||||
|
return sponsorList.value;
|
||||||
|
}
|
||||||
|
return sponsorList.value.filter(
|
||||||
|
(sponsor) => sponsor.type === activeCategory.value
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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 || "活动名称";
|
||||||
|
specialSponsor.value = sponsorListJsonData.specialSponsors || "";
|
||||||
|
logos.value = sponsorListJsonData.logos || [];
|
||||||
|
|
||||||
|
sponsorList.value = (sponsorListJsonData.sponsorList || []).reduce(
|
||||||
|
(acc, s, idx) => {
|
||||||
|
const category = categories.find((c) => c.type === s.type);
|
||||||
|
|
||||||
|
const sponsor = {
|
||||||
|
...s,
|
||||||
|
displayAmount:
|
||||||
|
s.type == "cash" ? s.amount.toLocaleString("en-MY") : s.amount,
|
||||||
|
seats: s.type == "table" ? tableToSeats(s.amount) : "",
|
||||||
|
pillBg: category?.pillBg,
|
||||||
|
pillColor: category?.pillColor,
|
||||||
|
_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",
|
||||||
|
displayAmount:
|
||||||
|
s.type == "cash"
|
||||||
|
? s.amount.toLocaleString("en-MY")
|
||||||
|
: s.amount,
|
||||||
|
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 tableToSeats = (input) => {
|
||||||
|
if (input === null || input === undefined || input === "") return "";
|
||||||
|
|
||||||
|
if (input === 0.5) return "半席";
|
||||||
|
|
||||||
|
const n = Number(input);
|
||||||
|
if (Number.isNaN(n)) throw new TypeError("输入不是有效数字: " + input);
|
||||||
|
|
||||||
|
const eps = 1e-9;
|
||||||
|
const sign = n < 0 ? "负" : "";
|
||||||
|
const abs = Math.abs(n);
|
||||||
|
|
||||||
|
// 把数值四舍五入到最接近的 0.5(保证容错)
|
||||||
|
const rounded = Math.round(abs * 2) / 2;
|
||||||
|
|
||||||
|
// 分离整数和小数部分
|
||||||
|
let intPart = Math.floor(rounded + eps);
|
||||||
|
const rem = rounded - intPart; // 只可能是 0 或 0.5
|
||||||
|
|
||||||
|
// 如果 rounded 恰好是 X.0 的情况下 rem ~ 0;如果是 X.5 则 rem ~ 0.5
|
||||||
|
// 特殊:如果 rem === 1(极罕见)就进位(上面已用 round 避免)
|
||||||
|
// 小范围容错处理
|
||||||
|
const isHalf = Math.abs(rem - 0.5) < eps;
|
||||||
|
const isInteger = Math.abs(rem) < eps;
|
||||||
|
|
||||||
|
// 数字到小范围中文(只对 0/1/2 做汉字)
|
||||||
|
const smallChinese = (num) => {
|
||||||
|
if (num === 0) return "零";
|
||||||
|
if (num === 1) return "一";
|
||||||
|
if (num === 2) return "两";
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const numLabel = (() => {
|
||||||
|
const c = smallChinese(intPart);
|
||||||
|
return c !== null ? c : String(intPart);
|
||||||
|
})();
|
||||||
|
|
||||||
|
// 判断 numLabel 是阿拉伯数字还是汉字(用于决定是否在数字与“席”之间加空格)
|
||||||
|
const isDigits = /^\d+$/.test(numLabel);
|
||||||
|
|
||||||
|
if (isInteger) {
|
||||||
|
// 整数
|
||||||
|
return isDigits ? `${sign}${numLabel} 席` : `${sign}${numLabel}席`;
|
||||||
|
} else if (isHalf) {
|
||||||
|
// 半席
|
||||||
|
return isDigits ? `${sign}${numLabel} 席半` : `${sign}${numLabel}席半`;
|
||||||
|
} else {
|
||||||
|
// 理论上不会到这里(因为已 round 到 0.5),但兜底返回字符串
|
||||||
|
return `${sign}${numLabel} 席`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventTitle,
|
||||||
|
logos,
|
||||||
|
sponsorList,
|
||||||
|
specialSponsor,
|
||||||
|
showStats,
|
||||||
|
activeCategory,
|
||||||
|
categories,
|
||||||
|
stats,
|
||||||
|
filteredSponsors,
|
||||||
|
tableToSeats,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.component("sponsor-card", SponsorCard); // 全局注册
|
||||||
|
app.mount("#app");
|
||||||
Reference in New Issue
Block a user