feat(app): introduce Hall of Fame section and refactor UI
This commit adds the '名人堂' (Hall of Fame) feature and removes the Element Plus dependency. - feat(content): Add Hall of Fame section with a new content collection, homepage component, and detail pages. - refactor(join-us): Rewrite the 'Join Us' form to remove Element Plus, using native elements and Reka UI. The form is temporarily disabled. - feat(ui): Display cover images on News and Events cards. - chore: Integrate Umami for web analytics. - fix: Correct minor text issues, including graduation year in the footer.
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<a href="https://tootaio.com" target="_blank" class="font-semibold hover:underline" style="color: #e24545;">
|
||||
Tootaio Studio
|
||||
</a>
|
||||
<span class="mt-1 text-sm text-gray-400">18 级毕业学长(麦祖奕)</span>
|
||||
<span class="mt-1 text-sm text-gray-400">2018 级毕业学长(麦祖奕)</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<nav class="space-x-6 hidden md:flex items-center">
|
||||
<a href="#news" class="hover:text-secondary">新闻</a>
|
||||
<a href="#events" class="hover:text-secondary">活动</a>
|
||||
<a href="#donate" class="hover:text-secondary">捐赠</a>
|
||||
<a href="#donate" class="hover:text-secondary">捐赠(未开放)</a>
|
||||
<a href="#about" class="hover:text-secondary">关于</a>
|
||||
<a href="/join-us"
|
||||
class="inline-flex items-center gap-2 bg-secondary text-white px-4 py-2 rounded-xl shadow hover:opacity-90">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<!-- 捐赠模块 -->
|
||||
<section id="donate" class="py-16 text-center bg-[var(--color-primary)]">
|
||||
<h3 class="text-2xl font-bold text-gray-900 mb-4">支持与捐赠</h3>
|
||||
<h3 class="text-2xl font-bold text-gray-900 mb-4">支持与捐赠(功能未开放)</h3>
|
||||
<p class="max-w-2xl mx-auto text-gray-700 mb-6">您的捐赠将用于奖学金、校园建设及校友活动发展。感谢您对母校的支持!</p>
|
||||
<a href="#" class="bg-[var(--color-secondary)] text-white px-8 py-3 rounded-xl shadow hover:opacity-90">立即捐赠</a>
|
||||
</section>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<div class="max-w-6xl mx-auto px-4">
|
||||
<h3 class="text-2xl font-bold text-gray-900 mb-6">校友活动</h3>
|
||||
<div class="grid md:grid-cols-3 gap-6">
|
||||
<div v-for="event in events" :key="event.id" class="bg-white shadow rounded-xl p-6">
|
||||
<div v-for="event in events" :key="event.id" class="bg-white shadow rounded-xl">
|
||||
<img :src="event.cover" :alt="event.title" class="rounded-xl" />
|
||||
<div class="p-6">
|
||||
<h4 class="font-semibold text-lg mb-2">{{ event.title }}</h4>
|
||||
<p class="text-sm text-gray-600 mb-1">日期:{{ useChineseDateFormat(event.date) }}</p>
|
||||
<p class="text-sm text-gray-600 mb-4">地点:{{ event.location }}</p>
|
||||
@@ -13,6 +15,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
31
app/components/index/HallOfFame.vue
Normal file
31
app/components/index/HallOfFame.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<section id="hall-of-fame" class="py-16">
|
||||
<div class="max-w-6xl mx-auto px-4">
|
||||
<h3 class="text-2xl font-bold text-center text-gray-900 mb-6">名人堂</h3>
|
||||
<div class="grid md:grid-cols-4 gap-6">
|
||||
<div v-for="person in persons" :key="person.id" class="flex flex-col items-center cursor-pointer transition hover:scale-105 hover:drop-shadow-2xl hover:-translate-y-1" @click="jumpToPersonIntro(person.path)">
|
||||
<img :src="person.photo" :alt="person.name" class="w-40 rounded-full border-secondary border-4" />
|
||||
<h4 class="text-lg font-bold">{{ person.name }}</h4>
|
||||
<p class="text-sm text-gray-500">{{ person.title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { data: persons } = await useAsyncData('hall-of-fames', () =>
|
||||
queryCollection('hallOfFames')
|
||||
.limit(4)
|
||||
.all()
|
||||
)
|
||||
|
||||
var router = useRouter()
|
||||
const jumpToPersonIntro = (path: string) => {
|
||||
router.push(path)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -18,10 +18,10 @@
|
||||
<a href="/join-us" class="bg-secondary text-white px-6 py-3 rounded-xl shadow hover:opacity-90">
|
||||
立即加入我们
|
||||
</a>
|
||||
<a href="#donate"
|
||||
<!-- <a href="#donate"
|
||||
class="bg-white border-2 border-secondary text-secondary px-6 py-3 rounded-xl hover:bg-secondary hover:text-white">
|
||||
支持捐赠
|
||||
</a>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
<h2 class="text-2xl font-bold text-gray-900 mb-6">最新新闻与公告</h2>
|
||||
<div class="grid md:grid-cols-3 gap-6">
|
||||
<article v-for="n in news" :key="n.id" @click="jumpToNewsDetail(n.stem)"
|
||||
class="bg-white rounded-xl shadow p-5 cursor-pointer transition transform hover:-translate-y-1 hover:scale-105 hover:shadow-xl duration-300 ease-in-out">
|
||||
class="bg-primary/10 rounded-xl shadow cursor-pointer transition transform hover:-translate-y-1 hover:scale-105 hover:shadow-xl duration-300 ease-in-out">
|
||||
<img class="rounded-xl" :src="n.cover" :alt="n.title">
|
||||
<div class="p-5">
|
||||
<h3 class="font-semibold mb-2">{{ n.title }}</h3>
|
||||
<div class="px-1 w-max bg-primary/25 border-primary border-2 rounded-xl text-secondary text-sm mb-2">{{
|
||||
useChineseDateFormat(n.date) }}</div>
|
||||
<p class="text-sm text-gray-600">{{ n.description }}</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
45
app/pages/hall-of-fames/[slug].vue
Normal file
45
app/pages/hall-of-fames/[slug].vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class="py-20 px-4">
|
||||
<div class="container mx-auto max-w-6xl">
|
||||
<!-- 内容渲染器 -->
|
||||
<div class="prose prose-invert prose-lg max-w-none">
|
||||
<ContentRenderer :value="person ?? {}">
|
||||
<template #empty>
|
||||
<div class="text-center py-12">
|
||||
<p class="text-gray-400 text-xl">内容加载中...</p>
|
||||
</div>
|
||||
</template>
|
||||
</ContentRenderer>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- 媒体展示区域 -->
|
||||
<section class="py-20 px-4">
|
||||
<div class="mt-16 container mx-auto max-w-6xl">
|
||||
<h2 class="text-3xl font-orbitron font-bold mb-8 text-center">图集</h2>
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
|
||||
<div v-for="i in person?.gallery" :key="i" class="group relative overflow-hidden rounded-xl cursor-pointer">
|
||||
<img :src="i" :alt="`游戏截图 ${i}`"
|
||||
class="w-full h-48 object-cover transition-transform duration-500 group-hover:scale-110" />
|
||||
<div
|
||||
class="absolute inset-0 bg-black/0 group-hover:bg-black/30 transition-all duration-300 flex items-center justify-center">
|
||||
<Icon name="mdi:like" class="w-12 h-12 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-300"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const route = useRoute()
|
||||
const { data: person } = await useAsyncData('hall-of-fames-detail', () =>
|
||||
queryCollection('hallOfFames')
|
||||
.path(`/hall-of-fames/${route.params.slug}`)
|
||||
.first()
|
||||
)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -3,6 +3,7 @@
|
||||
<IndexHero />
|
||||
<IndexNews />
|
||||
<IndexEvents />
|
||||
<IndexHallOfFame />
|
||||
<IndexDonate />
|
||||
<IndexAbout />
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,118 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, defineComponent, h } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { vMaska } from "maska/vue";
|
||||
|
||||
// Reka primitive parts we actually need
|
||||
import {
|
||||
Label,
|
||||
CheckboxRoot,
|
||||
CheckboxIndicator,
|
||||
RadioGroupRoot,
|
||||
RadioGroupItem,
|
||||
RadioGroupIndicator,
|
||||
} from 'reka-ui';
|
||||
|
||||
/**
|
||||
* Local lightweight FormField wrapper:
|
||||
* - props: label, error, for
|
||||
* - renders: <Label for="..."> + slot(default) + error paragraph
|
||||
*/
|
||||
const FormField = defineComponent({
|
||||
name: 'FormField',
|
||||
props: {
|
||||
label: { type: String, required: false },
|
||||
error: { type: String, required: false },
|
||||
for: { type: String, required: false },
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
return () =>
|
||||
h(
|
||||
'div',
|
||||
{ class: 'grid gap-2' },
|
||||
[
|
||||
props.label ? h(Label, { for: props.for }, () => props.label) : null,
|
||||
slots.default ? slots.default() : null,
|
||||
props.error
|
||||
? h('p', { class: 'text-sm text-red-600 mt-1' }, () => props.error)
|
||||
: null,
|
||||
].filter(Boolean)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// --- form state & helpers ---
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const form = reactive({
|
||||
chineseName: '',
|
||||
englishName: '',
|
||||
ic: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
gradYear: null as number | null,
|
||||
unknownGradYear: false,
|
||||
educationLevel: '',
|
||||
maritalStatus: '',
|
||||
country: '',
|
||||
address: '',
|
||||
});
|
||||
|
||||
const errors = reactive<Record<string, string>>({});
|
||||
|
||||
const toUpperCaseEnglish = () => {
|
||||
form.englishName = (form.englishName || '').toUpperCase();
|
||||
};
|
||||
|
||||
const graduationBatch = computed(() => {
|
||||
if (form.gradYear) {
|
||||
if (form.educationLevel === '高中毕业') {
|
||||
return form.gradYear - 1965;
|
||||
} else if (form.educationLevel === '初中毕业') {
|
||||
return form.gradYear - 1958;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const validate = () => {
|
||||
errors.chineseName = !form.chineseName ? '请输入中文姓名' : '';
|
||||
errors.englishName = !form.englishName ? '请输入英文姓名' : '';
|
||||
errors.ic = /^\d{6}-\d{2}-\d{4}$/.test(form.ic) ? '' : '格式应为 000000-00-0000';
|
||||
errors.email =
|
||||
(!form.email && form.country !== '马来西亚')
|
||||
? '国外居住必须填写电邮'
|
||||
: form.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)
|
||||
? '请输入有效的电邮地址'
|
||||
: '';
|
||||
errors.phone =
|
||||
!/^01\d{1}-\d{7,8}$/.test(form.phone) && !/^\+\d{1,3}\s?\d+$/.test(form.phone)
|
||||
? '请输入马来西亚号 (01x-xxxxxxx) 或带区号的号码'
|
||||
: '';
|
||||
errors.educationLevel = !form.educationLevel ? '请选择毕业层次' : '';
|
||||
errors.gradYear =
|
||||
!form.unknownGradYear && !form.gradYear ? '请输入毕业年份或勾选“不详”' : '';
|
||||
errors.maritalStatus = !form.maritalStatus ? '请选择婚姻状态' : '';
|
||||
errors.country = !form.country ? '请选择国家' : '';
|
||||
errors.address = !form.address ? '请输入详细地址' : '';
|
||||
|
||||
return Object.values(errors).every((e) => !e);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (validate()) {
|
||||
// 如果你已在根组件挂载 Reka 的 ToastProvider + useToast,可替换下面 alert 的实现(见备注)
|
||||
alert('提交成功!理事会将尽快联系您。');
|
||||
} else {
|
||||
alert('请完善表单信息');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="cursor-not-allowed fixed flex items-center justify-center min-h-screen min-w-screen bg-black opacity-50">
|
||||
<p class="text-white text-2xl">此功能尚未开放,敬请期待。谢谢</p>
|
||||
</div>
|
||||
<div class="min-h-screen bg-primary py-10">
|
||||
<div class="max-w-3xl mx-auto p-8 bg-white rounded-2xl shadow-lg">
|
||||
<h1 class="text-3xl font-bold mb-6 text-center text-secondary">
|
||||
@@ -11,202 +125,181 @@
|
||||
填写此表格之后,会有理事联系您协商入会费事宜。
|
||||
</p>
|
||||
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="130px" status-icon>
|
||||
<form @submit.prevent="handleSubmit" class="space-y-6">
|
||||
<!-- 中文姓名 -->
|
||||
<el-form-item label="中文姓名" prop="chineseName">
|
||||
<el-input v-model="form.chineseName" placeholder="请输入中文姓名" />
|
||||
</el-form-item>
|
||||
<FormField label="中文姓名" :error="errors.chineseName" for="chineseName">
|
||||
<input
|
||||
id="chineseName"
|
||||
v-model="form.chineseName"
|
||||
class="w-full border rounded px-3 py-2"
|
||||
placeholder="请输入中文姓名"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- 英文姓名 (自动转大写) -->
|
||||
<el-form-item label="英文姓名" prop="englishName">
|
||||
<el-input v-model="form.englishName" placeholder="请输入英文姓名" @input="toUpperCaseEnglish" />
|
||||
</el-form-item>
|
||||
<!-- 英文姓名 -->
|
||||
<FormField label="英文姓名" :error="errors.englishName" for="englishName">
|
||||
<input
|
||||
id="englishName"
|
||||
v-model="form.englishName"
|
||||
@input="toUpperCaseEnglish"
|
||||
class="w-full border rounded px-3 py-2"
|
||||
placeholder="请输入英文姓名"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- IC -->
|
||||
<el-form-item label="IC" prop="ic">
|
||||
<el-input v-model="form.ic" placeholder="000000-00-0000" v-maska="'######-##-####'" />
|
||||
</el-form-item>
|
||||
<FormField label="IC" :error="errors.ic" for="ic">
|
||||
<input
|
||||
id="ic"
|
||||
v-model="form.ic"
|
||||
class="w-full border rounded px-3 py-2"
|
||||
placeholder="000000-00-0000"
|
||||
v-maska="'######-##-####'"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- 电邮 -->
|
||||
<el-form-item label="电邮" prop="email">
|
||||
<el-input v-model="form.email" placeholder="选填 / 国外必填"
|
||||
v-maska="['###-#######', '###-########', '+#############']" />
|
||||
</el-form-item>
|
||||
<FormField label="电邮" :error="errors.email" for="email">
|
||||
<input
|
||||
id="email"
|
||||
v-model="form.email"
|
||||
class="w-full border rounded px-3 py-2"
|
||||
placeholder="选填 / 国外必填"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- 电话 -->
|
||||
<el-form-item label="电话" prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入电话(使用 WhatsApp 号码为佳,可加入校友会群组)" />
|
||||
</el-form-item>
|
||||
<FormField label="电话" :error="errors.phone" for="phone">
|
||||
<input
|
||||
id="phone"
|
||||
v-model="form.phone"
|
||||
class="w-full border rounded px-3 py-2"
|
||||
placeholder="请输入电话(WhatsApp 号码为佳)"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- 毕业层次 -->
|
||||
<el-form-item label="毕业层次" prop="educationLevel">
|
||||
<el-radio-group v-model="form.educationLevel">
|
||||
<el-radio label="初中毕业">初中毕业</el-radio>
|
||||
<el-radio label="高中毕业">高中毕业</el-radio>
|
||||
<el-radio label="辍学/转学肄业">辍学/转学肄业</el-radio>
|
||||
<el-radio label="不确定">不确定</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 毕业层次 (使用 Reka Radio primitives) -->
|
||||
<FormField label="毕业层次" :error="errors.educationLevel" for="educationLevel">
|
||||
<RadioGroupRoot
|
||||
v-model="form.educationLevel"
|
||||
class="flex flex-col gap-2"
|
||||
name="educationLevel"
|
||||
>
|
||||
<RadioGroupItem value="初中毕业" class="flex items-center gap-3">
|
||||
<RadioGroupIndicator class="w-4 h-4 rounded-full border flex items-center justify-center">
|
||||
<span class="block w-2 h-2 rounded-full bg-secondary" />
|
||||
</RadioGroupIndicator>
|
||||
<span>初中毕业</span>
|
||||
</RadioGroupItem>
|
||||
|
||||
<RadioGroupItem value="高中毕业" class="flex items-center gap-3">
|
||||
<RadioGroupIndicator class="w-4 h-4 rounded-full border flex items-center justify-center">
|
||||
<span class="block w-2 h-2 rounded-full bg-secondary" />
|
||||
</RadioGroupIndicator>
|
||||
<span>高中毕业</span>
|
||||
</RadioGroupItem>
|
||||
|
||||
<RadioGroupItem value="辍学/转学肄业" class="flex items-center gap-3">
|
||||
<RadioGroupIndicator class="w-4 h-4 rounded-full border flex items-center justify-center">
|
||||
<span class="block w-2 h-2 rounded-full bg-secondary" />
|
||||
</RadioGroupIndicator>
|
||||
<span>辍学/转学肄业</span>
|
||||
</RadioGroupItem>
|
||||
|
||||
<RadioGroupItem value="不确定" class="flex items-center gap-3">
|
||||
<RadioGroupIndicator class="w-4 h-4 rounded-full border flex items-center justify-center">
|
||||
<span class="block w-2 h-2 rounded-full bg-secondary" />
|
||||
</RadioGroupIndicator>
|
||||
<span>不确定</span>
|
||||
</RadioGroupItem>
|
||||
</RadioGroupRoot>
|
||||
</FormField>
|
||||
|
||||
<!-- 毕业年份 -->
|
||||
<el-form-item label="毕业年份" prop="gradYear">
|
||||
<el-input-number v-model="form.gradYear" :min="1957" :max="currentYear" :step="1"
|
||||
:disabled="form.unknownGradYear" />
|
||||
<el-checkbox v-model="form.unknownGradYear">毕业年份不详</el-checkbox>
|
||||
<span class="text-sm text-gray-500 ml-2" v-if="graduationBatch">
|
||||
<FormField label="毕业年份" :error="errors.gradYear" for="gradYear">
|
||||
<div class="flex items-center gap-3">
|
||||
<input
|
||||
id="gradYear"
|
||||
type="number"
|
||||
v-model="form.gradYear"
|
||||
:min="1957"
|
||||
:max="currentYear"
|
||||
:disabled="form.unknownGradYear"
|
||||
class="w-32 border rounded px-3 py-2"
|
||||
/>
|
||||
<label class="flex items-center gap-2 select-none">
|
||||
<CheckboxRoot v-model="form.unknownGradYear" class="w-5 h-5 rounded border flex items-center justify-center">
|
||||
<CheckboxIndicator class="flex items-center justify-center w-full h-full">
|
||||
<Icon icon="radix-icons:check" class="h-4 w-4 text-secondary" />
|
||||
</CheckboxIndicator>
|
||||
</CheckboxRoot>
|
||||
<span>毕业年份不详</span>
|
||||
</label>
|
||||
|
||||
<span class="text-sm text-gray-500" v-if="graduationBatch">
|
||||
您是第 <span class="font-bold">{{ graduationBatch }}</span> 届毕业生
|
||||
</span>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
<!-- 婚姻状态 -->
|
||||
<el-form-item label="婚姻状态" prop="maritalStatus">
|
||||
<el-radio-group v-model="form.maritalStatus">
|
||||
<el-radio label="未婚">未婚</el-radio>
|
||||
<el-radio label="已婚">已婚</el-radio>
|
||||
<el-radio label="其他">其他</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<FormField label="婚姻状态" :error="errors.maritalStatus" for="maritalStatus">
|
||||
<div class="flex flex-col gap-2">
|
||||
<RadioGroupRoot v-model="form.maritalStatus" name="maritalStatus">
|
||||
<RadioGroupItem value="未婚" class="flex items-center gap-3">
|
||||
<RadioGroupIndicator class="w-4 h-4 rounded-full border flex items-center justify-center">
|
||||
<span class="block w-2 h-2 rounded-full bg-secondary" />
|
||||
</RadioGroupIndicator>
|
||||
<span>未婚</span>
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem value="已婚" class="flex items-center gap-3">
|
||||
<RadioGroupIndicator class="w-4 h-4 rounded-full border flex items-center justify-center">
|
||||
<span class="block w-2 h-2 rounded-full bg-secondary" />
|
||||
</RadioGroupIndicator>
|
||||
<span>已婚</span>
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem value="其他" class="flex items-center gap-3">
|
||||
<RadioGroupIndicator class="w-4 h-4 rounded-full border flex items-center justify-center">
|
||||
<span class="block w-2 h-2 rounded-full bg-secondary" />
|
||||
</RadioGroupIndicator>
|
||||
<span>其他</span>
|
||||
</RadioGroupItem>
|
||||
</RadioGroupRoot>
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
<!-- 国家选择 -->
|
||||
<el-form-item label="国家" prop="country">
|
||||
<el-select v-model="form.country" placeholder="请选择国家">
|
||||
<el-option label="马来西亚" value="马来西亚" />
|
||||
<el-option label="新加坡" value="新加坡" />
|
||||
<el-option label="中国" value="中国" />
|
||||
<el-option label="美国" value="美国" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 国家(原生 select,简单且稳定) -->
|
||||
<FormField label="国家" :error="errors.country" for="country">
|
||||
<select id="country" v-model="form.country" class="w-full border rounded px-3 py-2">
|
||||
<option value="" disabled>请选择国家</option>
|
||||
<option>马来西亚</option>
|
||||
<option>新加坡</option>
|
||||
<option>中国</option>
|
||||
<option>美国</option>
|
||||
<option>其他</option>
|
||||
</select>
|
||||
</FormField>
|
||||
|
||||
<!-- 详细地址 -->
|
||||
<el-form-item label="详细地址" prop="address">
|
||||
<el-input type="textarea" v-model="form.address" placeholder="请输入现居详细地址" />
|
||||
</el-form-item>
|
||||
<FormField label="详细地址" :error="errors.address" for="address">
|
||||
<textarea
|
||||
id="address"
|
||||
v-model="form.address"
|
||||
class="w-full border rounded px-3 py-2"
|
||||
placeholder="请输入现居详细地址"
|
||||
rows="4"
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<div class="text-center mt-8">
|
||||
<el-button type="warning"
|
||||
class="bg-secondary border-none px-8 py-2 rounded-xl text-white font-bold shadow hover:scale-105 transition"
|
||||
@click="handleSubmit">
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-secondary text-white font-bold px-8 py-2 rounded-xl shadow hover:scale-105 transition"
|
||||
>
|
||||
提交申请
|
||||
</el-button>
|
||||
</button>
|
||||
</div>
|
||||
</el-form>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { vMaska } from 'maska/vue'
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const formRef = ref(null);
|
||||
|
||||
const form = reactive({
|
||||
chineseName: "",
|
||||
englishName: "",
|
||||
ic: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
gradYear: null,
|
||||
maritalStatus: "",
|
||||
country: "",
|
||||
address: "",
|
||||
});
|
||||
|
||||
// 英文名自动转大写
|
||||
const toUpperCaseEnglish = () => {
|
||||
form.englishName = form.englishName.toUpperCase();
|
||||
};
|
||||
|
||||
// 1966 年为第一届高中生毕业年份。由此根据 gradYear 计算毕业届别
|
||||
const graduationBatch = computed(() => {
|
||||
if (form.gradYear) {
|
||||
if (form.educationLevel === "高中毕业") {
|
||||
return form.gradYear - 1965; // 高中届别
|
||||
} else if (form.educationLevel === "初中毕业") {
|
||||
return form.gradYear - 1958; // 假设 1959 第一届初中
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
chineseName: [{ required: true, message: "请输入中文姓名", trigger: "blur" }],
|
||||
englishName: [{ required: true, message: "请输入英文姓名", trigger: "blur" }],
|
||||
ic: [
|
||||
{ required: true, message: "请输入 IC", trigger: "blur" },
|
||||
{
|
||||
pattern: /^\d{6}-\d{2}-\d{4}$/,
|
||||
message: "格式应为 000000-00-0000",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
email: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value && form.country !== "马来西亚") {
|
||||
callback(new Error("国外居住必须填写电邮"));
|
||||
} else if (value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
||||
callback(new Error("请输入有效的电邮地址"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: "请输入电话", trigger: "blur" },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (/^01\d{1}-\d{7,8}$/.test(value)) {
|
||||
callback();
|
||||
} else if (/^\+\d{1,3}\s?\d+$/.test(value)) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("请输入马来西亚号 (01x-xxxxxxx) 或带区号的号码"));
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
educationLevel: [
|
||||
{ required: true, message: "请选择毕业层次", trigger: "change" },
|
||||
],
|
||||
gradYear: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (!form.unknownGradYear && !value) {
|
||||
callback(new Error("请输入毕业年份或勾选“毕业年份不详”"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
maritalStatus: [
|
||||
{ required: true, message: "请选择婚姻状态", trigger: "change" },
|
||||
],
|
||||
country: [{ required: true, message: "请选择国家", trigger: "change" }],
|
||||
address: [{ required: true, message: "请输入详细地址", trigger: "blur" }],
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = () => {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
ElMessage.success("提交成功!理事会将尽快联系您。");
|
||||
} else {
|
||||
ElMessage.error("请完善表单信息");
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -11,6 +11,7 @@ export default defineContentConfig({
|
||||
subtitle: z.string(),
|
||||
date: z.coerce.date(),
|
||||
location: z.string(),
|
||||
cover: z.string().url(),
|
||||
}),
|
||||
}),
|
||||
// 新闻集合
|
||||
@@ -32,5 +33,17 @@ export default defineContentConfig({
|
||||
ogImage: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
// 名人堂
|
||||
hallOfFames: defineCollection({
|
||||
type: "page",
|
||||
source: "hall-of-fames/*md",
|
||||
schema: z.object({
|
||||
name: z.string(),
|
||||
photo: z.string().url(),
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
gallery: z.array(z.string()),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,7 +3,47 @@ title: "927 永中 • 钟意你"
|
||||
subtitle: "永中校友会 39周年庆"
|
||||
date: "2025-09-27"
|
||||
location: "永平中学校园"
|
||||
cover: "http://img.tootaio.com/i/2025/10/03/guw8wo.jpg"
|
||||
---
|
||||
# 永中校友会39周年会庆午宴 温馨欢聚350人
|
||||
|
||||

|
||||
来宾打卡照(星洲日报记者,邹智敏摄)
|
||||
|
||||
永平中学校友会于 9 月 27 日举办“927 永中·钟意你”39 周年会庆午宴,吸引约 350 位校友与社会贤达齐聚一堂,共度温馨时光,场面热闹盛大。
|
||||
|
||||

|
||||
与会嘉宾向永中校友会献上祝福,左起:许敏捷、王飞兴、林添顺、李煜斌、刘镇东、蓝宜宏、傅庆隆、张嘉群及郑凯聪(星洲日报记者,邹智敏摄)
|
||||
|
||||
本次会庆出席嘉宾包括:
|
||||
|
||||
* 投资、贸易及工业部副部长兼依城区国会议员 **刘镇东** 及其政治秘书 **郑凯聪**
|
||||
* 柔佛州行政议员兼永平区州议员 **林添顺**
|
||||
* 亚依淡区国会议员拿督斯里魏家祥代表 **余养铭**
|
||||
* 永中校友会顾问兼永中校长 **张嘉群**
|
||||
* 永中署理董事长 **郑珠洋**、副董事长 **许敏捷**、**王飞兴**、**傅庆隆**
|
||||
* 永平留台同学会会长兼校董 **陈贵花**
|
||||
* 永中校友会主席 **李煜斌**
|
||||
* 会庆工委会主席 **蓝宜宏** 等。
|
||||
|
||||
## 嘉宾致词与祝福
|
||||
|
||||
刘镇东副部长在致词时表示,虽然永中规模不大,仅有 700 余名学生,但其发展潜力无限。他期望永中在校友与社会各界的支持下,走出独特的办学道路,塑造自身特色。
|
||||
|
||||
林添顺州议员则指出,他与魏家祥国会议员将持续关注并支持永中的建设,尤其期盼 **新校园计划** 能早日实现。
|
||||
|
||||
此外,刘镇东与林添顺也慷慨捐赠各 5000 令吉,以实际行动支持校友会发展。
|
||||
|
||||
## 系列活动与感谢
|
||||
|
||||
为配合本次周年会庆,校友会于早前已举办“永中之星 2.0”歌唱赛及球类比赛,获得校友和热心人士的积极参与与赞助。李煜斌主席及蓝宜宏主席分别代表校友会与工委会,向所有支持者致以衷心感谢。
|
||||
|
||||
## 精彩表演与温馨氛围
|
||||
|
||||
午宴当天,永中二十四节令鼓队与舞蹈社学员带来精彩表演,“永中之星 1.0”歌唱赛校友组冠军 **黄秋慧** 与学生组冠军 **林妤桐** 亦倾情献唱。现场欢声笑语不断,气氛热烈温馨,为会庆增添了浓厚的节日色彩。
|
||||
|
||||
---
|
||||
|
||||
# 午宴 • 征信录
|
||||
|
||||
## 乐捐名单
|
||||
|
||||
33
content/hall-of-fames/he-si-rong.md
Normal file
33
content/hall-of-fames/he-si-rong.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: "何四荣学长"
|
||||
photo: "http://img.tootaio.com/i/2025/10/03/ff51je.png"
|
||||
title: "画家、作家与文化记录者"
|
||||
description: "马来西亚柔佛永平人,永平中学 1974 年毕业生。何学长为资深当代画家、作家与文化记录者,曾于吉隆坡美术学院深造,其创作多以「波光倒影 / 流动色彩」为主题,作品抽象且具内在意象,曾受媒体专访并在多项展览与校内艺术联展中担任特邀艺术家。"
|
||||
gallery:
|
||||
[
|
||||
"https://i.ytimg.com/vi/Nu50FOvrGhI/maxresdefault.jpg",
|
||||
"https://thumb.artron.net/Img/image?c=0&h=0&src=https%3A%2F%2Fimg10.artimg.net%2Fpublic%2Fbeian%2Fpng%2F202301%2Fefc77b56df4478d28bab8c00a7298669.png&w=800",
|
||||
"https://images.openai.com/thumbnails/45906510beb1aadaab0a2db3fc34a8e3.jpeg"
|
||||
]
|
||||
---
|
||||
# 何四荣学长
|
||||
|
||||
## 基本背景
|
||||
|
||||
何四荣学长出生并成长于柔佛州永平,系永平中学 1974 年届(第九届)毕业生,之后赴吉隆坡深造艺术相关学业。作为家乡出身的艺术家,他长期活跃于本地与区域艺术圈,並多次受邀参与校内外的艺术活动与联展。([vtour.my](https://vtour.my/local-artist-%E6%9C%AC%E5%9C%9F%E7%94%BB%E5%AE%B6/?utm_source=chatgpt.com "Local artist 本土画家| VTOUR"))
|
||||
|
||||
## 艺术风格与创作
|
||||
|
||||
何学长的绘画由写实逐步走向抽象,长期以「波光、倒影、流动颜色」为重要意象,作品强调色彩律动与内在心境的呈现。他在媒体专访中详述创作由来與心境表述,作品常以抽象的水面与光影映照人的内在世界,风格温和而富有节奏感。
|
||||
|
||||
## 出版与文化保存
|
||||
|
||||
除了视觉创作外,何四荣亦著有家族史与在地记忆类作品(例如私人出版的家族回忆录《百年回首》),致力以口述史、家族与地方史料保存家乡记忆,受到地方媒体与学者关注。([诗华日报](https://news.seehua.com/post/87615?utm_source=chatgpt.com "大锅饭的记忆"))
|
||||
|
||||
## 与永平中学的连结
|
||||
|
||||
何学长长期支持母校相关文化与艺术活动:曾作为永平中学多项校内艺术联展的特邀画家,并参与学校组织之虚拟联展与访谈,持续以个人影响力推动校内文化传播与艺术教育。([Facebook](https://www.facebook.com/yongpenghighschool/photos/%E6%9D%A5%E8%87%AA%E9%A9%AC%E6%9D%A5%E8%A5%BF%E4%BA%9A%E7%9A%84%E4%BD%95%E5%9B%9B%E8%8D%A3%E5%85%88%E7%94%9F%E4%B8%BA1221%E6%B0%B8%E5%B9%B3%E4%B8%AD%E5%AD%A6%E7%96%AB%E8%89%BA%E8%99%9A%E6%8B%9F%E8%89%BA%E6%9C%AF%E5%88%9B%E4%BD%9C%E8%81%94%E5%B1%95%E7%89%B9%E9%82%80%E7%94%BB%E5%AE%B6%E4%B9%8B%E4%B8%80/10159323761117302/?locale=ms_MY&utm_source=chatgpt.com "来自马来西亚的何四荣先生为1221永平中学疫•艺虚拟艺术 ..."))
|
||||
|
||||
## 展览与媒体
|
||||
|
||||
何学长曾接受专业艺术媒体采访(如艺术网/雅昌等),并在画展与专文中被介绍为“作品与本人气质高度吻合”的艺术家;其近年个展与群展常以“心境”“波光倒影”等题名,展现其数十年创作轨迹。
|
||||
28
content/hall-of-fames/ma-wong-ching.md
Normal file
28
content/hall-of-fames/ma-wong-ching.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: "马文清学长"
|
||||
photo: "http://img.tootaio.com/i/2025/10/03/fgbegj.png"
|
||||
title: "俐马集团董事长、永平中学董事长"
|
||||
---
|
||||
# 马文清学长
|
||||
|
||||
**马文清(1951年生,马来西亚柔佛州永平人)**
|
||||
|
||||
现任 **俐马集团(Ramatex Berhad)董事长** 、 **永平中学董事长** 。
|
||||
|
||||
马文清校友早年赴台湾就读逢甲大学海外青年技术训练班(纺织科),1972年毕业后返马投身纺织产业。1973年,他与胞弟马文明及校友共同创业,创立向荣针织公司,1976年扩展为 **俐马集团** ,并亲自担任董事长,带领企业走向国际。集团现已在马来西亚、中国、越南、柬埔寨及约旦设厂,产品行销全球,长期入选《亚洲周刊》国际华商500强,被誉为 **马来西亚纺织业巨擘** 。
|
||||
|
||||
在事业成就之外,马文清校友始终秉持「 **再穷不能穷教育** 」的理念,热心公益、扶植教育。作为永平中学董事长,他累计捐资 **超过5,600万令吉** ,并慷慨捐赠33亩土地兴建新校区,大力改善校舍设备、设立助学金,推动课程与师资改革,积极拓展国际交流,奠定永中迈向优质化与国际化的坚实基础。他同时长期支持台湾高教,捐赠逢甲大学逾新台币1.2亿元,用于奖助学金与校务发展。
|
||||
|
||||
在文化推广方面,他于2019年推动成立「 **永平中学丛书工作坊** 」,出版涵盖地方史、社会评论与时代议题的系列著作,成为华文教育与文化传承的重要典范。
|
||||
|
||||
凭借其对产业、教育与社会的卓越贡献,马文清校友荣获:
|
||||
|
||||
* **逢甲大学第五届杰出校友(2003年)**
|
||||
* **逢甲大学荣誉博士学位(2023年,第8位获此荣衔校友)**
|
||||
* **2025全球杰出僑生校友 企业工商奖**
|
||||
|
||||
马文清校友以企业家的远见、教育家的情怀与社会责任感,书写了跨越产业与公益的荣耀篇章,堪为永中人之典范。
|
||||
|
||||
---
|
||||
|
||||
要不要我帮你再优化成 **官网专用排版** (例如加上年表、重点标签、照片位置说明),这样放在网页会更美观 📌?
|
||||
@@ -4,7 +4,7 @@ date: "2025-10-01"
|
||||
updated: "2025-10-01"
|
||||
author: "麦祖奕学长"
|
||||
description: "永中校友会官网正式上线,为校友提供最新资讯、活动报名及互动交流平台。"
|
||||
cover: "/images/news/news-launch.jpg"
|
||||
cover: "http://img.tootaio.com/i/2025/10/02/t1pq4s.png"
|
||||
tags: ["活动", "公告", "产品更新"]
|
||||
category: "通知"
|
||||
highlight: true
|
||||
@@ -12,7 +12,6 @@ seoTitle: "永中校友会官网上线 | 最新活动与资讯平台"
|
||||
seoDescription: "永中校友会官网正式上线,校友可在平台获取最新资讯、报名活动及参与互动。"
|
||||
ogImage: "/images/og/news-launch.jpg"
|
||||
---
|
||||
|
||||
永中校友会官网正式上线啦!🎉
|
||||
|
||||
经过团队数月的精心策划与开发,永中校友会官网现已正式对外开放,为广大校友提供了一个便捷、高效的线上交流与信息获取平台。
|
||||
@@ -24,12 +23,6 @@ ogImage: "/images/og/news-launch.jpg"
|
||||
- **校友互动**:建立校友档案,参与讨论,扩展人脉网络。
|
||||
- **资源分享**:提供校友资源与信息共享平台,助力校友发展。
|
||||
|
||||
## 使用指南
|
||||
|
||||
1. 打开官网 [twww.tootaio.com](https://www.tootaio.com)。
|
||||
2. 注册或登录账号,完善个人信息。
|
||||
3. 浏览新闻、参与活动或发布信息,即刻融入校友网络。
|
||||
|
||||
官网的上线标志着永中校友会迈向数字化、智能化管理的新阶段,我们期待每位校友积极参与,一起构建温暖、活跃的校友社区。
|
||||
|
||||
> **特别鸣谢**:本次官网开发由 **Tootaio Studio** 负责设计与技术支持,由 **18 级毕业生麦祖奕** 指导策划。
|
||||
> **特别鸣谢**:本次官网开发由 **Tootaio Studio** 负责设计与技术支持,由 **2018 级毕业生麦祖奕** 策划开发。
|
||||
|
||||
@@ -4,23 +4,20 @@ import tailwindcss from "@tailwindcss/vite";
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: "2025-07-15",
|
||||
devtools: { enabled: true },
|
||||
modules: ["@nuxt/content", "@nuxt/image", "@nuxt/ui", "@element-plus/nuxt"],
|
||||
modules: ["@nuxt/content", "@nuxt/image", "@nuxt/ui", "reka-ui/nuxt"],
|
||||
css: ["~/assets/css/main.css"],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
elementPlus: {
|
||||
importStyle: "scss",
|
||||
themeChalk: {
|
||||
$colors: {
|
||||
primary: { base: "#fb9e3a" },
|
||||
default: { base: "#fcef91" },
|
||||
},
|
||||
},
|
||||
},
|
||||
app: {
|
||||
head: {
|
||||
title: "永平中学校友会",
|
||||
script: [
|
||||
{
|
||||
src: "/analytics.js",
|
||||
tagPosition: "head",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@element-plus/nuxt": "1.1.4",
|
||||
"@nuxt/content": "3.7.1",
|
||||
"@nuxt/image": "1.11.0",
|
||||
"@nuxt/ui": "4.0.0",
|
||||
@@ -21,10 +19,12 @@
|
||||
"html2pdf.js": "^0.12.1",
|
||||
"maska": "^3.2.0",
|
||||
"nuxt": "^4.1.2",
|
||||
"reka-ui": "^2.5.1",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"typescript": "^5.9.2",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.1",
|
||||
"vue-sonner": "^2.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sass-embedded": "^1.93.2"
|
||||
|
||||
87
pnpm-lock.yaml
generated
87
pnpm-lock.yaml
generated
@@ -8,12 +8,6 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@element-plus/icons-vue':
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2(vue@3.5.22(typescript@5.9.2))
|
||||
'@element-plus/nuxt':
|
||||
specifier: 1.1.4
|
||||
version: 1.1.4(@element-plus/icons-vue@2.3.2(vue@3.5.22(typescript@5.9.2)))(element-plus@2.11.4(vue@3.5.22(typescript@5.9.2)))(magicast@0.3.5)
|
||||
'@nuxt/content':
|
||||
specifier: 3.7.1
|
||||
version: 3.7.1(better-sqlite3@12.4.1)(magicast@0.3.5)
|
||||
@@ -41,6 +35,9 @@ importers:
|
||||
nuxt:
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.22)(better-sqlite3@12.4.1)(db0@0.3.2(better-sqlite3@12.4.1))(ioredis@5.8.0)(lightningcss@1.30.1)(magicast@0.3.5)(rollup@4.52.3)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)(vite@7.1.7(jiti@2.6.0)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1)
|
||||
reka-ui:
|
||||
specifier: ^2.5.1
|
||||
version: 2.5.1(typescript@5.9.2)(vue@3.5.22(typescript@5.9.2))
|
||||
tailwindcss:
|
||||
specifier: ^4.1.13
|
||||
version: 4.1.13
|
||||
@@ -53,6 +50,9 @@ importers:
|
||||
vue-router:
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(vue@3.5.22(typescript@5.9.2))
|
||||
vue-sonner:
|
||||
specifier: ^2.0.9
|
||||
version: 2.0.9(@nuxt/kit@4.1.2(magicast@0.3.5))(@nuxt/schema@4.1.2)(nuxt@4.1.2(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.22)(better-sqlite3@12.4.1)(db0@0.3.2(better-sqlite3@12.4.1))(ioredis@5.8.0)(lightningcss@1.30.1)(magicast@0.3.5)(rollup@4.52.3)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)(vite@7.1.7(jiti@2.6.0)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1))
|
||||
devDependencies:
|
||||
sass-embedded:
|
||||
specifier: ^1.93.2
|
||||
@@ -245,12 +245,6 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
'@element-plus/nuxt@1.1.4':
|
||||
resolution: {integrity: sha512-tDGpJgzbu/4of9nsjDqVD34FJYH8bFHB7xDh5ePfP3RsRQLsCw9SjNpMR4o+6wUfJACl0tydbC/1lsYY0HT8gw==}
|
||||
peerDependencies:
|
||||
'@element-plus/icons-vue': '>=0.2.6'
|
||||
element-plus: '>=2'
|
||||
|
||||
'@emnapi/core@1.5.0':
|
||||
resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==}
|
||||
|
||||
@@ -2968,10 +2962,6 @@ packages:
|
||||
resolution: {integrity: sha512-8ngQgLhcT0t3YBdn9CGkZqCYlvwW9pm7aWJwd7AxseVWf1RU8ZHCQvG1mt3N5vvUme+pXTcHB8G/7fE666U8Vw==}
|
||||
engines: {node: '>=20.18.0'}
|
||||
|
||||
magic-string@0.27.0:
|
||||
resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
magic-string@0.30.19:
|
||||
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
|
||||
|
||||
@@ -3774,6 +3764,11 @@ packages:
|
||||
peerDependencies:
|
||||
vue: '>= 3.2.0'
|
||||
|
||||
reka-ui@2.5.1:
|
||||
resolution: {integrity: sha512-QJGB3q21wQ1Kw28HhhNDpjfFe8qpePX1gK4FTBRd68XTh9aEnhR5bTJnlV0jxi8FBPh0xivZBeNFUc3jiGx7mQ==}
|
||||
peerDependencies:
|
||||
vue: '>= 3.2.0'
|
||||
|
||||
remark-emoji@5.0.2:
|
||||
resolution: {integrity: sha512-IyIqGELcyK5AVdLFafoiNww+Eaw/F+rGrNSXoKucjo95uL267zrddgxGM83GN1wFIb68pyDuAsY3m5t2Cav1pQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -4408,10 +4403,6 @@ packages:
|
||||
vue-router:
|
||||
optional: true
|
||||
|
||||
unplugin@1.16.1:
|
||||
resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
unplugin@2.3.10:
|
||||
resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
@@ -4660,6 +4651,20 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
vue-sonner@2.0.9:
|
||||
resolution: {integrity: sha512-i6BokNlNDL93fpzNxN/LZSn6D6MzlO+i3qXt6iVZne3x1k7R46d5HlFB4P8tYydhgqOrRbIZEsnRd3kG7qGXyw==}
|
||||
peerDependencies:
|
||||
'@nuxt/kit': ^4.0.3
|
||||
'@nuxt/schema': ^4.0.3
|
||||
nuxt: ^4.0.3
|
||||
peerDependenciesMeta:
|
||||
'@nuxt/kit':
|
||||
optional: true
|
||||
'@nuxt/schema':
|
||||
optional: true
|
||||
nuxt:
|
||||
optional: true
|
||||
|
||||
vue@3.5.22:
|
||||
resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==}
|
||||
peerDependencies:
|
||||
@@ -5021,16 +5026,6 @@ snapshots:
|
||||
dependencies:
|
||||
vue: 3.5.22(typescript@5.9.2)
|
||||
|
||||
'@element-plus/nuxt@1.1.4(@element-plus/icons-vue@2.3.2(vue@3.5.22(typescript@5.9.2)))(element-plus@2.11.4(vue@3.5.22(typescript@5.9.2)))(magicast@0.3.5)':
|
||||
dependencies:
|
||||
'@element-plus/icons-vue': 2.3.2(vue@3.5.22(typescript@5.9.2))
|
||||
'@nuxt/kit': 3.19.2(magicast@0.3.5)
|
||||
element-plus: 2.11.4(vue@3.5.22(typescript@5.9.2))
|
||||
magic-string: 0.27.0
|
||||
unplugin: 1.16.1
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@emnapi/core@1.5.0':
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.1.0
|
||||
@@ -8101,10 +8096,6 @@ snapshots:
|
||||
dependencies:
|
||||
magic-string: 0.30.19
|
||||
|
||||
magic-string@0.27.0:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
magic-string@0.30.19:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -9356,6 +9347,23 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- typescript
|
||||
|
||||
reka-ui@2.5.1(typescript@5.9.2)(vue@3.5.22(typescript@5.9.2)):
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.4
|
||||
'@floating-ui/vue': 1.1.9(vue@3.5.22(typescript@5.9.2))
|
||||
'@internationalized/date': 3.9.0
|
||||
'@internationalized/number': 3.6.5
|
||||
'@tanstack/vue-virtual': 3.13.12(vue@3.5.22(typescript@5.9.2))
|
||||
'@vueuse/core': 12.8.2(typescript@5.9.2)
|
||||
'@vueuse/shared': 12.8.2(typescript@5.9.2)
|
||||
aria-hidden: 1.2.6
|
||||
defu: 6.1.4
|
||||
ohash: 2.0.11
|
||||
vue: 3.5.22(typescript@5.9.2)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- typescript
|
||||
|
||||
remark-emoji@5.0.2:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
@@ -10133,11 +10141,6 @@ snapshots:
|
||||
- typescript
|
||||
- vue
|
||||
|
||||
unplugin@1.16.1:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
webpack-virtual-modules: 0.6.2
|
||||
|
||||
unplugin@2.3.10:
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
@@ -10338,6 +10341,12 @@ snapshots:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.22(typescript@5.9.2)
|
||||
|
||||
vue-sonner@2.0.9(@nuxt/kit@4.1.2(magicast@0.3.5))(@nuxt/schema@4.1.2)(nuxt@4.1.2(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.22)(better-sqlite3@12.4.1)(db0@0.3.2(better-sqlite3@12.4.1))(ioredis@5.8.0)(lightningcss@1.30.1)(magicast@0.3.5)(rollup@4.52.3)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)(vite@7.1.7(jiti@2.6.0)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1)):
|
||||
optionalDependencies:
|
||||
'@nuxt/kit': 4.1.2(magicast@0.3.5)
|
||||
'@nuxt/schema': 4.1.2
|
||||
nuxt: 4.1.2(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.22)(better-sqlite3@12.4.1)(db0@0.3.2(better-sqlite3@12.4.1))(ioredis@5.8.0)(lightningcss@1.30.1)(magicast@0.3.5)(rollup@4.52.3)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(typescript@5.9.2)(vite@7.1.7(jiti@2.6.0)(lightningcss@1.30.1)(sass-embedded@1.93.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1)
|
||||
|
||||
vue@3.5.22(typescript@5.9.2):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.22
|
||||
|
||||
10
public/analytics.js
Normal file
10
public/analytics.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function () {
|
||||
const UMAMI_SCRIPT_JS = "https://umami.tootaio.com/script.js";
|
||||
const UMAMI_ID = "32769237-81da-4983-aec3-3c1ff9240306";
|
||||
|
||||
var script = document.createElement("script");
|
||||
script.async = true;
|
||||
script.src = UMAMI_SCRIPT_JS;
|
||||
script.setAttribute("data-website-id", UMAMI_ID);
|
||||
document.head.appendChild(script);
|
||||
})();
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 50 KiB |
Reference in New Issue
Block a user