Commit 9c0fe5ee by luoqi

feat(web/plans): per-view filter options + priority hover integration

FilterBar 增 view prop:
  我的工单 / 召回池 两个 view 的 status 选项应该不同(工单看跟进状态,
  召回池看入池状态)。setView 时清 status 避免无效组合残留。

行级优先级数字接 PriorityHover:鼠标悬浮看 6 因子算分明细,
跟详情页效果一致。
parent 8d708bac
...@@ -33,6 +33,7 @@ import { plansApi } from './plans-api'; ...@@ -33,6 +33,7 @@ import { plansApi } from './plans-api';
import { usePlansList } from './use-plans-list'; import { usePlansList } from './use-plans-list';
import { usePlanCounts } from './use-plan-counts'; import { usePlanCounts } from './use-plan-counts';
import { ReasonLine } from '@/components/plan-detail/reason-line'; import { ReasonLine } from '@/components/plan-detail/reason-line';
import { PriorityHover, type PriorityBreakdown } from '@/components/priority-hover';
// ───────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────
// 召回任务工作台(/plans) // 召回任务工作台(/plans)
...@@ -201,8 +202,10 @@ export function PlansListApp() { ...@@ -201,8 +202,10 @@ export function PlansListApp() {
}; };
// 切换 view / status / q / sort 时回到第一页 + 清空选择(常识) // 切换 view / status / q / sort 时回到第一页 + 清空选择(常识)
// ⭐ 切 view 时清空 status — 不同 view 状态可选项不同(pool 无 filter,mine 不可选 active)
// 不清会出现 mine + status=active 这种空查询的怪状态
const setView = (v: View) => { const setView = (v: View) => {
setQuery({ view: v, page: 1 }); setQuery({ view: v, status: undefined, page: 1 });
clearSelected(); clearSelected();
}; };
const setStatus = (s: ListPlansQuery['status'] | undefined) => { const setStatus = (s: ListPlansQuery['status'] | undefined) => {
...@@ -226,6 +229,7 @@ export function PlansListApp() { ...@@ -226,6 +229,7 @@ export function PlansListApp() {
</div> </div>
<FilterBar <FilterBar
view={view}
q={q} q={q}
setQ={setQ} setQ={setQ}
status={query.status} status={query.status}
...@@ -467,10 +471,12 @@ function ViewTabs({ ...@@ -467,10 +471,12 @@ function ViewTabs({
// ───────────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────────
function FilterBar({ function FilterBar({
view,
q, setQ, status, setStatus, q, setQ, status, setStatus,
sort, setSort, density, setDensity, sort, setSort, density, setDensity,
allSelected, someSelected, onTogglePage, allSelected, someSelected, onTogglePage,
}: { }: {
view: View;
q: string; q: string;
setQ: (v: string) => void; setQ: (v: string) => void;
status: ListPlansQuery['status'] | undefined; status: ListPlansQuery['status'] | undefined;
...@@ -483,6 +489,27 @@ function FilterBar({ ...@@ -483,6 +489,27 @@ function FilterBar({
someSelected: boolean; someSelected: boolean;
onTogglePage: () => void; onTogglePage: () => void;
}) { }) {
// 按 view 决定状态可选 — 跟后端 view filter 口径对齐(plan.service.ts)
// pool: WHERE status=active AND assigneeUserId IS NULL → 全部都是"待认领",status filter 无意义,隐藏
// mine: WHERE assigneeUserId=user → 可能 assigned/completed/abandoned,不会有 active
// all: 无 view 过滤,全状态可见
const statusOptions: Array<{ value: string; label: string }> | null =
view === 'pool'
? null // pool 隐藏 status filter(语义上等价"待认领")
: view === 'mine'
? [
{ value: '__all', label: '全部我的' },
{ value: 'assigned', label: '进行中' },
{ value: 'completed', label: '已完成' },
{ value: 'abandoned', label: '已放弃' },
]
: [
{ value: '__all', label: '全部状态' },
{ value: 'active', label: '待认领' },
{ value: 'assigned', label: '已认领' },
{ value: 'completed', label: '已完成' },
{ value: 'abandoned', label: '已放弃' },
];
return ( return (
<div className="flex flex-none flex-wrap items-center gap-2 border-b border-slate-200 bg-white px-5 py-2.5"> <div className="flex flex-none flex-wrap items-center gap-2 border-b border-slate-200 bg-white px-5 py-2.5">
<div className="relative"> <div className="relative">
...@@ -496,16 +523,16 @@ function FilterBar({ ...@@ -496,16 +523,16 @@ function FilterBar({
className="w-64 pl-8" className="w-64 pl-8"
/> />
</div> </div>
<Select value={status ?? '__all'} onValueChange={(v) => setStatus(v === '__all' ? undefined : (v as ListPlansQuery['status']))}> {statusOptions && (
<SelectTrigger className="h-8 w-32 text-xs"><SelectValue placeholder="状态" /></SelectTrigger> <Select value={status ?? '__all'} onValueChange={(v) => setStatus(v === '__all' ? undefined : (v as ListPlansQuery['status']))}>
<SelectContent> <SelectTrigger className="h-8 w-32 text-xs"><SelectValue placeholder="状态" /></SelectTrigger>
<SelectItem value="__all">全部状态</SelectItem> <SelectContent>
<SelectItem value="active">待认领</SelectItem> {statusOptions.map((o) => (
<SelectItem value="assigned">我的工单</SelectItem> <SelectItem key={o.value} value={o.value}>{o.label}</SelectItem>
<SelectItem value="completed">已完成</SelectItem> ))}
<SelectItem value="abandoned">已放弃</SelectItem> </SelectContent>
</SelectContent> </Select>
</Select> )}
<Select value={sort} onValueChange={(v) => setSort(v as SortKey)}> <Select value={sort} onValueChange={(v) => setSort(v as SortKey)}>
<SelectTrigger className="h-8 w-44 text-xs"><SelectValue placeholder="排序" /></SelectTrigger> <SelectTrigger className="h-8 w-44 text-xs"><SelectValue placeholder="排序" /></SelectTrigger>
<SelectContent> <SelectContent>
...@@ -654,16 +681,18 @@ function PatientPlanCard({ ...@@ -654,16 +681,18 @@ function PatientPlanCard({
)} )}
</div> </div>
</div> </div>
<span <PriorityHover
className="inline-flex items-center gap-1 cursor-help group/score relative" score={p.priorityScore}
title={`优先级 ${p.priorityScore}/100 — 由 6 因子算分(临床基线 × 时间窗 + 价值 + 转化 + 紧迫,× 信号置信)。点开详情查完整拆解。`} breakdown={(p.reasons[0]?.breakdown as { priority?: PriorityBreakdown } | null | undefined)?.priority}
> >
<PriorityBar score={p.priorityScore} /> <span className="inline-flex items-center gap-1 cursor-help group/score">
<svg className="w-3 h-3 text-slate-400 opacity-0 group-hover/score:opacity-100 transition-opacity" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> <PriorityBar score={p.priorityScore} />
<circle cx="12" cy="12" r="10" /> <svg className="w-3 h-3 text-slate-400 opacity-0 group-hover/score:opacity-100 transition-opacity" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M12 16v-4M12 8h.01" strokeLinecap="round" /> <circle cx="12" cy="12" r="10" />
</svg> <path d="M12 16v-4M12 8h.01" strokeLinecap="round" />
</span> </svg>
</span>
</PriorityHover>
</div> </div>
{/* 中:scenario + status */} {/* 中:scenario + status */}
......
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