Commit c1267e65 by luoqi

fix(web): 认领/返池后原地移除该行(不再留在错 tab)

召回池认领→行离开池;我的返池→行离开我的。新增 usePatientPicker.removeItem
(过滤 + total-1),claim/recycle 按当前 view 决定 removeItem 还是 patchItem。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
parent a8eb0ff1
...@@ -98,7 +98,7 @@ export function PatientPickerRail({ ...@@ -98,7 +98,7 @@ export function PatientPickerRail({
}), }),
[view, keyword, sort, realPhoneOnly, clinics, tags], [view, keyword, sort, realPhoneOnly, clinics, tags],
); );
const { items, total, loading, hasMore, error, loadMore, patchItem } = usePatientPicker(filters); const { items, total, loading, hasMore, error, loadMore, patchItem, removeItem } = usePatientPicker(filters);
// 默认「我的」加载完成后若为空 → 自动回落「召回池」(只一次)。 // 默认「我的」加载完成后若为空 → 自动回落「召回池」(只一次)。
// 与落地页 mine 优先、空则 pool 的默认患者策略一致,避免左栏 tab 与所选患者来源不符。 // 与落地页 mine 优先、空则 pool 的默认患者策略一致,避免左栏 tab 与所选患者来源不符。
...@@ -127,25 +127,30 @@ export function PatientPickerRail({ ...@@ -127,25 +127,30 @@ export function PatientPickerRail({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [sync.seq]); }, [sync.seq]);
// 认领:assign 给自己 → 原地把行改成"进行中"(不重拉,保滚动/筛选) // 认领:assign 给自己。认领后该 plan 归「我的」、离开「召回池」:
// 在召回池 tab → 原地移除该行(它已不属于本 tab);其它 tab(all)→ 仅改状态。
// 不重拉,保滚动/筛选。
const claim = async (planId: string, name: string) => { const claim = async (planId: string, name: string) => {
const sub = user?.sub; const sub = user?.sub;
if (!sub) return; if (!sub) return;
try { try {
await plansApi.assign(planId, sub); await plansApi.assign(planId, sub);
patchItem(planId, { status: 'assigned', assigneeUserId: sub }); if (view === 'pool') removeItem(planId);
else patchItem(planId, { status: 'assigned', assigneeUserId: sub });
toast.success(`已认领 ${name}`); toast.success(`已认领 ${name}`);
} catch (err) { } catch (err) {
toast.error(err instanceof Error ? err.message : '认领失败'); toast.error(err instanceof Error ? err.message : '认领失败');
} }
}; };
// 返池:回收已认领的 plan → 原地把行改回"待认领"、清 assignee(不重拉,保滚动/筛选)。 // 返池:回收已认领的 plan → 回到召回池可被再次认领(plan 仍 active)。需 PLAN_RECYCLE 权限。
// plan 仍 active,回到召回池可被再次认领。需 PLAN_RECYCLE 权限。 // 返池后该 plan 离开「我的」:在我的 tab → 原地移除该行;其它 tab → 仅改状态。
// 不重拉,保滚动/筛选。
const recycle = async (planId: string, name: string) => { const recycle = async (planId: string, name: string) => {
try { try {
await plansApi.recycle(planId); await plansApi.recycle(planId);
patchItem(planId, { status: 'active', assigneeUserId: null }); if (view === 'mine') removeItem(planId);
else patchItem(planId, { status: 'active', assigneeUserId: null });
toast.success(`已返池 ${name}`); toast.success(`已返池 ${name}`);
} catch (err) { } catch (err) {
toast.error(err instanceof Error ? err.message : '返池失败'); toast.error(err instanceof Error ? err.message : '返池失败');
......
...@@ -28,6 +28,8 @@ export interface UsePatientPicker { ...@@ -28,6 +28,8 @@ export interface UsePatientPicker {
refresh: () => void; refresh: () => void;
/** 原地更新一条(如认领后改状态),不重拉、不丢滚动位置 */ /** 原地更新一条(如认领后改状态),不重拉、不丢滚动位置 */
patchItem: (id: string, patch: Partial<PlanListItem>) => void; patchItem: (id: string, patch: Partial<PlanListItem>) => void;
/** 原地移除一条(如认领/返池后该行已不属于当前 tab),同步减 total,不重拉 */
removeItem: (id: string) => void;
} }
/** /**
...@@ -101,5 +103,13 @@ export function usePatientPicker(filters: PickerFilters): UsePatientPicker { ...@@ -101,5 +103,13 @@ export function usePatientPicker(filters: PickerFilters): UsePatientPicker {
setItems((prev) => prev.map((x) => (x.id === id ? { ...x, ...patch } : x))); setItems((prev) => prev.map((x) => (x.id === id ? { ...x, ...patch } : x)));
}, []); }, []);
return { items, total, loading, hasMore, error, loadMore, refresh, patchItem }; const removeItem = useCallback((id: string) => {
setItems((prev) => {
const next = prev.filter((x) => x.id !== id);
if (next.length !== prev.length) setTotal((t) => Math.max(0, t - 1));
return next;
});
}, []);
return { items, total, loading, hasMore, error, loadMore, refresh, patchItem, removeItem };
} }
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