From 8ed6118b9af4c404800935c7211f4a2e65f4d374 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Sat, 22 Aug 2026 20:48:01 +0800 Subject: [PATCH] fix(web): persist uncertain birth time as a start/end clock window Users pick a clock range instead of a coarse period plus notes, so rectification and window consult scan that range instead of a leftover afternoon bucket. Co-authored-by: Cursor --- docs/BUG_HISTORY.md | 16 +++ frontend/src/app/api/account/route.ts | 122 +++++++++++++----- frontend/src/app/api/consult/route.ts | 2 +- frontend/src/app/api/onboarding/route.ts | 2 +- frontend/src/app/globals.css | 17 ++- frontend/src/app/page.tsx | 22 +++- frontend/src/components/birth-time-intake.tsx | 68 +++++----- frontend/src/lib/account-profile-patch.ts | 33 ++++- frontend/src/lib/birth-profile-timezone.ts | 2 + frontend/src/lib/birth-time-intake-model.ts | 94 +++++++++++--- .../src/lib/consultation-route-service.ts | 9 +- frontend/src/lib/declared-birth-window.ts | 49 ++++++- frontend/src/lib/onboarding-cache-policy.ts | 4 + frontend/src/lib/onboarding-client.ts | 4 + frontend/src/lib/onboarding-post.ts | 22 ++-- .../src/lib/rectification-agentic/session.ts | 33 ++--- .../rectification-agentic/v9/case-service.ts | 27 +++- ...822010000_declared_birth_window_clocks.sql | 62 +++++++++ frontend/tests/account-api.test.ts | 28 ++++ .../birth-time-consultation-consent.test.ts | 8 +- frontend/tests/birth-time-intake.test.ts | 79 +++++++++++- .../tests/consultation-route-service.test.ts | 24 +++- .../tests/database-local-business.test.ts | 1 + frontend/tests/declared-birth-window.test.ts | 51 ++++++++ .../tests/onboarding-cache-policy.test.ts | 5 + .../tests/onboarding-presentation.test.ts | 2 +- frontend/tests/onboarding-route-fake.ts | 2 + frontend/tests/personal-report-entry.test.ts | 2 + frontend/tests/profile-persistence.test.ts | 28 +++- .../rectification-agentic-session.test.ts | 13 ++ .../rectification-v9-case-service.test.ts | 73 +++++++++++ 31 files changed, 760 insertions(+), 144 deletions(-) create mode 100644 frontend/supabase/migrations/20260822010000_declared_birth_window_clocks.sql diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index bdf890a1..df271f96 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -5339,3 +5339,19 @@ - 复发自:无 - 修复版本:97620634 +## BUG-353 | 不确定时间只扫五档时段,补充描述里的钟点纠正用不上 + +- 状态:resolved +- 首次发现:2026-08-22 +- 最近更新:2026-08-22 +- 影响面:出生时间表单、`profiles.declared_window_start/end`、V9 Case 候选窗、普通咨询 declared window、账户 PATCH +- 用户现象:用户已把不确定时间改成 14:30–15:00,或写在补充描述里,生时纠正和 Agent 仍按下午 12:00–17:59 扫描。 +- 触发条件:`birth_time_source=period_only` 且用户记得的是一段钟点而不是五档粗时段;旧资料把钟点写在 `birth_time_clue`。 +- 根因:`period_only` 只把 `birth_time_period` 映射成五档固定窗。`birth_time_clue` 不进 Case fingerprint、不进 `candidate_range`、也不被解析。Case 窗在创建时冻结,聊天工具不能改。正则解析备注会把“6—8 点”这类歧义话当成扫描分钟。 +- 修复:不确定路径改为开始/结束钟点,不再收集备注。保存 `declared_window_start/end`;自定义钟点优先于五档时段;开始结束相同则拒绝;允许跨午夜。旧 `afternoon` 等行回填对应钟点,用户可以收紧到 14:30–15:00。收紧后 fingerprint 变化,首页开新 Case。不解析 clue,保存时把 clue 写成 null。 +- 验证:`frontend/tests/birth-time-intake.test.ts` 锁定开始结束表单、14:30–15:00 持久化且不留下 afternoon 桶;`frontend/tests/declared-birth-window.test.ts` 锁定自定义钟点覆盖 afternoon;`frontend/tests/rectification-v9-case-service.test.ts` 锁定 Case 开窗 14:30–15:00 且 fingerprint 变化;`frontend/tests/consultation-route-service.test.ts` 锁定咨询窗;`frontend/tests/account-api.test.ts` 锁定 PATCH 接受开始结束、拒绝同一分钟。 +- 防复发:不得把备注正则解析成扫描窗。不得只扫五档时段而丢掉用户选择的开始结束。不得把自定义窗的中点写成 `reported_birth_time`。改窗必须开新 Case,旧会话继续展示冻结窗。 +- 相关记录:BUG-197、BUG-198 +- 复发自:BUG-197(不确定入口恢复后仍用五档时段加备注,备注从未进入扫描窗) +- 修复版本:待提交 + diff --git a/frontend/src/app/api/account/route.ts b/frontend/src/app/api/account/route.ts index bbe86bdc..65408dfa 100644 --- a/frontend/src/app/api/account/route.ts +++ b/frontend/src/app/api/account/route.ts @@ -49,26 +49,43 @@ export async function GET() { const admin = createAdminSupabaseClient(); let { data: profile, error: profileError } = await supabase .from("profiles") - .select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,name,birth_time,rectification_case_id") + .select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,name,birth_time,rectification_case_id") .eq("id", userId) .single(); if (profileError && isMissingProfileColumn(profileError)) { - const fallback = await supabase + const withoutDeclaredWindow = await supabase .from("profiles") - .select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,name,birth_time,rectification_case_id") + .select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,name,birth_time,rectification_case_id") .eq("id", userId) .single(); - profile = fallback.data ? { - ...fallback.data, - birth_place_label: undefined, - birth_place_type: undefined, - birth_place_provider: undefined, - birth_place_provider_id: undefined, - timezone_id: undefined, - timezone_source: undefined, - } : null; - profileError = fallback.error; + if (!withoutDeclaredWindow.error && withoutDeclaredWindow.data) { + profile = { + ...withoutDeclaredWindow.data, + declared_window_start: undefined, + declared_window_end: undefined, + }; + profileError = withoutDeclaredWindow.error; + } else if (withoutDeclaredWindow.error && isMissingProfileColumn(withoutDeclaredWindow.error)) { + const fallback = await supabase + .from("profiles") + .select("credits,active_birth_time,birth_time_status,birth_date,reported_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,name,birth_time,rectification_case_id") + .eq("id", userId) + .single(); + profile = fallback.data ? { + ...fallback.data, + birth_place_label: undefined, + birth_place_type: undefined, + birth_place_provider: undefined, + birth_place_provider_id: undefined, + timezone_id: undefined, + timezone_source: undefined, + } : null; + profileError = fallback.error; + } else { + profile = withoutDeclaredWindow.data; + profileError = withoutDeclaredWindow.error; + } } if (profileError || !profile) { @@ -170,28 +187,45 @@ export async function PATCH(request: Request) { const admin = createAdminSupabaseClient(); let { data: currentProfile, error: currentProfileError } = await admin .from("profiles") - .select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source") + .select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source") .eq("id", userId) .maybeSingle(); if (currentProfileError && isMissingProfileColumn(currentProfileError)) { - const fallback = await admin + const withoutDeclaredWindow = await admin .from("profiles") - .select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code") + .select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source") .eq("id", userId) .maybeSingle(); - currentProfile = fallback.data ? { - ...fallback.data, - latitude: undefined, - longitude: undefined, - timezone_offset: undefined, - birth_place_label: undefined, - birth_place_type: undefined, - birth_place_provider: undefined, - birth_place_provider_id: undefined, - timezone_id: undefined, - timezone_source: undefined, - } : null; - currentProfileError = fallback.error; + if (!withoutDeclaredWindow.error && withoutDeclaredWindow.data) { + currentProfile = { + ...withoutDeclaredWindow.data, + declared_window_start: undefined, + declared_window_end: undefined, + }; + currentProfileError = withoutDeclaredWindow.error; + } else if (withoutDeclaredWindow.error && isMissingProfileColumn(withoutDeclaredWindow.error)) { + const fallback = await admin + .from("profiles") + .select("birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,rectification_case_id,country_code,province_code,city_code,district_code") + .eq("id", userId) + .maybeSingle(); + currentProfile = fallback.data ? { + ...fallback.data, + latitude: undefined, + longitude: undefined, + timezone_offset: undefined, + birth_place_label: undefined, + birth_place_type: undefined, + birth_place_provider: undefined, + birth_place_provider_id: undefined, + timezone_id: undefined, + timezone_source: undefined, + } : null; + currentProfileError = fallback.error; + } else { + currentProfile = withoutDeclaredWindow.data; + currentProfileError = withoutDeclaredWindow.error; + } } if (currentProfileError) { return NextResponse.json({ error: "暂时无法核对现有出生资料" }, { status: 500 }); @@ -210,6 +244,12 @@ export async function PATCH(request: Request) { ...(payload.birth_time_period !== undefined ? { birth_time_period: payload.birth_time_period } : {}), + ...(payload.declared_window_start !== undefined + ? { declared_window_start: payload.declared_window_start } + : {}), + ...(payload.declared_window_end !== undefined + ? { declared_window_end: payload.declared_window_end } + : {}), ...(payload.birth_time_clue !== undefined ? { birth_time_clue: payload.birth_time_clue } : {}), @@ -261,6 +301,30 @@ export async function PATCH(request: Request) { data = fallback.data; error = fallback.error; } + if (error && isMissingProfileColumn(error)) { + const { + declared_window_start: _start, + declared_window_end: _end, + ...withoutDeclaredWindow + } = withCoordinates; + void _start; + void _end; + const fallback = await writeProfile(withoutDeclaredWindow); + data = fallback.data; + error = fallback.error; + } + if (error && isMissingProfileColumn(error)) { + const { + declared_window_start: _start, + declared_window_end: _end, + ...withoutCoordinatesOrWindow + } = withoutCoordinates; + void _start; + void _end; + const fallback = await writeProfile(withoutCoordinatesOrWindow); + data = fallback.data; + error = fallback.error; + } if (!error && !data && invalidatesUnconfirmedApplication) { return NextResponse.json({ diff --git a/frontend/src/app/api/consult/route.ts b/frontend/src/app/api/consult/route.ts index 8a043994..ed563592 100644 --- a/frontend/src/app/api/consult/route.ts +++ b/frontend/src/app/api/consult/route.ts @@ -341,7 +341,7 @@ export async function POST(request: Request) { async loadProfile(profileUserId) { const { data, error } = await supabase .from("profiles") - .select("name,birth_date,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source") + .select("name,birth_date,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source") .eq("id", profileUserId) .single(); if (error || !data) throw new ConsultationProfileTruthError("profile_unavailable"); diff --git a/frontend/src/app/api/onboarding/route.ts b/frontend/src/app/api/onboarding/route.ts index e6291810..ccab3ae5 100644 --- a/frontend/src/app/api/onboarding/route.ts +++ b/frontend/src/app/api/onboarding/route.ts @@ -20,7 +20,7 @@ function createProfileRepository( async loadProfile(userId) { return admin .from("profiles") - .select("id,name,birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,onboarding_payload,onboarding_version,onboarding_generated_at") + .select("id,name,birth_date,birth_time,reported_birth_time,active_birth_time,birth_time_source,birth_time_period,declared_window_start,declared_window_end,birth_time_clue,uncertainty_before_minutes,uncertainty_after_minutes,birth_time_status,country_code,province_code,city_code,district_code,latitude,longitude,timezone_offset,birth_place_label,birth_place_type,birth_place_provider,birth_place_provider_id,timezone_id,timezone_source,onboarding_payload,onboarding_version,onboarding_generated_at") .eq("id", userId) .maybeSingle(); }, diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 900c86c5..ca82a8f6 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -2050,8 +2050,17 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .birth-time-source-option b { font-size: var(--type-body-sm); line-height: 1.35; } .birth-time-source-option small { font-size: var(--type-caption); line-height: 1.5; } .birth-time-detail-grid { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas-soft); } -.birth-time-period-details { align-items: start; } -.birth-time-period-details > label { align-self: start; } +.birth-time-window-details { align-items: start; } +.birth-time-window-details > label { align-self: start; } +.birth-time-window-intro { + grid-column: 1 / -1; + margin: 0; + color: var(--color-ink-secondary); + font-size: var(--type-caption); + line-height: 1.5; +} +.birth-time-window-details .birth-time-skip-button { grid-column: 1 / -1; justify-self: start; min-height: 40px; padding-inline: 13px; border-color: transparent; background: transparent; color: var(--color-action); font-size: var(--type-caption); } +.birth-time-window-details .birth-time-skip-button:hover { background: color-mix(in srgb, var(--color-action-soft) 72%, transparent); } .birth-time-detail-grid > label { gap: 7px; } .birth-time-detail-grid > label > span { color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 700; } .birth-time-detail-grid textarea { @@ -2081,8 +2090,6 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .birth-time-detail-grid textarea::-webkit-resizer { background: color-mix(in srgb, var(--color-border-strong) 70%, transparent); } -.birth-time-period-details .birth-time-skip-button { grid-column: 1 / -1; justify-self: start; min-height: 40px; padding-inline: 13px; border-color: transparent; background: transparent; color: var(--color-action); font-size: var(--type-caption); } -.birth-time-period-details .birth-time-skip-button:hover { background: color-mix(in srgb, var(--color-action-soft) 72%, transparent); } .birth-time-detail-note[role="status"] { display: grid; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-canvas-soft); } .birth-time-detail-note[role="status"] p { margin: 0; } .onboarding-card-actions { align-items: center; justify-content: space-between; gap: var(--space-4); padding-top: var(--space-2); } @@ -2091,7 +2098,7 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class @media (max-width: 620px) { .onboarding-card-heading { align-items: flex-start; flex-direction: column; gap: var(--space-2); } .birth-time-source-list, .birth-time-detail-grid { grid-template-columns: 1fr; } - .birth-time-period-details .birth-time-skip-button { grid-column: auto; justify-self: stretch; } + .birth-time-window-details .birth-time-skip-button { grid-column: auto; justify-self: stretch; } .onboarding-card-actions { align-items: stretch; flex-direction: column; } .onboarding-card-actions .button-primary { width: 100%; } } diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 32cd6de1..110780d9 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -61,6 +61,7 @@ import { birthTimePersistenceValues, declaredBirthInputChanged, describeBirthTimeDraft, + hydrateDeclaredWindowDraft, isDeclaredBirthProfileComplete, isBirthTimeDraftReady, birthTimeDraftReadyHint, @@ -358,6 +359,8 @@ const emptyProfile: Profile = { reportedTime: "", birthTimeSource: "", birthTimePeriod: "", + declaredWindowStart: "", + declaredWindowEnd: "", birthTimeClue: "", uncertaintyBeforeMinutes: null, uncertaintyAfterMinutes: null, @@ -659,6 +662,8 @@ function readProfile(value: unknown): Profile { active_birth_time?: unknown; birth_time_source?: unknown; birth_time_period?: unknown; + declared_window_start?: unknown; + declared_window_end?: unknown; birth_time_clue?: unknown; uncertainty_before_minutes?: unknown; uncertainty_after_minutes?: unknown; @@ -696,6 +701,13 @@ function readProfile(value: unknown): Profile { const reportedTime = persistedReportedTime || (source === "legacy_import" ? time : ""); const knownPeriods = ["early_morning", "morning", "afternoon", "evening", "late_night"] as const; const period = knownPeriods.find((item) => item === profile.birth_time_period) ?? ""; + const windowStart = typeof profile.declared_window_start === "string" ? profile.declared_window_start.slice(0, 5) : ""; + const windowEnd = typeof profile.declared_window_end === "string" ? profile.declared_window_end.slice(0, 5) : ""; + const declaredWindow = hydrateDeclaredWindowDraft({ + period, + start: windowStart, + end: windowEnd, + }); const knownStatuses = ["reported", "assessing", "rectifying", "candidate", "accepted", "confirmed"] as const; const status = knownStatuses.find((item) => item === profile.birth_time_status) ?? (time ? "confirmed" : ""); @@ -723,8 +735,10 @@ function readProfile(value: unknown): Profile { time: typeof time === "string" ? time : "", reportedTime: typeof reportedTime === "string" ? reportedTime : "", birthTimeSource: source, - birthTimePeriod: period, - birthTimeClue: typeof profile.birth_time_clue === "string" ? profile.birth_time_clue.slice(0, 240) : "", + birthTimePeriod: declaredWindow.birthTimePeriod, + declaredWindowStart: declaredWindow.declaredWindowStart, + declaredWindowEnd: declaredWindow.declaredWindowEnd, + birthTimeClue: "", uncertaintyBeforeMinutes: typeof profile.uncertainty_before_minutes === "number" ? profile.uncertainty_before_minutes : null, uncertaintyAfterMinutes: typeof profile.uncertainty_after_minutes === "number" ? profile.uncertainty_after_minutes : null, birthTimeStatus: status, @@ -1475,7 +1489,9 @@ export default function Home() { time: isCompletedCandidatePreview ? "04:53" : isRectificationPreview || isAssessmentLoadingPreview ? "" : "12:30", reportedTime: isRectificationPreview ? "14:30" : isAssessmentLoadingPreview || isCompletedCandidatePreview ? "" : "12:30", birthTimeSource: isRectificationPreview ? "approximate" : isAssessmentLoadingPreview || isCompletedCandidatePreview ? "period_only" : "legacy_import", - birthTimePeriod: isAssessmentLoadingPreview || isCompletedCandidatePreview ? "early_morning" : "", + ...hydrateDeclaredWindowDraft({ + period: isAssessmentLoadingPreview || isCompletedCandidatePreview ? "early_morning" : "", + }), birthTimeClue: "", uncertaintyBeforeMinutes: isRectificationPreview ? 30 : null, uncertaintyAfterMinutes: isRectificationPreview ? 30 : null, diff --git a/frontend/src/components/birth-time-intake.tsx b/frontend/src/components/birth-time-intake.tsx index fb015225..bd67915c 100644 --- a/frontend/src/components/birth-time-intake.tsx +++ b/frontend/src/components/birth-time-intake.tsx @@ -6,7 +6,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { birthTimeConsultationOptionsCopy } from "@/lib/birth-time-consultation-consent"; import { birthTimeDisplayState, - birthTimePeriodOptions, birthTimeSourceDefaults, birthTimeSourceOptions, type BirthTimeDraft, @@ -30,9 +29,18 @@ function splitBirthTime(value: string) { type BirthClockSelectProps = { readonly value: string; readonly onChange: (value: string) => void; + readonly ariaLabel?: string; + readonly hourLabel?: string; + readonly minuteLabel?: string; }; -function BirthClockSelect({ value, onChange }: BirthClockSelectProps) { +function BirthClockSelect({ + value, + onChange, + ariaLabel = "选择出生时间", + hourLabel = "出生时间小时", + minuteLabel = "出生时间分钟", +}: BirthClockSelectProps) { const currentTime = value || ""; const [timeParts, setTimeParts] = useState(() => splitBirthTime(currentTime)); @@ -44,9 +52,9 @@ function BirthClockSelect({ value, onChange }: BirthClockSelectProps) { }; return ( -
+
{ - if (typeof nextValue === "string") onPatch({ birthTimePeriod: nextValue }); - }} - > - - - {(selectedValue) => birthTimePeriodOptions.find((option) => option.value === selectedValue)?.label ?? "请选择大致时段"} - - - - {birthTimePeriodOptions.map((option) => ( - {option.label} - ))} - - + 开始 + onPatch({ declaredWindowStart })} + />