add customer lunar birthdate
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
<template>
|
||||
<div class="birthdate-picker">
|
||||
<t-radio-group class="mode" v-model="calendarProxy" variant="default-filled">
|
||||
<t-radio :block="false" borderless value="SOLAR">公历</t-radio>
|
||||
<t-radio :block="false" borderless value="LUNAR">农历</t-radio>
|
||||
<t-button
|
||||
v-if="modelValue"
|
||||
class="clear"
|
||||
size="extra-small"
|
||||
@click="clearValue"
|
||||
>
|
||||
清除
|
||||
</t-button>
|
||||
</t-radio-group>
|
||||
<div class="value" @click="openPicker">
|
||||
<span :class="{ placeholder: !displayValue }" v-text="displayValue || placeholder" />
|
||||
<t-icon name="chevron-right" size="1.25rem" />
|
||||
</div>
|
||||
<t-popup v-model="visible" placement="bottom">
|
||||
<t-date-time-picker
|
||||
v-if="calendarProxy === 'SOLAR'"
|
||||
:title="'选择公历出生日期'"
|
||||
mode="date"
|
||||
:value="solarDraft"
|
||||
:start="startDate"
|
||||
:end="today"
|
||||
format="YYYY-MM-DD"
|
||||
cancel-btn="取消"
|
||||
confirm-btn="确定"
|
||||
@cancel="visible = false"
|
||||
@confirm="handleSolarConfirm"
|
||||
/>
|
||||
<t-picker
|
||||
v-else
|
||||
title="选择农历出生日期"
|
||||
:columns="lunarColumns"
|
||||
:value="lunarPickerValue"
|
||||
cancel-btn="取消"
|
||||
confirm-btn="确定"
|
||||
@pick="handleLunarPick"
|
||||
@cancel="visible = false"
|
||||
@confirm="handleLunarConfirm"
|
||||
/>
|
||||
</t-popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { LunarCalendar, type LunarDate } from "@/utils/LunarCalendar";
|
||||
|
||||
defineOptions({
|
||||
name: "BirthdatePicker"
|
||||
});
|
||||
|
||||
type BirthdateCalendar = "SOLAR" | "LUNAR";
|
||||
type PickerValue = string | number;
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: string;
|
||||
calendar?: BirthdateCalendar;
|
||||
placeholder?: string;
|
||||
}>(), {
|
||||
modelValue: "",
|
||||
calendar: "SOLAR",
|
||||
placeholder: "请选择出生日期"
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "update:modelValue", value: string): void;
|
||||
(e: "update:calendar", value: BirthdateCalendar): void;
|
||||
}>();
|
||||
|
||||
const startDate = `${LunarCalendar.startYear}-01-31`;
|
||||
const today = LunarCalendar.today();
|
||||
const visible = ref(false);
|
||||
const solarDraft = ref(today);
|
||||
const lunarDraft = ref<LunarDate>(LunarCalendar.solarToLunar(today) || {
|
||||
year: LunarCalendar.startYear,
|
||||
month: 1,
|
||||
day: 1,
|
||||
isLeap: false
|
||||
});
|
||||
|
||||
const calendarProxy = computed<BirthdateCalendar>({
|
||||
get: () => props.calendar,
|
||||
set: value => emit("update:calendar", value)
|
||||
});
|
||||
const displayValue = computed(() => {
|
||||
if (!props.modelValue) {
|
||||
return "";
|
||||
}
|
||||
if (calendarProxy.value === "SOLAR") {
|
||||
return props.modelValue;
|
||||
}
|
||||
const lunarDate = LunarCalendar.solarToLunar(props.modelValue);
|
||||
return lunarDate ? LunarCalendar.formatLunarDate(lunarDate) : props.modelValue;
|
||||
});
|
||||
const lunarPickerValue = computed(() => [
|
||||
`${lunarDraft.value.year}`,
|
||||
`${lunarDraft.value.month}${lunarDraft.value.isLeap ? "-leap" : ""}`,
|
||||
`${lunarDraft.value.day}`
|
||||
]);
|
||||
const lunarColumns = computed(() => {
|
||||
const yearOptions = LunarCalendar.getYearOptions(today);
|
||||
const monthOptions = LunarCalendar.getMonthOptions(lunarDraft.value.year)
|
||||
.filter(option => isLunarDateSelectable({
|
||||
year: lunarDraft.value.year,
|
||||
month: option.month,
|
||||
day: 1,
|
||||
isLeap: option.isLeap
|
||||
}));
|
||||
const selectedMonth = monthOptions.some(option => option.value === lunarPickerValue.value[1])
|
||||
? lunarPickerValue.value[1]
|
||||
: monthOptions[0]?.value;
|
||||
const selectedOption = monthOptions.find(option => option.value === selectedMonth) || monthOptions[0];
|
||||
const dayCount = selectedOption ? LunarCalendar.getMonthDays({
|
||||
year: lunarDraft.value.year,
|
||||
month: selectedOption.month,
|
||||
isLeap: selectedOption.isLeap
|
||||
}) : 29;
|
||||
const dayOptions = Array.from({ length: dayCount }, (_, index) => {
|
||||
const day = index + 1;
|
||||
return {
|
||||
value: `${day}`,
|
||||
label: LunarCalendar.formatLunarDate({
|
||||
year: lunarDraft.value.year,
|
||||
month: selectedOption?.month || 1,
|
||||
day,
|
||||
isLeap: selectedOption?.isLeap || false
|
||||
})
|
||||
.split(" ")
|
||||
.slice(-1)[0] || `${day}`
|
||||
};
|
||||
}).filter(option => isLunarDateSelectable({
|
||||
year: lunarDraft.value.year,
|
||||
month: selectedOption?.month || 1,
|
||||
day: Number(option.value),
|
||||
isLeap: selectedOption?.isLeap || false
|
||||
}));
|
||||
return [
|
||||
yearOptions,
|
||||
monthOptions,
|
||||
dayOptions
|
||||
];
|
||||
});
|
||||
|
||||
function openPicker(): void {
|
||||
if (calendarProxy.value === "SOLAR") {
|
||||
solarDraft.value = props.modelValue || today;
|
||||
} else {
|
||||
const lunarDate = props.modelValue ? LunarCalendar.solarToLunar(props.modelValue) : LunarCalendar.solarToLunar(today);
|
||||
if (lunarDate) {
|
||||
lunarDraft.value = lunarDate;
|
||||
}
|
||||
}
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
function handleSolarConfirm(value: PickerValue): void {
|
||||
const date = String(value);
|
||||
if (!date) {
|
||||
return;
|
||||
}
|
||||
emit("update:modelValue", date);
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
function handleLunarPick(value: PickerValue[]): void {
|
||||
applyLunarPickerValue(value);
|
||||
}
|
||||
|
||||
function handleLunarConfirm(value: PickerValue[]): void {
|
||||
applyLunarPickerValue(value);
|
||||
const solarDate = LunarCalendar.lunarToSolar(lunarDraft.value);
|
||||
if (!solarDate || solarDate > today) {
|
||||
return;
|
||||
}
|
||||
emit("update:modelValue", solarDate);
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
function applyLunarPickerValue(value: PickerValue[]): void {
|
||||
const year = Number(value[0]);
|
||||
const monthValue = String(value[1] || "1");
|
||||
const monthOptions = LunarCalendar.getMonthOptions(year)
|
||||
.filter(option => isLunarDateSelectable({
|
||||
year,
|
||||
month: option.month,
|
||||
day: 1,
|
||||
isLeap: option.isLeap
|
||||
}));
|
||||
const selectedMonth = monthOptions.find(option => option.value === monthValue) || monthOptions[0];
|
||||
const day = Number(value[2]);
|
||||
if (!year || !selectedMonth || !day) {
|
||||
return;
|
||||
}
|
||||
const monthDays = LunarCalendar.getMonthDays({
|
||||
year,
|
||||
month: selectedMonth.month,
|
||||
isLeap: selectedMonth.isLeap
|
||||
});
|
||||
lunarDraft.value = {
|
||||
year,
|
||||
month: selectedMonth.month,
|
||||
day: Math.max(1, Math.min(day, monthDays)),
|
||||
isLeap: selectedMonth.isLeap
|
||||
};
|
||||
}
|
||||
|
||||
function isLunarDateSelectable(lunarDate: LunarDate): boolean {
|
||||
const solarDate = LunarCalendar.lunarToSolar(lunarDate);
|
||||
return !!solarDate && solarDate <= today;
|
||||
}
|
||||
|
||||
function clearValue(): void {
|
||||
emit("update:modelValue", "");
|
||||
visible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.birthdate-picker {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.mode {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.value {
|
||||
gap: .25rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
span {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
.clear {
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
.birthdate-picker {
|
||||
|
||||
.placeholder {
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user