Commit 9d5e7862 by luoqi

feat(执行页): 补上回主管工作台的入口 —— 之前只有去、没有回

工作台头部那条「客服执行页 」旁边的注释早就写了
「两个页面是双向的, 别做成有去无回」,但实际只做了去的那一半:
主管点进执行页之后,除了改地址栏没有回去的路(产品发现)。

️ 判据用 PLAN_DISPATCH,与 /supervisor 那条路由**同一把闸** ——
    不用 role 判断:权限按 role 现算,而 token 里的 role 是签发时固化的,
   两处判据不同源就会出现"看得见入口、点进去被弹回来"。
️ 嵌入宿主时隐藏,与 IdentityCluster 同一条规矩(宿主自有导航)。

实测:主管进执行页有入口、点了落 /supervisor;客服进任务详情不出现。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 59378564
Pipeline #3560 failed in 0 seconds
......@@ -4,7 +4,8 @@ import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { useRouter } from 'next/navigation';
import { createPortal } from 'react-dom';
import { toast } from 'sonner';
import { RefreshCw, ChevronDown, ThumbsUp, ThumbsDown, Sparkles, CalendarPlus, CalendarClock, XCircle } from 'lucide-react';
import Link from 'next/link';
import { RefreshCw, ChevronDown, ThumbsUp, ThumbsDown, Sparkles, CalendarPlus, CalendarClock, XCircle, ArrowUpRight } from 'lucide-react';
import {
Dialog,
DialogContent,
......@@ -25,6 +26,7 @@ import { useAuthStore } from '@/stores/auth-store';
import { emitPetEvent } from '@/lib/pet-events';
import { usePlanSyncStore } from '@/stores/plan-sync-store';
import { IdentityCluster } from '@/components/identity-cluster';
import { useHasPermission } from '@/hooks/use-permission';
import { isEmbedded } from '@/lib/embed';
import { hostActionMode, openHostAction } from '@/lib/host-message';
import { CloseOpportunityDialog } from './close-opportunity-dialog';
......@@ -43,6 +45,7 @@ import {
diagnosisCodeNameZh,
EXECUTION_OUTCOME_META,
ExecutionChannel,
Permission,
RECALL_FEEDBACK_OPTIONS,
ABANDON_REASON_META,
personaFeatureSortKey,
......@@ -1419,6 +1422,16 @@ function TopBar({
{/* ⭐ 分配单时效 —— 只在单子**还挂在人手上**时显示(回到池子里的没有"还剩多久")。
⚠️ 基于 assignmentExpiresAt(已启用),不是 recycleAt(认领的 24h 兜底,从未启用)。 */}
{plan.assigneeUserId && <ExpiryBadge expiresAt={plan.assignmentExpiresAt} />}
{/*
🔴 **回主管工作台的路** —— 与工作台头部那条「客服执行页 ↗」配成一对。
工作台那边的注释早就写了「两个页面是双向的,⛔ 别做成有去无回」,
但实际只做了去的那一半:主管点进执行页之后,除了改地址栏没有回去的路
(2026-08-10 产品发现)。
⚠️ 只有**有派单权**的人看得见 —— 客服看到一个自己点不进去的入口只会困惑
(那条路由对他会直接弹回来)。
⚠️ 嵌入宿主时隐藏:与 IdentityCluster 同一条规矩,宿主自有导航。
*/}
<SupervisorBackLink />
{/* 用户名 + 头像 + 退出 —— 嵌入宿主时整体隐藏(见 IdentityCluster) */}
<IdentityCluster />
</div>
......@@ -1488,6 +1501,30 @@ function AIDisclaimerFooter({
}
// ──────────────────────────────────────────
// SupervisorBackLink — 回主管工作台(与工作台头部的「客服执行页 ↗」配成一对)
// ──────────────────────────────────────────
/**
* ⚠️ 判据是 `PLAN_DISPATCH`(派单权),与工作台那条路由**同一把闸** ——
* ⛔ 别改用 role 判断:权限是按 role 现算的,而 token 里的 role 是签发时固化的,
* 两处判据一旦不同源,就会出现"看得见入口、点进去被弹回来"。
* ⚠️ 嵌入宿主(iframe)时不显示 —— 宿主自有导航,与 IdentityCluster 同一条规矩。
*/
function SupervisorBackLink() {
const canDispatch = useHasPermission(Permission.PLAN_DISPATCH);
if (!canDispatch || isEmbedded()) return null;
return (
<Link
href="/supervisor"
title="主管工作台"
className="hidden sm:inline-flex flex-none items-center gap-1 text-[12.5px] text-brand-700 hover:underline"
>
主管工作台
<ArrowUpRight className="h-3.5 w-3.5" />
</Link>
);
}
// ──────────────────────────────────────────
// ExpiryBadge — 分配单还剩多久(到点自动退回召回池)
// ──────────────────────────────────────────
/**
......
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