feat: 任务工作区 UI 细化(底部按钮主题色/左上业务名/右侧智能体信息默认收起/xdfx目的弹框按行动id解析)
- 底部「快捷跳转」按钮改用主题色 var(--color-main)(随红/蓝主题自适配),hover 用 --brand-950 - 三个工作区左上角标明当前分析页面(任务分析/三情分析/行动分析) - 进入任务工作区(taskGate/taskBind)时右侧「智能体信息」面板默认收起(AgentDetailPanel defaultCollapsed) - xdfx「弹框查看」目的树:先用行动id换回任务id 再查(TaskPurposeDialog resolveFromActionId); 仅深链原始行动id(≠任务id)时触发,切换业务进入不误解析 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
095469b8a9
commit
f762a76ecf
4 files changed
+50
-9
No files matched your search
@@ -131,14 +131,17 @@ export function AgentDetailPanel({
|
|||||||
agentName,
|
agentName,
|
||||||
onAskQuestion,
|
onAskQuestion,
|
||||||
disabled,
|
disabled,
|
||||||
|
defaultCollapsed,
|
||||||
}: {
|
}: {
|
||||||
agent: Agent | null | undefined;
|
agent: Agent | null | undefined;
|
||||||
agentName: string;
|
agentName: string;
|
||||||
onAskQuestion: (text: string) => void;
|
onAskQuestion: (text: string) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
/** 初始收起(任务工作区进入时默认收起智能体信息面板)。 */
|
||||||
|
defaultCollapsed?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(defaultCollapsed ?? false);
|
||||||
const [mobileOpen, setMobileOpen] = useState(false);
|
const [mobileOpen, setMobileOpen] = useState(false);
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
const [draft, setDraft] = useState<AgentDraft | null>(null);
|
const [draft, setDraft] = useState<AgentDraft | null>(null);
|
||||||
|
|||||||
@@ -435,6 +435,7 @@ export default function AgentChatPage({
|
|||||||
agentName={agentDisplayName}
|
agentName={agentDisplayName}
|
||||||
disabled={thread.isLoading || isDemoMode}
|
disabled={thread.isLoading || isDemoMode}
|
||||||
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
|
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
|
||||||
|
defaultCollapsed={taskCtxOn}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ThreadContext.Provider>
|
</ThreadContext.Provider>
|
||||||
|
|||||||
+21
-2
@@ -36,6 +36,7 @@ import {
|
|||||||
type TaskDeeplinkConfig,
|
type TaskDeeplinkConfig,
|
||||||
} from "@/core/auth/task-deeplink-config";
|
} from "@/core/auth/task-deeplink-config";
|
||||||
import {
|
import {
|
||||||
|
TASK_BUSINESSES,
|
||||||
buttonsForBusiness,
|
buttonsForBusiness,
|
||||||
resolveTaskButtons,
|
resolveTaskButtons,
|
||||||
runTaskButton,
|
runTaskButton,
|
||||||
@@ -120,6 +121,10 @@ function TaskJumpBar({ business, taskId }: { business: TaskBusiness; taskId: str
|
|||||||
|
|
||||||
// 目的树弹框(purpose 类型按钮)由组件层打开。
|
// 目的树弹框(purpose 类型按钮)由组件层打开。
|
||||||
const [purposeOpen, setPurposeOpen] = useState(false);
|
const [purposeOpen, setPurposeOpen] = useState(false);
|
||||||
|
// xdfx:深链原始「行动id」(与解析后的任务id 不同才算真行动id),弹框据此换回任务id 再查目的。
|
||||||
|
const ctxActionId = readTaskContext()?.actionId;
|
||||||
|
const purposeActionId =
|
||||||
|
business === "xdfx" && ctxActionId && ctxActionId !== taskId ? ctxActionId : undefined;
|
||||||
|
|
||||||
if (jumpButtons.length === 0) return null;
|
if (jumpButtons.length === 0) return null;
|
||||||
|
|
||||||
@@ -141,7 +146,7 @@ function TaskJumpBar({ business, taskId }: { business: TaskBusiness; taskId: str
|
|||||||
{jumpButtons.map((btn) => (
|
{jumpButtons.map((btn) => (
|
||||||
<Button
|
<Button
|
||||||
key={btn.id}
|
key={btn.id}
|
||||||
className="h-11 gap-2 border-transparent bg-[#cf1322] px-6 text-base text-white shadow-sm hover:bg-[#a30f1b]"
|
className="h-11 gap-2 border-transparent bg-[var(--color-main)] px-6 text-base text-white shadow-sm hover:bg-[var(--brand-950)]"
|
||||||
onClick={() => handleRun(btn)}
|
onClick={() => handleRun(btn)}
|
||||||
title={btn.label}
|
title={btn.label}
|
||||||
>
|
>
|
||||||
@@ -155,7 +160,12 @@ function TaskJumpBar({ business, taskId }: { business: TaskBusiness; taskId: str
|
|||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
<TaskPurposeDialog taskId={taskId} open={purposeOpen} onOpenChange={setPurposeOpen} />
|
<TaskPurposeDialog
|
||||||
|
taskId={taskId}
|
||||||
|
open={purposeOpen}
|
||||||
|
onOpenChange={setPurposeOpen}
|
||||||
|
resolveFromActionId={purposeActionId}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -168,21 +178,29 @@ function TaskConversationsList({
|
|||||||
taskId,
|
taskId,
|
||||||
agentId,
|
agentId,
|
||||||
chatsBase,
|
chatsBase,
|
||||||
|
business,
|
||||||
lite,
|
lite,
|
||||||
}: {
|
}: {
|
||||||
taskId: string;
|
taskId: string;
|
||||||
agentId: string;
|
agentId: string;
|
||||||
chatsBase: string;
|
chatsBase: string;
|
||||||
|
business: TaskBusiness;
|
||||||
lite?: boolean;
|
lite?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { threads, isLoading } = useTaskThreads(taskId, agentId);
|
const { threads, isLoading } = useTaskThreads(taskId, agentId);
|
||||||
const currentThreadId = useMemo(() => parseThreadId(pathname, chatsBase), [pathname, chatsBase]);
|
const currentThreadId = useMemo(() => parseThreadId(pathname, chatsBase), [pathname, chatsBase]);
|
||||||
|
const businessName = TASK_BUSINESSES.find((b) => b.code === business)?.name ?? "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-background/85 border-border/70 flex w-64 shrink-0 flex-col border-r shadow-[12px_0_30px_-30px_rgba(15,23,42,0.45)] backdrop-blur-xl dark:border-[#1b477e]/70 dark:bg-[#061b35]/82">
|
<div className="bg-background/85 border-border/70 flex w-64 shrink-0 flex-col border-r shadow-[12px_0_30px_-30px_rgba(15,23,42,0.45)] backdrop-blur-xl dark:border-[#1b477e]/70 dark:bg-[#061b35]/82">
|
||||||
<div className="border-border/70 border-b p-3 dark:border-[#1b477e]/70">
|
<div className="border-border/70 border-b p-3 dark:border-[#1b477e]/70">
|
||||||
|
{/* 左上角标明当前是哪个分析页面 */}
|
||||||
|
<div className="mb-3 flex items-center gap-2">
|
||||||
|
<span className="h-4 w-1 rounded-full bg-[var(--color-main)]" />
|
||||||
|
<span className="text-foreground text-sm font-semibold tracking-wide">{businessName}</span>
|
||||||
|
</div>
|
||||||
<TaskDetailHeader />
|
<TaskDetailHeader />
|
||||||
<div className="mb-2 flex items-center justify-between gap-2">
|
<div className="mb-2 flex items-center justify-between gap-2">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
@@ -318,6 +336,7 @@ export function TaskFxWorkspace({
|
|||||||
taskId={taskId}
|
taskId={taskId}
|
||||||
agentId={agentId}
|
agentId={agentId}
|
||||||
chatsBase={chatsBase}
|
chatsBase={chatsBase}
|
||||||
|
business={business}
|
||||||
lite={lite}
|
lite={lite}
|
||||||
/>
|
/>
|
||||||
<div className="relative min-h-0 min-w-0 flex-1">
|
<div className="relative min-h-0 min-w-0 flex-1">
|
||||||
|
|||||||
+24
-6
@@ -30,6 +30,7 @@ import {
|
|||||||
type PurposeTreeNode,
|
type PurposeTreeNode,
|
||||||
type TaskPurposeResult,
|
type TaskPurposeResult,
|
||||||
} from "@/core/auth/task-purpose";
|
} from "@/core/auth/task-purpose";
|
||||||
|
import { fetchTaskIdFromAction } from "@/core/auth/task-deeplink";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
/** 目的树表格:拍平成行,列数随数据自适应;层级缺失补空、字段变动不崩。 */
|
/** 目的树表格:拍平成行,列数随数据自适应;层级缺失补空、字段变动不崩。 */
|
||||||
@@ -98,32 +99,49 @@ export function TaskPurposeDialog({
|
|||||||
taskId,
|
taskId,
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
|
resolveFromActionId,
|
||||||
}: {
|
}: {
|
||||||
taskId: string;
|
taskId: string;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
|
/** xdfx:深链原始「行动id」。提供时弹框先用它换出真正「任务id」再查目的树。 */
|
||||||
|
resolveFromActionId?: string;
|
||||||
}) {
|
}) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [result, setResult] = useState<TaskPurposeResult | null>(null);
|
const [result, setResult] = useState<TaskPurposeResult | null>(null);
|
||||||
|
const [resolvedTaskId, setResolvedTaskId] = useState(taskId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
fetchTaskPurposeDetail(taskId)
|
(async () => {
|
||||||
.then((r) => !cancelled && setResult(r))
|
try {
|
||||||
.catch(() => !cancelled && setResult({ tree: [], hasData: false }))
|
// xdfx:行动id 换任务id 再查目的树(与登录时的解析一致);无行动id 时直接用任务id。
|
||||||
.finally(() => !cancelled && setLoading(false));
|
let tid = taskId;
|
||||||
|
if (resolveFromActionId) {
|
||||||
|
tid = (await fetchTaskIdFromAction(resolveFromActionId)) || taskId;
|
||||||
|
}
|
||||||
|
if (cancelled) return;
|
||||||
|
setResolvedTaskId(tid);
|
||||||
|
const r = await fetchTaskPurposeDetail(tid);
|
||||||
|
if (!cancelled) setResult(r);
|
||||||
|
} catch {
|
||||||
|
if (!cancelled) setResult({ tree: [], hasData: false });
|
||||||
|
} finally {
|
||||||
|
if (!cancelled) setLoading(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [open, taskId]);
|
}, [open, taskId, resolveFromActionId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="w-[90vw] !max-w-[90vw] sm:!max-w-[90vw]">
|
<DialogContent className="w-[90vw] !max-w-[90vw] sm:!max-w-[90vw]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>任务目的(任务 {taskId})</DialogTitle>
|
<DialogTitle>任务目的(任务 {resolvedTaskId})</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="text-muted-foreground flex items-center gap-2 py-6 text-sm">
|
<div className="text-muted-foreground flex items-center gap-2 py-6 text-sm">
|
||||||
|
|||||||
Reference in new issue
Block a user