Commit a50ad3ff by luoqi

feat(执行页): 撤掉「召回池」tab 与「分配」按钮 —— 分配已经搬去主管工作台

产品:主管进客服执行页时,召回池和分配隐藏。

T16 原版把池子和分配挂在这一页,是因为那时主管和客服共用它。
08-04 评审推翻了后半句、分配搬进独立的主管工作台之后,
这一页就该回到它唯一的职责:**把手上的任务打完**。留着就是同一个动作两个入口。

️ 藏 tab 不等于做到了 —— view 还有两条会自己滑到 pool 的路,一起堵掉:
  ① 左栏「我的为空 → 自动回落召回池」的 effect
  ② /plans 落地页「② 召回池第一个(仅主管)」的兜底
     —— 它是最后一条还能把人滑进池子的路:主管的「我的」一空就被送进
     池子里某个患者的详情页,而左栏根本没有那个 tab,他会以为这条单是分给自己的。

顺带清掉随之而来的死代码:矩阵浮层(openMatrix / pickCell / matrix 三个 state)、
canDispatch 判据、以及 PoolMatrix / Button / useAssistantStore 等 6 个 import。
️ view='pool'/'all' 的**取数逻辑保留**,将来要放回 tab 时不用重接。

空态文案跟着改:原来主管那句写着「我的任务与召回池都是空的,可以在左侧调整筛选(视图/…)」
—— 那两样东西现在都没有了,照说就是让人去找一个不存在的地方。
改成「这一页只显示分给你自己的任务。要给团队分新的一批,去主管工作台」+ 一个入口。

