Commit 6a13bdb4 by luoqi

fix(web): 召回池搜索兼容中文输入法组字(避免拿拼音中间态误报"没有匹配")

现象:搜"李石明"时请求 keyword=李石ming(拼音"明"未上屏),ILIKE 不命中 → 空结果;
搜单字"李"反而能出。根因:input onChange 在 IME 组字中就触发 300ms 防抖搜索,
拿到中间态"李石ming",组字完成(→明)后没用最终中文重搜。
修:composingRef 标记组字态,组字中 effect 跳过搜索;onCompositionEnd 解锁 +
用最终中文重新触发防抖。仅前端。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parent 2359bbf7
Pipeline #3368 failed in 0 seconds
......@@ -15,6 +15,7 @@ import {
type PlanListItem,
} from '@pac/types';
import { cn, formatGender } from '@/lib/utils';
import { actionTemplate } from '@/lib/action-url';
import { useHasPermission } from '@/hooks/use-permission';
import { useAuthStore, visibleClinics } from '@/stores/auth-store';
import { PriorityHover, type PriorityBreakdown } from '@/components/priority-hover';
......@@ -49,11 +50,16 @@ export function PatientPickerRail({
const router = useRouter();
const canViewAll = useHasPermission(Permission.PLAN_VIEW_ALL);
const [view, setView] = useState<View>('mine');
const [view, setView] = useState<View>('pool');
const [search, setSearch] = useState('');
const [keyword, setKeyword] = useState(''); // 防抖后的值(真正上服务端)
const [sort, setSort] = useState<PickerFilters['sort']>('priority_desc');
const composingRef = useRef(false); // 中文输入法组字中(true 时别拿拼音中间态"李石ming"去搜)
// 排序固定优先级高→低(去掉可切换的排序控件后,顺序仍按优先级)
const [sort] = useState<PickerFilters['sort']>('priority_desc');
const [realPhoneOnly, setRealPhoneOnly] = useState(false);
// 配了原始档案(宿主 VIEW_PATIENT)时,手机号在宿主档案页看 → 与身份卡手机号行同条件,
// 隐藏"真手机号"筛选(PAC 侧为造数假号,按真伪筛选无意义)。
const hasPatientArchive = !!actionTemplate('VIEW_PATIENT');
const [clinics, setClinics] = useState<string[]>([]);
const [tags, setTags] = useState<Set<string>>(new Set()); // "key:value"
......@@ -62,6 +68,9 @@ export function PatientPickerRail({
const clinicOptions = useMemo(() => visibleClinics(user), [user]);
useEffect(() => {
// IME 组字中(如拼音 "李石ming" 未上屏)不触发搜索 —— 否则会拿中间态去查 → 误报"没有匹配"。
// 组字完成由 onCompositionEnd 解锁并用最终中文重新触发本 effect。
if (composingRef.current) return;
const t = setTimeout(() => setKeyword(search.trim()), 300);
return () => clearTimeout(t);
}, [search]);
......@@ -151,33 +160,34 @@ export function PatientPickerRail({
type="search"
value={search}
onChange={(e) => setSearch(e.target.value)}
onCompositionStart={() => {
composingRef.current = true;
}}
onCompositionEnd={(e) => {
// 组字完成(拼音→汉字上屏)→ 解锁,并用最终中文触发一次防抖搜索
composingRef.current = false;
setSearch(e.currentTarget.value);
}}
placeholder="姓名 / 手机 / 病历号"
className="h-7 w-full rounded-md border border-slate-100 bg-slate-50 pl-7 pr-2 text-[12px] outline-none placeholder:text-slate-400 focus:border-teal-300 focus:bg-white"
/>
</div>
<div className="flex items-center gap-1.5">
<select
value={sort}
onChange={(e) => setSort(e.target.value as PickerFilters['sort'])}
className="h-6.5 rounded-md border border-slate-100 bg-white px-1.5 py-0.5 text-[11px] text-slate-600"
>
<option value="priority_desc">优先级 高→低</option>
<option value="priority_asc">优先级 低→高</option>
<option value="created_desc">最新生成</option>
</select>
<button
type="button"
onClick={() => setRealPhoneOnly((v) => !v)}
title="只看已核实的真实手机号"
className={cn(
'inline-flex h-6 items-center gap-1 rounded-md border px-1.5 text-[11px] transition-colors',
realPhoneOnly
? 'border-teal-300 bg-teal-50 font-medium text-teal-700'
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
)}
>
<PhoneCall className="h-3 w-3" />
</button>
{!hasPatientArchive && (
<button
type="button"
onClick={() => setRealPhoneOnly((v) => !v)}
title="只看已核实的真实手机号"
className={cn(
'inline-flex h-6 items-center gap-1 rounded-md border px-1.5 text-[11px] transition-colors',
realPhoneOnly
? 'border-teal-300 bg-teal-50 font-medium text-teal-700'
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
)}
>
<PhoneCall className="h-3 w-3" />
</button>
)}
<PersonaTagFilter selected={tags} onToggle={toggleTag} onClear={() => setTags(new Set())} />
</div>
{clinicOptions.length > 1 && (
......
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