Files
yphsalumni.org/app/pages/join-us/index.vue
xiaomai cd414542a9 style(theme): expand color palettes and refine dark mode styles
- 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.
2025-11-28 17:25:02 +08:00

359 lines
12 KiB
Vue
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.
<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>