实测:主管 / 客服进执行页,左栏都只剩「我的」,无分配按钮;
主管空态给出「主管工作台 」,且不再被自动滑进召回池。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 586828ca
Pipeline #3566 failed in 0 seconds
'use client'; 'use client';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { Inbox, RefreshCw } from 'lucide-react'; import { ArrowUpRight, Inbox, RefreshCw } from 'lucide-react';
import { Permission } from '@pac/types'; import { Permission } from '@pac/types';
import { Can } from '@/components/can'; import { Can } from '@/components/can';
import { useHasPermission } from '@/hooks/use-permission'; import { useHasPermission } from '@/hooks/use-permission';
...@@ -16,8 +17,9 @@ import { isEmbedded } from '@/lib/embed'; ...@@ -16,8 +17,9 @@ import { isEmbedded } from '@/lib/embed';
* *
* 进来直接落到详情工作台,选人规则(纯规则): * 进来直接落到详情工作台,选人规则(纯规则):
* ① 我的「进行中」第一个(优先续上手头工作) * ① 我的「进行中」第一个(优先续上手头工作)
* ② 没有 → 召回池优先级最高的一个(开新工作) * ② 没有 → 空工作台
* ③ 都没有 → 空工作台:左栏照常(可自行调筛选找人)+ 右侧整体空态 * ⛔ 没有"② 召回池第一个"那一档了(2026-08-10):池子与分配整个搬去了主管工作台,
* 这一页只回答「分给我的还有哪些」。
* 所有旧入口(/ 重定向、各处"返回召回池"链接)自动适配 —— 都会被本页弹回详情。 * 所有旧入口(/ 重定向、各处"返回召回池"链接)自动适配 —— 都会被本页弹回详情。
*/ */
export default function PlansEntryPage() { export default function PlansEntryPage() {
...@@ -30,7 +32,7 @@ export default function PlansEntryPage() { ...@@ -30,7 +32,7 @@ export default function PlansEntryPage() {
function EntryResolver() { function EntryResolver() {
const router = useRouter(); const router = useRouter();
/// 主管判据(T16):只有他有召回池,客服的落地兜底不能滑进池子 /// 主管判据:决定**落地默认**(工作台 vs 执行页)与空态文案,⛔ 不再决定"能不能看池子"
const canDispatch = useHasPermission(Permission.PLAN_DISPATCH); const canDispatch = useHasPermission(Permission.PLAN_DISPATCH);
const [phase, setPhase] = useState<'resolving' | 'empty'>('resolving'); const [phase, setPhase] = useState<'resolving' | 'empty'>('resolving');
...@@ -76,18 +78,15 @@ function EntryResolver() { ...@@ -76,18 +78,15 @@ function EntryResolver() {
router.replace(`/plans/${mine.items[0].id}`); router.replace(`/plans/${mine.items[0].id}`);
return; return;
} }
// ② 召回池第一个 —— ⭐ **仅主管**。 /**
// 客服没有召回池入口(T16),这里若不门控就是一条隐式的认领通道: * 🔴 这里**曾经有一个「② 召回池第一个(仅主管)」的兜底**,2026-08-10 撤掉。
// 他的「我的」一空,系统就自动把他送进池子里某个患者的详情页, *
// 而那个人根本不是分给他的。tab 藏了没用,路径也得堵。 * 执行页已经没有召回池了(产品定:池子与分配整个搬去主管工作台),
if (canDispatch) { * 留着这条兜底就是**唯一一条还能把人滑进池子的路** ——
const pool = await plansApi.list({ view: 'pool', sort: 'priority_desc', page: 1, pageSize: 1 }); * 主管的「我的」一空,系统就自动把他送进池子里某个患者的详情页,
if (cancelled) return; * 而左栏根本没有那个 tab,他会以为这条单是分给自己的。
if (pool.items[0]) { * ⛔ 别加回来:要看池子去工作台的「分一批新的」。
router.replace(`/plans/${pool.items[0].id}`); */
return;
}
}
setPhase('empty'); setPhase('empty');
} catch { } catch {
if (!cancelled) setPhase('empty'); if (!cancelled) setPhase('empty');
...@@ -129,15 +128,29 @@ function EntryResolver() { ...@@ -129,15 +128,29 @@ function EntryResolver() {
<Inbox className="h-7 w-7" /> <Inbox className="h-7 w-7" />
</span> </span>
<div className="text-[15px] font-medium text-slate-700"> <div className="text-[15px] font-medium text-slate-700">
{canDispatch ? '当前没有待跟进的召回任务' : '暂无分配给你的任务'} {canDispatch ? '你名下没有待跟进的任务' : '暂无分配给你的任务'}
</div> </div>
{/* ⭐ 客服看到的空态不能提"召回池"——他没有那个入口,提了只会让他去找一个不存在的地方。 {/*
主管的空态照旧(他确实两边都能看)。 */} ⭐ 空态**不许再提「召回池」和「左侧切视图」**(2026-08-10):
执行页已经没有那两样东西了 —— 提了就是让人去找一个不存在的地方。
⚠️ 主管与客服的差别只剩**下一步去哪** :主管去工作台自己分一批,
客服等派发。⛔ 别再按角色说两套"能看到什么"。
*/}
<p className="max-w-sm text-[12.5px] leading-relaxed text-slate-500"> <p className="max-w-sm text-[12.5px] leading-relaxed text-slate-500">
{canDispatch {canDispatch
? '我的任务与召回池都是空的。可以在左侧调整筛选(视图 / 诊所 / 筛选标签)自行查找,或等召回引擎下次重算后刷新。' ? '这一页只显示分给你自己的任务。要给团队分新的一批,去主管工作台。'
: '手头的任务都处理完了。新任务由主管统一派发,稍后刷新看看。'} : '手头的任务都处理完了。新任务由主管统一派发,稍后刷新看看。'}
</p> </p>
{/* 主管的空态给一条明确的去路 —— ⛔ 别只说"去工作台"却不给入口 */}
{canDispatch && (
<Link
href="/supervisor"
className="inline-flex items-center gap-1 text-[12.5px] text-brand-700 hover:underline"
>
主管工作台
<ArrowUpRight className="h-3.5 w-3.5" />
</Link>
)}
<button <button
type="button" type="button"
onClick={() => window.location.reload()} onClick={() => window.location.reload()}
......
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