Commit cfaca4c4 by luoqi

feat(web): 详情页手机号加 真/假 角标(假号也明确标出)

- plan-aggregate 聚合 patient 透出 phoneVerified;
- 详情页手机号旁角标:真(teal,外部对照表已核实)/ 假(灰,宿主同步造数号),
  tooltip 注明含义;类型/adapter/mock 同步。

验证:真号患者(刘倍磊)phoneVerified=true、假号患者(吴小燕)false,两端 tsc 0。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 5671371c
...@@ -278,6 +278,7 @@ function serializePatient(patient: { ...@@ -278,6 +278,7 @@ function serializePatient(patient: {
gender: string | null; gender: string | null;
birthDate: Date | null; birthDate: Date | null;
phone: string | null; phone: string | null;
phoneVerified: boolean;
active: boolean; active: boolean;
preferences: unknown; preferences: unknown;
profile: { tags: string[] } | null; profile: { tags: string[] } | null;
...@@ -294,6 +295,8 @@ function serializePatient(patient: { ...@@ -294,6 +295,8 @@ function serializePatient(patient: {
age: patient.birthDate ? calcAge(patient.birthDate) : null, age: patient.birthDate ? calcAge(patient.birthDate) : null,
phone: patient.phone, phone: patient.phone,
phoneMasked: maskPhone(patient.phone), phoneMasked: maskPhone(patient.phone),
/// 号码真实性:true = 外部对照表核实的真号;false = 宿主同步号(测试库为造数假号)
phoneVerified: patient.phoneVerified,
active: patient.active, active: patient.active,
/// 专属客服({id,name})— 从 preferences.dedicatedCs 解析;无则 null /// 专属客服({id,name})— 从 preferences.dedicatedCs 解析;无则 null
dedicatedCs: extractDedicatedCs(patient.preferences), dedicatedCs: extractDedicatedCs(patient.preferences),
......
...@@ -47,6 +47,7 @@ export function adaptData(real: PlanDetailData, dict?: TokenDictionary) { ...@@ -47,6 +47,7 @@ export function adaptData(real: PlanDetailData, dict?: TokenDictionary) {
birthDate: real.patient.birthDate ?? '', birthDate: real.patient.birthDate ?? '',
phone: real.patient.phone ?? '', phone: real.patient.phone ?? '',
phoneMasked: real.patient.phoneMasked ?? '', phoneMasked: real.patient.phoneMasked ?? '',
phoneVerified: real.patient.phoneVerified ?? false,
tags: real.patient.tags, // 无运营标签时为空 — 不再硬塞"普通患者"占位 tags: real.patient.tags, // 无运营标签时为空 — 不再硬塞"普通患者"占位
preferences: { preferences: {
contactWindow: '19:00–21:30', // 还没数据源 contactWindow: '19:00–21:30', // 还没数据源
......
...@@ -40,6 +40,7 @@ export const mockPatient = { ...@@ -40,6 +40,7 @@ export const mockPatient = {
birthDate: '1984-03-12', birthDate: '1984-03-12',
phone: '13855612937', phone: '13855612937',
phoneMasked: '138****2937', phoneMasked: '138****2937',
phoneVerified: false,
tags: ['VIP 金卡', '老客户'] as string[], tags: ['VIP 金卡', '老客户'] as string[],
preferences: { preferences: {
contactWindow: '19:00–21:30', contactWindow: '19:00–21:30',
......
...@@ -1013,6 +1013,18 @@ function IdentityCard({ ...@@ -1013,6 +1013,18 @@ function IdentityCard({
<span className="text-[12px] tabular-nums font-mono text-slate-700"> <span className="text-[12px] tabular-nums font-mono text-slate-700">
{revealed && revealedPhone ? revealedPhone : patient.phoneMasked} {revealed && revealedPhone ? revealedPhone : patient.phoneMasked}
</span> </span>
{/* 号码真实性角标:真 = 外部对照表已核实;假 = 宿主同步号(测试库造数) */}
<span
title={patient.phoneVerified ? '真实手机号(外部对照表已核实)' : '未核实号码(测试库为造数假号)'}
className={cn(
'inline-flex items-center rounded px-1 text-[10px] font-medium leading-4 ring-1 ring-inset',
patient.phoneVerified
? 'bg-teal-50 text-teal-700 ring-teal-200'
: 'bg-slate-50 text-slate-400 ring-slate-200',
)}
>
{patient.phoneVerified ? '真' : '假'}
</span>
{/* 查看明文 — eye / eye-off toggle(reveal,需 PATIENT_VIEW 权限) */} {/* 查看明文 — eye / eye-off toggle(reveal,需 PATIENT_VIEW 权限) */}
<button <button
onClick={toggleReveal} onClick={toggleReveal}
......
...@@ -20,6 +20,8 @@ export type PlanDetailData = { ...@@ -20,6 +20,8 @@ export type PlanDetailData = {
age: number | null; age: number | null;
phone: string | null; phone: string | null;
phoneMasked: string | null; phoneMasked: string | null;
/// 号码真实性:true = 已核实真号;false = 宿主同步号(测试库为造数假号)
phoneVerified: boolean;
active: boolean; active: boolean;
tags: string[]; tags: string[];
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment