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,
|
||||
onAskQuestion,
|
||||
disabled,
|
||||
defaultCollapsed,
|
||||
}: {
|
||||
agent: Agent | null | undefined;
|
||||
agentName: string;
|
||||
onAskQuestion: (text: string) => void;
|
||||
disabled?: boolean;
|
||||
/** 初始收起(任务工作区进入时默认收起智能体信息面板)。 */
|
||||
defaultCollapsed?: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [collapsed, setCollapsed] = useState(defaultCollapsed ?? false);
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [draft, setDraft] = useState<AgentDraft | null>(null);
|
||||
|
||||
@@ -435,6 +435,7 @@ export default function AgentChatPage({
|
||||
agentName={agentDisplayName}
|
||||
disabled={thread.isLoading || isDemoMode}
|
||||
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
|
||||
defaultCollapsed={taskCtxOn}
|
||||
/>
|
||||
</div>
|
||||
</ThreadContext.Provider>
|
||||
|
||||
+21
-2
@@ -36,6 +36,7 @@ import {
|
||||
type TaskDeeplinkConfig,
|
||||
} from "@/core/auth/task-deeplink-config";
|
||||
import {
|
||||
TASK_BUSINESSES,
|
||||
buttonsForBusiness,
|
||||
resolveTaskButtons,
|
||||
runTaskButton,
|
||||
@@ -120,6 +121,10 @@ function TaskJumpBar({ business, taskId }: { business: TaskBusiness; taskId: str
|
||||
|
||||
// 目的树弹框(purpose 类型按钮)由组件层打开。
|
||||
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;
|
||||
|
||||
@@ -141,7 +146,7 @@ function TaskJumpBar({ business, taskId }: { business: TaskBusiness; taskId: str
|
||||
{jumpButtons.map((btn) => (
|
||||
<Button
|
||||
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)}
|
||||
title={btn.label}
|
||||
>
|
||||
@@ -155,7 +160,12 @@ function TaskJumpBar({ business, taskId }: { business: TaskBusiness; taskId: str
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
<TaskPurposeDialog taskId={taskId} open={purposeOpen} onOpenChange={setPurposeOpen} />
|
||||
<TaskPurposeDialog
|
||||
taskId={taskId}
|
||||
open={purposeOpen}
|
||||
onOpenChange={setPurposeOpen}
|
||||
resolveFromActionId={purposeActionId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -168,21 +178,29 @@ function TaskConversationsList({
|
||||
taskId,
|
||||
agentId,
|
||||
chatsBase,
|
||||
business,
|
||||
lite,
|
||||
}: {
|
||||
taskId: string;
|
||||
agentId: string;
|
||||
chatsBase: string;
|
||||
business: TaskBusiness;
|
||||
lite?: boolean;
|
||||
}) {
|
||||
const { pathname } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { threads, isLoading } = useTaskThreads(taskId, agentId);
|
||||
const currentThreadId = useMemo(() => parseThreadId(pathname, chatsBase), [pathname, chatsBase]);
|
||||
const businessName = TASK_BUSINESSES.find((b) => b.code === business)?.name ?? "";
|
||||
|
||||
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="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 />
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<div className="min-w-0">
|
||||
@@ -318,6 +336,7 @@ export function TaskFxWorkspace({
|
||||
taskId={taskId}
|
||||
agentId={agentId}
|
||||
chatsBase={chatsBase}
|
||||
business={business}
|
||||
lite={lite}
|
||||
/>
|
||||
<div className="relative min-h-0 min-w-0 flex-1">
|
||||
|
||||
+24
-6
@@ -30,6 +30,7 @@ import {
|
||||
type PurposeTreeNode,
|
||||
type TaskPurposeResult,
|
||||
} from "@/core/auth/task-purpose";
|
||||
import { fetchTaskIdFromAction } from "@/core/auth/task-deeplink";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/** 目的树表格:拍平成行,列数随数据自适应;层级缺失补空、字段变动不崩。 */
|
||||
@@ -98,32 +99,49 @@ export function TaskPurposeDialog({
|
||||
taskId,
|
||||
open,
|
||||
onOpenChange,
|
||||
resolveFromActionId,
|
||||
}: {
|
||||
taskId: string;
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
/** xdfx:深链原始「行动id」。提供时弹框先用它换出真正「任务id」再查目的树。 */
|
||||
resolveFromActionId?: string;
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [result, setResult] = useState<TaskPurposeResult | null>(null);
|
||||
const [resolvedTaskId, setResolvedTaskId] = useState(taskId);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
fetchTaskPurposeDetail(taskId)
|
||||
.then((r) => !cancelled && setResult(r))
|
||||
.catch(() => !cancelled && setResult({ tree: [], hasData: false }))
|
||||
.finally(() => !cancelled && setLoading(false));
|
||||
(async () => {
|
||||
try {
|
||||
// xdfx:行动id 换任务id 再查目的树(与登录时的解析一致);无行动id 时直接用任务id。
|
||||
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 () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [open, taskId]);
|
||||
}, [open, taskId, resolveFromActionId]);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="w-[90vw] !max-w-[90vw] sm:!max-w-[90vw]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>任务目的(任务 {taskId})</DialogTitle>
|
||||
<DialogTitle>任务目的(任务 {resolvedTaskId})</DialogTitle>
|
||||
</DialogHeader>
|
||||
{loading ? (
|
||||
<div className="text-muted-foreground flex items-center gap-2 py-6 text-sm">
|
||||
|
||||
Reference in new issue
Block a user