add customer lunar birthdate

This commit is contained in:
Timi
2026-08-03 18:54:28 +08:00
parent 386d5e0613
commit dd8534eaad
5 changed files with 577 additions and 2 deletions
+52
View File
@@ -5,6 +5,18 @@
:customer="customer"
/>
<t-cell-group v-if="customer" theme="card">
<t-cell title="出生日期">
<button
v-if="isLunarBirthdate"
class="birthdate-trigger"
type="button"
@click.stop="toggleBirthdateText"
>
<span v-text="birthdateText" />
</button>
<span v-else v-text="birthdateText" />
</t-cell>
<t-cell title="年龄" :note="ageText" />
<t-cell title="住址" :note="customer.address || '无'" />
<t-cell title="病症" :note="customer.disease || '无'" />
<t-cell title="备注" :note="customer.remark || '无'" />
@@ -162,6 +174,7 @@ import { useSettingStore } from "@/store/settingStore";
import type { BodyAreaValue, Customer } from "@/types/Customer";
import { getCustomerPhotoURL, normalizeBodyAreaValue } from "@/utils/customer";
import { gaoPermissionCodes } from "@/utils/gaoPermission";
import { LunarCalendar } from "@/utils/LunarCalendar";
defineOptions({
name: "CustomerDetail"
@@ -180,6 +193,7 @@ const scanPopupVisible = ref(false);
const pendingInvitee = ref<Customer>();
const scanStatus = ref("点击扫码开始识别。");
const removing = ref(false);
const showSolarBirthdate = ref(false);
let latestDecodedText = "";
const inviter = computed(() => customer.value?.introducerCustomer);
@@ -189,6 +203,10 @@ const creatorName = computed(() => {
return creatorUser?.nick || creatorUser?.name || customer.value?.creatorUserId || "无";
});
const createdAtText = computed(() => formatTime(customer.value?.createdAt));
const isLunarBirthdate = computed(() => customer.value?.birthdateCalendar === "LUNAR" && !!customer.value?.birthdate);
const solarBirthdateText = computed(() => formatSolarBirthdate(customer.value));
const birthdateText = computed(() => showSolarBirthdate.value && isLunarBirthdate.value ? solarBirthdateText.value : formatBirthdate(customer.value));
const ageText = computed(() => customer.value?.age === undefined || customer.value?.age === null ? "无" : `${customer.value.age} 岁`);
const selectedAreas = computed<BodyAreaValue>({
get() {
return normalizeBodyAreaValue(customer.value?.conditioningPart);
@@ -203,10 +221,12 @@ async function loadCustomer(): Promise<void> {
if (!id) {
customer.value = undefined;
invitees.value = [];
showSolarBirthdate.value = false;
return;
}
try {
showSolarBirthdate.value = false;
customer.value = await CustomerAPI.detail(id);
await loadInvitees();
} catch (err) {
@@ -297,6 +317,27 @@ function formatTime(value?: number): string {
return Time.formatDateTime(date);
}
function formatBirthdate(value?: Customer): string {
if (!value?.birthdate) {
return "无";
}
if (value.birthdateCalendar !== "LUNAR") {
return `公历 ${value.birthdate}`;
}
const lunarDate = LunarCalendar.solarToLunar(value.birthdate);
return lunarDate ? LunarCalendar.formatLunarDate(lunarDate) : "农历日期";
}
function formatSolarBirthdate(value?: Customer): string {
return value?.birthdate ? `公历 ${value.birthdate}` : "无";
}
function toggleBirthdateText(): void {
if (isLunarBirthdate.value) {
showSolarBirthdate.value = !showSolarBirthdate.value;
}
}
function isCustomerAncestor(invitee: Customer): boolean {
if (!customer.value?.id) {
return false;
@@ -519,6 +560,17 @@ watch(() => route.params.id, async () => {
}
}
.birthdate-trigger {
padding: 0;
border: 0;
color: var(--td-text-color-primary);
font: inherit;
background: transparent;
text-align: right;
text-decoration: underline dotted;
text-underline-offset: .2rem;
}
.scan-popup {
gap: 1rem;
padding: 1rem 1rem calc(1rem + env(safe-area-inset-bottom, 0)) 1rem;
+39 -2
View File
@@ -34,11 +34,18 @@
borderless
/>
</t-form-item>
<t-form-item label="出生日期">
<birthdate-picker
v-model="formModel.birthdate"
v-model:calendar="formModel.birthdateCalendar"
/>
</t-form-item>
<t-form-item label="年龄">
<t-input
v-model.trim="formModel.age"
type="number"
placeholder="年龄"
:readonly="!!formModel.birthdate"
:placeholder="formModel.birthdate ? '根据出生日期自动计算' : '年龄'"
borderless
/>
</t-form-item>
@@ -157,7 +164,7 @@ import CodeScanner from "@/components/CodeScanner.vue";
import CustomerSelector from "@/components/CustomerSelector.vue";
import PhotoField from "@/components/PhotoField.vue";
import { useSettingStore } from "@/store/settingStore";
import type { BodyAreaValue, Customer, CustomerGender } from "@/types/Customer";
import type { BodyAreaValue, Customer, CustomerBirthdateCalendar, CustomerGender } from "@/types/Customer";
import { createBodyAreaValue, getCustomerPhotoURL, normalizeBodyAreaValue } from "@/utils/customer";
import { gaoPermissionCodes } from "@/utils/gaoPermission";
import { compressUploadImage } from "@/utils/imageCompression";
@@ -194,6 +201,8 @@ const formModel = reactive({
photoURL: "",
telephone: "",
age: "",
birthdate: "",
birthdateCalendar: "SOLAR" as CustomerBirthdateCalendar,
address: "",
referrer: "",
disease: "",
@@ -209,6 +218,8 @@ function resetFormModel(): void {
photoURL: "",
telephone: "",
age: "",
birthdate: "",
birthdateCalendar: "SOLAR",
address: "",
referrer: "",
disease: "",
@@ -258,6 +269,8 @@ async function fillForm(): Promise<void> {
photoURL: getCustomerPhotoURL(customer),
telephone: customer.telephone || "",
age: customer.age === undefined || customer.age === null ? "" : String(customer.age),
birthdate: customer.birthdate || "",
birthdateCalendar: customer.birthdateCalendar || "SOLAR",
address: customer.address || "",
referrer: customer.introducerCustomer?.name || "",
disease: customer.disease || "",
@@ -406,6 +419,8 @@ async function saveCustomer(): Promise<void> {
name: formModel.name,
gender: formModel.gender || undefined,
age: Number(formModel.age) || undefined,
birthdate: formModel.birthdate || null,
birthdateCalendar: formModel.birthdate ? formModel.birthdateCalendar : undefined,
telephone: formModel.telephone,
address: formModel.address,
remark: formModel.note,
@@ -463,6 +478,28 @@ watch(() => formModel.referrer, (value) => {
}
});
watch(() => formModel.birthdate, (value) => {
if (value) {
formModel.age = String(calculateAge(value));
}
});
function calculateAge(value: string): number {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
if (!match) {
return 0;
}
const birthYear = Number(match[1]);
const birthMonth = Number(match[2]);
const birthDay = Number(match[3]);
const today = new Date();
let age = today.getFullYear() - birthYear;
if (today.getMonth() + 1 < birthMonth || (today.getMonth() + 1 === birthMonth && today.getDate() < birthDay)) {
age--;
}
return Math.max(age, 0);
}
onMounted(async () => {
await fillForm();
});