- Expand CSS variables for primary and secondary colors to include full 50-950 scales in `main.css`. - Update components to reference specific color shades (e.g., `primary-400`, `secondary-200`) instead of generic variables. - Add dark mode background and text colors to Events, Hall of Fame, and Index sections. - Adjust image aspect ratio in the Events component.
359 lines
12 KiB
Vue
359 lines
12 KiB
Vue
<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>
|
||
<UPage class="bg-primary-400">
|
||
<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>
|
||
<UPageBody>
|
||
<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">
|
||
永平中学校友会入会申请表
|
||
</h1>
|
||
|
||
<p class="text-sm text-gray-600 my-6 text-center leading-relaxed">
|
||
兹申请加入成为永平中学校友会会员,愿遵守会规及议决案,并填此表为据。<br />
|
||
入会费 <span class="font-bold text-secondary">RM60 / 年</span>。<br />
|
||
填写此表格之后,会有理事联系您协商入会费事宜。
|
||
</p>
|
||
|
||
<form @submit.prevent="handleSubmit" class="space-y-6">
|
||
<!-- 中文姓名 -->
|
||
<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>
|
||
|
||
<!-- 英文姓名 -->
|
||
<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 -->
|
||
<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>
|
||
|
||
<!-- 电邮 -->
|
||
<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>
|
||
|
||
<!-- 电话 -->
|
||
<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>
|
||
|
||
<!-- 毕业层次 (使用 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>
|
||
|
||
<!-- 毕业年份 -->
|
||
<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>
|
||
</div>
|
||
</FormField>
|
||
|
||
<!-- 婚姻状态 -->
|
||
<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>
|
||
|
||
<!-- 国家(原生 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>
|
||
|
||
<!-- 详细地址 -->
|
||
<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">
|
||
<button
|
||
type="submit"
|
||
class="bg-secondary text-white font-bold px-8 py-2 rounded-xl shadow hover:scale-105 transition"
|
||
>
|
||
提交申请
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</UPageBody>
|
||
</UPage>
|
||
</template>
|