Commit c3a8f1a5 by luoqi

feat(plans): 患者卡片手机号直接显示明文,去掉脱敏与查看图标

详情页患者卡片:手机号从「脱敏 + 眼睛 toggle」改为直接显示 patient.phone 明文;
移除 eye/eye-off 按钮及 reveal 状态。保留真/假角标 + 复制按钮。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parent 2b11ccc5
Pipeline #3349 failed in 0 seconds
...@@ -984,35 +984,11 @@ function IdentityCard({ ...@@ -984,35 +984,11 @@ function IdentityCard({
onOpenProfile: () => void; onOpenProfile: () => void;
}) { }) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const [revealed, setRevealed] = useState(false); // patient.phone 当前 backend 已返回明文(plan-aggregate.service.ts:131)—— 详情页直接显示明文,
const [revealedPhone, setRevealedPhone] = useState<string | null>(null); // 不脱敏、不做 eye toggle;若未来 backend 只发 phoneMasked,复制时降级调 phone-reveal 端点。
// patient.phone 当前 backend 已返回明文(plan-aggregate.service.ts:131),前端优先用;
// 若未来 backend 隐藏明文(只发 phoneMasked),则降级调 /patients/:id/phone-reveal 拉
// TODO(W5+):backend 应只回 phoneMasked,reveal 走专用端点 + audit log
const toggleReveal = async (e: React.MouseEvent) => {
e.stopPropagation();
if (revealed) {
setRevealed(false);
return;
}
// 已有明文 → 直接显示;否则调 API
if (patient.phone && patient.phone !== patient.phoneMasked) {
setRevealedPhone(patient.phone);
setRevealed(true);
return;
}
try {
const { phone } = await plansApi.revealPhone(patient.id);
setRevealedPhone(phone);
setRevealed(true);
} catch (err) {
toast.error(err instanceof Error ? err.message : '获取号码失败');
}
};
const copyPhone = async (e: React.MouseEvent) => { const copyPhone = async (e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
// 复制总是明文;若未 reveal 则先拉(用户意图明确想要 raw) let raw = patient.phone;
let raw = revealedPhone ?? patient.phone;
if (!raw || raw === patient.phoneMasked) { if (!raw || raw === patient.phoneMasked) {
try { try {
const { phone } = await plansApi.revealPhone(patient.id); const { phone } = await plansApi.revealPhone(patient.id);
...@@ -1065,7 +1041,7 @@ function IdentityCard({ ...@@ -1065,7 +1041,7 @@ function IdentityCard({
)} )}
<div className="mt-1 flex items-center gap-1.5"> <div className="mt-1 flex items-center gap-1.5">
<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} {patient.phone ?? patient.phoneMasked}
</span> </span>
{/* 号码真实性角标:真 = 外部对照表已核实;假 = 宿主同步号(测试库造数) */} {/* 号码真实性角标:真 = 外部对照表已核实;假 = 宿主同步号(测试库造数) */}
<span <span
...@@ -1079,30 +1055,7 @@ function IdentityCard({ ...@@ -1079,30 +1055,7 @@ function IdentityCard({
> >
{patient.phoneVerified ? '真' : '假'} {patient.phoneVerified ? '真' : '假'}
</span> </span>
{/* 查看明文 — eye / eye-off toggle(reveal,需 PATIENT_VIEW 权限) */} {/* 复制号码明文 */}
<button
onClick={toggleReveal}
title={revealed ? '隐藏号码' : '查看号码'}
className={cn(
'inline-flex items-center justify-center w-5 h-5 rounded transition-colors',
revealed ? 'text-teal-700 bg-teal-50' : 'text-slate-400 hover:text-teal-700 hover:bg-teal-50',
)}
>
{revealed ? (
// eye-off
<svg viewBox="0 0 24 24" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="1.8">
<path d="M17.94 17.94A10.94 10.94 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A10.94 10.94 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" strokeLinecap="round" strokeLinejoin="round" />
<line x1="1" y1="1" x2="23" y2="23" strokeLinecap="round" />
</svg>
) : (
// eye
<svg viewBox="0 0 24 24" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="1.8">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
<circle cx="12" cy="12" r="3" />
</svg>
)}
</button>
{/* 复制(总是复制明文,未 reveal 时自动拉) */}
<button <button
onClick={copyPhone} onClick={copyPhone}
title="复制号码" title="复制号码"
......
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