样式修改
This commit is contained in:
1 parent
08ce3dda73
commit
9f788e37de
4 files changed
+468
-150
No files matched your search
@@ -465,7 +465,7 @@ export function PositionNodeChat({
|
||||
</span>
|
||||
{thread.isLoading ? (
|
||||
<div
|
||||
className="flex h-9 shrink-0 items-center rounded-xl border border-slate-200 bg-white px-3 text-xs font-semibold text-slate-700 shadow-sm dark:border-[#2b8ffe]/35 dark:bg-[#082d5f] dark:text-[#e8edf5]"
|
||||
className="flex h-9 shrink-0 items-center rounded-[4px] border border-slate-200 bg-white px-3 text-xs font-semibold text-slate-700 shadow-sm dark:border-[#2b8ffe]/35 dark:bg-[#082d5f] dark:text-[#e8edf5]"
|
||||
style={switchSelectStyle}
|
||||
>
|
||||
{agentDisplayName} · 进行中
|
||||
@@ -473,7 +473,7 @@ export function PositionNodeChat({
|
||||
) : (
|
||||
<TSelect
|
||||
aria-label="同岗位智能体切换"
|
||||
className="shrink-0 [&_.t-input]:!rounded-xl [&_.t-input]:!border-slate-200 [&_.t-input]:!bg-white [&_.t-input]:!text-xs [&_.t-input]:!font-semibold [&_.t-input]:!text-slate-700 [&_.t-input]:!shadow-sm dark:[&_.t-input]:!border-[#2b8ffe]/35 dark:[&_.t-input]:!bg-[#082d5f] dark:[&_.t-input]:!text-[#e8edf5]"
|
||||
className="shrink-0 [&_.t-input]:!rounded-[4px] [&_.t-input]:!border-slate-200 [&_.t-input]:!bg-white [&_.t-input]:!text-xs [&_.t-input]:!font-semibold [&_.t-input]:!text-slate-700 [&_.t-input]:!shadow-sm dark:[&_.t-input]:!border-[#2b8ffe]/35 dark:[&_.t-input]:!bg-[#082d5f] dark:[&_.t-input]:!text-[#e8edf5]"
|
||||
style={switchSelectStyle}
|
||||
value={node.nodeKey}
|
||||
options={switchSelectOptions}
|
||||
|
||||
@@ -11,7 +11,8 @@ export interface PositionTask {
|
||||
title: string;
|
||||
description: string;
|
||||
direction: string;
|
||||
source: "mock";
|
||||
source: string;
|
||||
issuedAt: string;
|
||||
}
|
||||
|
||||
export const MOCK_POSITION_TASKS: readonly PositionTask[] = [
|
||||
@@ -21,7 +22,8 @@ export const MOCK_POSITION_TASKS: readonly PositionTask[] = [
|
||||
description:
|
||||
"围绕暑期活动的游泳夏令营做宣传",
|
||||
direction: "暑期活动",
|
||||
source: "mock",
|
||||
source: "本系统创建",
|
||||
issuedAt: "2026-07-22 17:42:00",
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -51,6 +53,13 @@ export function taskFromPositionSnapshot(
|
||||
title: value(["title", "taskName", "name"], fallback.title),
|
||||
description: value(["description", "taskContent", "overview", "content"], fallback.description),
|
||||
direction: value(["direction", "taskDirection", "category"], fallback.direction),
|
||||
source: "mock",
|
||||
source: (() => {
|
||||
const source = value(["source", "taskSource", "sourceName"], fallback.source);
|
||||
return source === "mock" ? "本系统创建" : source;
|
||||
})(),
|
||||
issuedAt: value(
|
||||
["issuedAt", "issued_at", "dispatchTime", "publishedAt", "createdAt", "created_at"],
|
||||
fallback.issuedAt,
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { BaseStream } from "@langchain/langgraph-sdk/react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import {
|
||||
AlertTriangle,
|
||||
ArrowLeft,
|
||||
Archive,
|
||||
Bot,
|
||||
BriefcaseBusiness,
|
||||
CheckCircle2,
|
||||
@@ -16,6 +15,7 @@ import {
|
||||
FileOutput,
|
||||
FileText,
|
||||
GitBranch,
|
||||
History,
|
||||
Layers3,
|
||||
Loader2,
|
||||
MessageSquareText,
|
||||
@@ -51,6 +51,14 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { LayoutModeSwitcher, ThemeSwitcher } from "@/components/workspace/appearance-controls";
|
||||
import { getAPIClient } from "@/core/api/api-client";
|
||||
@@ -84,7 +92,6 @@ import {
|
||||
} from "@/position-roundtable/lib/positions";
|
||||
import {
|
||||
activatePositionSession,
|
||||
archivePositionSession,
|
||||
completePositionActionPlan,
|
||||
completePositionSummary,
|
||||
createPositionSession,
|
||||
@@ -375,6 +382,27 @@ function formatResultTime(value: string | undefined, fallback = "待完成"): st
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
function historySessionTitle(session: PositionRoundtableSession): string {
|
||||
for (const key of ["title", "taskName", "task_name"]) {
|
||||
const value = session.taskSnapshot?.[key];
|
||||
if (typeof value === "string" && value.trim()) return value.trim();
|
||||
}
|
||||
return "岗位协同会话";
|
||||
}
|
||||
|
||||
function historySessionStatus(status: PositionRoundtableSession["status"]): string {
|
||||
switch (status) {
|
||||
case "archived":
|
||||
return "已归档";
|
||||
case "completed":
|
||||
return "已完成";
|
||||
case "active":
|
||||
return "进行中";
|
||||
default:
|
||||
return "意图识别";
|
||||
}
|
||||
}
|
||||
|
||||
function artifactName(artifact: Record<string, unknown> | null | undefined): string {
|
||||
if (!artifact) return "";
|
||||
const name = typeof artifact.name === "string" ? artifact.name.trim() : "";
|
||||
@@ -463,8 +491,10 @@ export default function PositionRoundtablePage({
|
||||
const [startingChain, setStartingChain] = useState(false);
|
||||
const [rightPanelOpen, setRightPanelOpen] = useState(true);
|
||||
const [rightTab, setRightTab] = useState<WorkspaceTab>("agents");
|
||||
const [taskPanelCollapsed, setTaskPanelCollapsed] = useState(false);
|
||||
const [intentPanelCollapsed, setIntentPanelCollapsed] = useState(false);
|
||||
const [leftPanelCollapsed, setLeftPanelCollapsed] = useState(false);
|
||||
const [historyMenuOpen, setHistoryMenuOpen] = useState(false);
|
||||
const [historySessions, setHistorySessions] = useState<PositionRoundtableSession[]>([]);
|
||||
const [historyLoading, setHistoryLoading] = useState(false);
|
||||
const artifactsCtx = useArtifactsOptional();
|
||||
const artifactsCtxRef = useRef(artifactsCtx);
|
||||
const [sandboxArtifact, setSandboxArtifact] = useState<PositionArtifactPreview | null>(null);
|
||||
@@ -634,6 +664,8 @@ export default function PositionRoundtablePage({
|
||||
title,
|
||||
description: taskDraft.description.trim(),
|
||||
direction: taskDraft.direction.trim() || task.direction,
|
||||
source: taskDraft.source.trim() || task.source,
|
||||
issuedAt: taskDraft.issuedAt.trim() || task.issuedAt,
|
||||
};
|
||||
setTaskSaving(true);
|
||||
try {
|
||||
@@ -751,15 +783,14 @@ export default function PositionRoundtablePage({
|
||||
}, [positionSession?.chainSnapshot, positionSession?.status, selectedChain]);
|
||||
const isIntelligenceDeliveryOverview =
|
||||
activePosition.isIntentPosition && positionSession !== null && positionSession.status !== "intent_pending";
|
||||
const showTaskAndIntentPanels = !activePosition.isIntentPosition || isIntelligenceDeliveryOverview;
|
||||
const leftSidebarHidden =
|
||||
showTaskAndIntentPanels && taskPanelCollapsed && intentPanelCollapsed;
|
||||
const leftSidebarHidden = leftPanelCollapsed;
|
||||
const allPositionDeliveriesDone = useMemo(
|
||||
() => allExecutableNodesDone(positionSession?.nodes ?? []),
|
||||
[positionSession?.nodes],
|
||||
);
|
||||
const summaryDone = snapshotStatus(positionSession?.summarySnapshot) === "done";
|
||||
const actionPlanDone = snapshotStatus(positionSession?.actionPlanSnapshot) === "done";
|
||||
const showClosureAgents = positionId === DEFAULT_POSITION_ID;
|
||||
const [activeBuiltinKind, setActiveBuiltinKind] = useState<PositionBuiltinAgentKind | null>(null);
|
||||
const [builtinAutoStartRequestKey, setBuiltinAutoStartRequestKey] = useState(0);
|
||||
const builtinAgentOptions = useMemo<PositionBuiltinAgentSwitchOption[]>(
|
||||
@@ -1158,18 +1189,54 @@ export default function PositionRoundtablePage({
|
||||
setSearchParams(next);
|
||||
}, [intent, searchParams, setSearchParams, task.id]);
|
||||
|
||||
const handleArchiveSession = useCallback(() => {
|
||||
if (!positionSession || positionSession.status === "archived") return;
|
||||
if (!window.confirm("归档后该会话将只读,是否继续?")) return;
|
||||
void archivePositionSession(positionSession.id)
|
||||
.then((session) => {
|
||||
setPositionSession((current) => mergePositionSession(current, session));
|
||||
toast.success("会话已归档,可新建会话继续研讨");
|
||||
const loadHistorySessions = useCallback(() => {
|
||||
setHistoryLoading(true);
|
||||
void listPositionSessions(task.id)
|
||||
.then((sessions) => {
|
||||
setHistorySessions(
|
||||
[...sessions].sort((left, right) => {
|
||||
const leftTime = new Date(left.updatedAt ?? left.createdAt ?? 0).getTime();
|
||||
const rightTime = new Date(right.updatedAt ?? right.createdAt ?? 0).getTime();
|
||||
return rightTime - leftTime;
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error(`归档会话失败:${error instanceof Error ? error.message : String(error)}`);
|
||||
toast.error(`加载历史记录失败:${error instanceof Error ? error.message : String(error)}`);
|
||||
})
|
||||
.finally(() => {
|
||||
setHistoryLoading(false);
|
||||
});
|
||||
}, [positionSession]);
|
||||
}, [task.id]);
|
||||
|
||||
const handleHistoryMenuOpenChange = useCallback(
|
||||
(open: boolean) => {
|
||||
setHistoryMenuOpen(open);
|
||||
if (open) loadHistorySessions();
|
||||
},
|
||||
[loadHistorySessions],
|
||||
);
|
||||
|
||||
const handleHistorySessionSelect = useCallback(
|
||||
(sessionId: string) => {
|
||||
setHistoryMenuOpen(false);
|
||||
if (sessionId === sessionIdFromUrl) return;
|
||||
|
||||
const ctx = artifactsCtxRef.current;
|
||||
if (sandboxArtifactRef.current || ctx?.open) ctx?.setOpen(false);
|
||||
setSandboxArtifact(null);
|
||||
setSandboxThreadSnapshot(null);
|
||||
setActiveNodeKey(null);
|
||||
setActiveBuiltinKind(null);
|
||||
setSelectedAgentId(null);
|
||||
const next = new URLSearchParams(searchParams);
|
||||
next.set("taskId", task.id);
|
||||
next.set("sessionId", sessionId);
|
||||
next.delete("new");
|
||||
setSearchParams(next);
|
||||
},
|
||||
[searchParams, sessionIdFromUrl, setSearchParams, task.id],
|
||||
);
|
||||
|
||||
const handlePreviewArtifact = useCallback((artifact: PositionArtifactPreview) => {
|
||||
if (!artifact.threadId || !artifact.path) {
|
||||
@@ -1315,6 +1382,10 @@ export default function PositionRoundtablePage({
|
||||
}, []);
|
||||
const handleSelectBuiltinStage = useCallback(
|
||||
(kind: ClosureStageKind) => {
|
||||
if (positionId !== DEFAULT_POSITION_ID) {
|
||||
toast.info("收口智能体仅在测试分析岗中开放使用。");
|
||||
return;
|
||||
}
|
||||
if (!allPositionDeliveriesDone) {
|
||||
toast.info("岗位产物还在汇集中;全部岗位完成 Markdown 交付后,即可开启方案总结。");
|
||||
return;
|
||||
@@ -1330,7 +1401,7 @@ export default function PositionRoundtablePage({
|
||||
setActiveBuiltinKind(kind);
|
||||
setBuiltinAutoStartRequestKey((current) => current + 1);
|
||||
},
|
||||
[allPositionDeliveriesDone, closeSandbox, summaryDone],
|
||||
[allPositionDeliveriesDone, closeSandbox, positionId, summaryDone],
|
||||
);
|
||||
const handleSelectPositionNode = useCallback(
|
||||
(nextNode: PositionRoundtableNode) => {
|
||||
@@ -1353,8 +1424,7 @@ export default function PositionRoundtablePage({
|
||||
setSelectedAgentId(null);
|
||||
setActiveNodeKey(null);
|
||||
setRightPanelOpen(nextPositionId === DEFAULT_POSITION_ID);
|
||||
setTaskPanelCollapsed(false);
|
||||
setIntentPanelCollapsed(false);
|
||||
setLeftPanelCollapsed(false);
|
||||
},
|
||||
[closeSandbox],
|
||||
);
|
||||
@@ -1490,17 +1560,72 @@ export default function PositionRoundtablePage({
|
||||
>
|
||||
新建会话
|
||||
</TButton>
|
||||
{positionSession && positionSession.status !== "archived" && (
|
||||
<TButton
|
||||
size="small"
|
||||
variant="outline"
|
||||
className="shrink-0 whitespace-nowrap"
|
||||
icon={<Archive className="size-3.5" />}
|
||||
onClick={handleArchiveSession}
|
||||
<DropdownMenu open={historyMenuOpen} onOpenChange={handleHistoryMenuOpenChange}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<TButton
|
||||
size="small"
|
||||
variant="outline"
|
||||
className="shrink-0 whitespace-nowrap"
|
||||
icon={<History className="size-3.5" />}
|
||||
>
|
||||
历史记录
|
||||
</TButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-[320px] rounded-[4px] border-slate-200 p-1.5 shadow-lg dark:border-[#2b8ffe]/50 dark:bg-[#032650]"
|
||||
>
|
||||
归档
|
||||
</TButton>
|
||||
)}
|
||||
<DropdownMenuLabel className="px-2.5 py-2 text-xs font-semibold text-slate-700 dark:text-[#d9efff]">
|
||||
当前任务的历史记录
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator className="bg-slate-200 dark:bg-[#2b8ffe]/30" />
|
||||
{historyLoading ? (
|
||||
<div className="flex h-20 items-center justify-center gap-2 text-xs text-muted-foreground dark:text-[#8aa0c4]">
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
正在加载历史记录…
|
||||
</div>
|
||||
) : historySessions.length === 0 ? (
|
||||
<div className="px-2.5 py-5 text-center text-xs text-muted-foreground dark:text-[#8aa0c4]">
|
||||
暂无历史记录
|
||||
</div>
|
||||
) : (
|
||||
<div className="max-h-80 overflow-y-auto py-0.5">
|
||||
{historySessions.map((session) => {
|
||||
const current = session.id === sessionIdFromUrl;
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={session.id}
|
||||
onSelect={() => handleHistorySessionSelect(session.id)}
|
||||
className={cn(
|
||||
"mb-1 flex cursor-pointer items-start rounded-[4px] px-2.5 py-2.5 last:mb-0",
|
||||
current
|
||||
? "bg-primary/10 text-primary dark:bg-[#2b8ffe]/15 dark:text-[#a2e1f9]"
|
||||
: "text-slate-700 dark:text-[#d8e6f8] dark:focus:bg-[#0b386d] dark:focus:text-white",
|
||||
)}
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span className="truncate text-xs font-semibold">
|
||||
{desensitize(historySessionTitle(session))}
|
||||
</span>
|
||||
{current && (
|
||||
<span className="shrink-0 rounded-[4px] bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold text-primary dark:bg-[#2b8ffe]/15 dark:text-[#a2e1f9]">
|
||||
当前
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1 flex items-center justify-between gap-2 text-[10px] text-muted-foreground dark:text-[#8aa0c4]">
|
||||
<span>{historySessionStatus(session.status)}</span>
|
||||
<span>{formatResultTime(session.updatedAt ?? session.createdAt, "未记录时间")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<span className="hidden shrink-0 whitespace-nowrap text-xs font-medium text-muted-foreground sm:inline">
|
||||
当前岗位
|
||||
</span>
|
||||
@@ -1529,57 +1654,49 @@ export default function PositionRoundtablePage({
|
||||
<aside
|
||||
aria-hidden={leftSidebarHidden || undefined}
|
||||
className={cn(
|
||||
"custom-scrollbar relative flex min-h-0 shrink-0 flex-col gap-3 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm transition-[width,opacity,transform,padding,border-width] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none dark:rounded-none dark:border-0 dark:bg-transparent dark:text-[#c5cfe0] dark:shadow-none",
|
||||
"custom-scrollbar relative flex min-h-0 shrink-0 flex-col gap-3 overflow-y-auto rounded-2xl border border-slate-200 bg-white shadow-sm transition-[width,opacity,transform,padding,border-width] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none dark:rounded-none dark:border-0 dark:bg-transparent dark:text-[#c5cfe0] dark:shadow-none",
|
||||
leftSidebarHidden
|
||||
? "w-0 -translate-x-4 !border-0 !p-0 opacity-0 pointer-events-none"
|
||||
? "w-0 translate-x-4 !border-0 !p-0 opacity-0 pointer-events-none"
|
||||
: "w-[360px] translate-x-0 p-3 opacity-100 dark:p-0",
|
||||
)}
|
||||
>
|
||||
<TaskInformationCard
|
||||
<TaskAnalysisPanel
|
||||
task={task}
|
||||
editable={!isArchivedSession}
|
||||
onEdit={openTaskEditor}
|
||||
collapsed={taskPanelCollapsed}
|
||||
onCollapsedChange={setTaskPanelCollapsed}
|
||||
/>
|
||||
|
||||
{activePosition.isIntentPosition ? (
|
||||
isIntelligenceDeliveryOverview ? (
|
||||
<IntentUnderstandingCard
|
||||
intent={frozenIntent ?? intent.intentReady}
|
||||
editable={!isArchivedSession}
|
||||
onEdit={openIntentEditor}
|
||||
collapsed={intentPanelCollapsed}
|
||||
onCollapsedChange={setIntentPanelCollapsed}
|
||||
/>
|
||||
) : (
|
||||
intent={frozenIntent ?? intent.intentReady}
|
||||
taskEditable={!isArchivedSession}
|
||||
intentEditable={!isArchivedSession}
|
||||
onEditTask={openTaskEditor}
|
||||
onEditIntent={openIntentEditor}
|
||||
collapsed={false}
|
||||
onCollapsedChange={setLeftPanelCollapsed}
|
||||
className="min-h-full"
|
||||
>
|
||||
{activePosition.isIntentPosition && !isIntelligenceDeliveryOverview && (
|
||||
<BusinessChainSelector
|
||||
chains={chains}
|
||||
loading={chainsLoading}
|
||||
value={selectedChainId}
|
||||
onChange={setSelectedChainId}
|
||||
onConfigure={openPositionConfig}
|
||||
className="min-h-0 flex-1"
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<div className="contents">
|
||||
<IntentUnderstandingCard
|
||||
intent={frozenIntent ?? intent.intentReady}
|
||||
editable={!isArchivedSession}
|
||||
onEdit={openIntentEditor}
|
||||
collapsed={intentPanelCollapsed}
|
||||
onCollapsedChange={setIntentPanelCollapsed}
|
||||
/>
|
||||
)}
|
||||
{(!activePosition.isIntentPosition || isIntelligenceDeliveryOverview) && (
|
||||
<BusinessChainFlow
|
||||
chain={activeChain}
|
||||
nodes={positionSession?.nodes ?? []}
|
||||
session={positionSession}
|
||||
activeNodeKey={activeNodeKey}
|
||||
onSelectIntent={
|
||||
activePosition.isIntentPosition ? openIntentFlowDetail : undefined
|
||||
}
|
||||
onSelectNode={openNodeFlowDetail}
|
||||
onSelectSeat={openSeatFlowDetail}
|
||||
onSelectBuiltin={handleSelectBuiltinStage}
|
||||
embedded
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</TaskAnalysisPanel>
|
||||
</aside>
|
||||
|
||||
{leftSidebarHidden && (
|
||||
@@ -1588,8 +1705,7 @@ export default function PositionRoundtablePage({
|
||||
title="展开左侧任务与意图信息"
|
||||
aria-label="展开左侧任务与意图信息"
|
||||
onClick={() => {
|
||||
setTaskPanelCollapsed(false);
|
||||
setIntentPanelCollapsed(false);
|
||||
setLeftPanelCollapsed(false);
|
||||
}}
|
||||
className="absolute left-3 top-3 z-20 flex h-10 w-[30px] items-center justify-center rounded-r-[4px] border border-l-0 border-[#2b8ffe]/60 bg-[#032650]/95 shadow-[4px_0_12px_rgba(0,0,0,0.18)] transition-colors hover:bg-[#0b386d] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/70"
|
||||
>
|
||||
@@ -1597,7 +1713,7 @@ export default function PositionRoundtablePage({
|
||||
</button>
|
||||
)}
|
||||
|
||||
<section className="relative flex min-w-[420px] flex-1 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:shadow-none dark:backdrop-blur-md">
|
||||
<section className="relative flex min-w-[420px] flex-1 flex-col overflow-hidden rounded-[4px] border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:shadow-none dark:backdrop-blur-md">
|
||||
{!isIntelligenceDeliveryOverview && !rightPanelOpen && !sandboxOpen && (
|
||||
<TButton
|
||||
size="small"
|
||||
@@ -1620,8 +1736,8 @@ export default function PositionRoundtablePage({
|
||||
summaryDone={summaryDone}
|
||||
selectedModel={selectedModel}
|
||||
autoStartRequestKey={builtinAutoStartRequestKey}
|
||||
builtinAgentOptions={builtinAgentOptions}
|
||||
onSelectBuiltinAgent={handleSelectBuiltinStage}
|
||||
builtinAgentOptions={showClosureAgents ? builtinAgentOptions : undefined}
|
||||
onSelectBuiltinAgent={showClosureAgents ? handleSelectBuiltinStage : undefined}
|
||||
onSessionUpdate={setPositionSession}
|
||||
onConversationStart={() => setRightPanelOpen(false)}
|
||||
onPreviewArtifact={handlePreviewArtifact}
|
||||
@@ -1641,8 +1757,8 @@ export default function PositionRoundtablePage({
|
||||
agentName={activeNodeAgentName}
|
||||
positionNodeOptions={currentPositionNodeOptions}
|
||||
onSelectPositionNode={handleSelectPositionNode}
|
||||
builtinAgentOptions={builtinAgentOptions}
|
||||
onSelectBuiltinAgent={handleSelectBuiltinStage}
|
||||
builtinAgentOptions={showClosureAgents ? builtinAgentOptions : undefined}
|
||||
onSelectBuiltinAgent={showClosureAgents ? handleSelectBuiltinStage : undefined}
|
||||
onNodeUpdate={handleNodeUpdate}
|
||||
onConversationStart={() => setRightPanelOpen(false)}
|
||||
onPreviewArtifact={handlePreviewArtifact}
|
||||
@@ -1657,23 +1773,8 @@ export default function PositionRoundtablePage({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{isIntelligenceDeliveryOverview && !sandboxOpen && (
|
||||
<aside className="flex w-[390px] shrink-0 overflow-hidden">
|
||||
<BusinessChainFlow
|
||||
chain={activeChain}
|
||||
nodes={positionSession?.nodes ?? []}
|
||||
session={positionSession}
|
||||
activeNodeKey={activeNodeKey}
|
||||
onSelectIntent={openIntentFlowDetail}
|
||||
onSelectNode={openNodeFlowDetail}
|
||||
onSelectSeat={openSeatFlowDetail}
|
||||
onSelectBuiltin={handleSelectBuiltinStage}
|
||||
className="h-full w-full"
|
||||
/>
|
||||
</aside>
|
||||
)}
|
||||
{activePosition.isIntentPosition && !isIntelligenceDeliveryOverview && rightPanelOpen && !sandboxOpen && (
|
||||
<aside className="flex w-[400px] shrink-0 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:text-[#c5cfe0] dark:shadow-none dark:backdrop-blur-md">
|
||||
<aside className="flex w-[400px] shrink-0 flex-col overflow-hidden rounded-[4px] border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:text-[#c5cfe0] dark:shadow-none dark:backdrop-blur-md">
|
||||
<RightWorkspace
|
||||
selectedAgent={selectedAgent}
|
||||
selectedAgentId={selectedAgentId}
|
||||
@@ -1692,7 +1793,7 @@ export default function PositionRoundtablePage({
|
||||
onSessionUpdate={setPositionSession}
|
||||
onCollapse={() => setRightPanelOpen(false)}
|
||||
onPreviewArtifact={handlePreviewArtifact}
|
||||
onOpenBuiltinChat={handleSelectBuiltinStage}
|
||||
onOpenBuiltinChat={showClosureAgents ? handleSelectBuiltinStage : undefined}
|
||||
tab={rightTab}
|
||||
onTabChange={setRightTab}
|
||||
/>
|
||||
@@ -1730,7 +1831,7 @@ export default function PositionRoundtablePage({
|
||||
onSessionUpdate={setPositionSession}
|
||||
onCollapse={() => setRightPanelOpen(false)}
|
||||
onPreviewArtifact={handlePreviewArtifact}
|
||||
onOpenBuiltinChat={handleSelectBuiltinStage}
|
||||
onOpenBuiltinChat={showClosureAgents ? handleSelectBuiltinStage : undefined}
|
||||
tab={rightTab}
|
||||
onTabChange={setRightTab}
|
||||
/>
|
||||
@@ -1738,7 +1839,7 @@ export default function PositionRoundtablePage({
|
||||
</TDrawer>
|
||||
)}
|
||||
{sandboxOpen && sandboxArtifact && artifactsCtx && (
|
||||
<aside className="flex w-[min(48vw,720px)] min-w-[440px] shrink-0 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:shadow-none">
|
||||
<aside className="flex w-[min(48vw,720px)] min-w-[440px] shrink-0 flex-col overflow-hidden rounded-[4px] border border-slate-200 bg-white shadow-sm dark:border-[#2b8ffe]/45 dark:bg-[#032650]/80 dark:shadow-none">
|
||||
<ThreadContext.Provider value={sandboxThreadContextValue}>
|
||||
<ArtifactFileDetail
|
||||
className="min-h-0 flex-1"
|
||||
@@ -2635,9 +2736,11 @@ function DeliveryOverviewCard({ item }: { item: DeliveryOverviewItem }) {
|
||||
function HoverEditButton({
|
||||
title,
|
||||
onClick,
|
||||
revealClassName = "group-hover:pointer-events-auto group-hover:opacity-100",
|
||||
}: {
|
||||
title: string;
|
||||
onClick: () => void;
|
||||
revealClassName?: string;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
@@ -2645,13 +2748,179 @@ function HoverEditButton({
|
||||
title={title}
|
||||
aria-label={title}
|
||||
onClick={onClick}
|
||||
className="pointer-events-none flex size-7 shrink-0 items-center justify-center rounded-[4px] text-[#45c3f3] opacity-0 transition-[opacity,color,background-color] duration-150 group-hover:pointer-events-auto group-hover:opacity-100 hover:bg-[#45c3f3]/10 hover:text-[#a2e1f9] focus-visible:pointer-events-auto focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/65 motion-reduce:transition-none"
|
||||
className={cn(
|
||||
"pointer-events-none flex size-7 shrink-0 items-center justify-center rounded-[4px] text-[#45c3f3] opacity-0 transition-[opacity,color,background-color] duration-150 hover:bg-[#45c3f3]/10 hover:text-[#a2e1f9] focus-visible:pointer-events-auto focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/65 motion-reduce:transition-none",
|
||||
revealClassName,
|
||||
)}
|
||||
>
|
||||
<PencilLine className="size-3.5" strokeWidth={2.4} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function TaskAnalysisPanel({
|
||||
task,
|
||||
intent,
|
||||
taskEditable,
|
||||
intentEditable,
|
||||
onEditTask,
|
||||
onEditIntent,
|
||||
collapsed,
|
||||
onCollapsedChange,
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
task: PositionTask;
|
||||
intent: ReturnType<typeof useStep1Intent>["intentReady"];
|
||||
taskEditable?: boolean;
|
||||
intentEditable?: boolean;
|
||||
onEditTask?: () => void;
|
||||
onEditIntent?: () => void;
|
||||
collapsed: boolean;
|
||||
onCollapsedChange: (collapsed: boolean) => void;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section
|
||||
className={cn(
|
||||
"group relative flex shrink-0 flex-col overflow-hidden rounded-[4px] border border-[#ECE7E4] bg-white shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[rgba(43,143,254,0.6)] dark:bg-[#032650] dark:shadow-none",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="relative flex h-10 items-center border-b border-slate-200 !bg-[linear-gradient(89.65deg,#0062d9_0.03%,#0062d900_99.97%)] px-4 dark:border-b-[#2b8ffe]/60 dark:after:absolute dark:after:inset-x-0 dark:after:bottom-[-1px] dark:after:h-px dark:after:bg-[#45c3f3]/85 dark:after:content-['']">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-3 h-5 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||
/>
|
||||
<h2 className="text-base font-[550] tracking-wide text-white">任务分析</h2>
|
||||
<button
|
||||
type="button"
|
||||
title={collapsed ? "展开任务分析" : "收起任务分析"}
|
||||
aria-label={collapsed ? "展开任务分析" : "收起任务分析"}
|
||||
aria-expanded={!collapsed}
|
||||
onClick={() => onCollapsedChange(!collapsed)}
|
||||
className="relative z-10 ml-auto flex size-7 shrink-0 items-center justify-center rounded-[4px] transition-colors hover:bg-[#45c3f3]/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#45c3f3]/65"
|
||||
>
|
||||
<img src={expandLogo} alt="" className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"grid min-h-0 flex-1 transition-[grid-template-rows,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none",
|
||||
collapsed ? "grid-rows-[0fr] opacity-0" : "grid-rows-[1fr] opacity-100",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"min-h-0",
|
||||
children
|
||||
? "flex flex-col"
|
||||
: "custom-scrollbar overflow-y-auto",
|
||||
)}
|
||||
>
|
||||
<div className={cn("space-y-4 p-4", children && "shrink-0")}>
|
||||
<article className="group/task-details relative overflow-hidden rounded-[4px] bg-[rgba(0,98,217,0.1)] px-4 py-5">
|
||||
{taskEditable && onEditTask && (
|
||||
<div className="absolute right-2 top-2">
|
||||
<HoverEditButton
|
||||
title="编辑任务信息"
|
||||
onClick={onEditTask}
|
||||
revealClassName="group-hover/task-details:pointer-events-auto group-hover/task-details:opacity-100"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<h3
|
||||
title={desensitize(task.title)}
|
||||
className="pr-8 text-xl font-[550] leading-8 text-white"
|
||||
>
|
||||
<span className="line-clamp-1">{desensitize(task.title)}</span>
|
||||
</h3>
|
||||
<p
|
||||
title={desensitize(task.description)}
|
||||
className="mt-3 line-clamp-3 whitespace-pre-wrap text-sm leading-6 text-white"
|
||||
>
|
||||
{desensitize(task.description)}
|
||||
</p>
|
||||
<div className="mt-4 space-y-2 border-t border-[rgba(85,166,254,0.16)] pt-3 text-sm text-slate-600 dark:text-[#c5d7ee]">
|
||||
<p className="flex min-w-0 items-center gap-2">
|
||||
<FileText className="size-4 shrink-0 text-[#45c3f3]" strokeWidth={1.8} />
|
||||
<span className="shrink-0 text-slate-500 dark:text-[#9ebfe2]">任务来源:</span>
|
||||
<span className="truncate">{desensitize(task.source)}</span>
|
||||
</p>
|
||||
<p className="flex min-w-0 items-center gap-2">
|
||||
<Clock3 className="size-4 shrink-0 text-[#45c3f3]" strokeWidth={1.8} />
|
||||
<span className="shrink-0 text-slate-500 dark:text-[#9ebfe2]">下发时间:</span>
|
||||
<span className="truncate">{task.issuedAt || "未设置"}</span>
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<section className="group/intent-analysis">
|
||||
<div className="relative flex h-10 items-center border-b border-slate-200 dark:border-[#2b8ffe]/60">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-3 h-6 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||
/>
|
||||
<h3 className="text-lg font-[550] tracking-wide text-slate-900 dark:text-white">意图分析</h3>
|
||||
{intentEditable && onEditIntent && (
|
||||
<div className="ml-auto">
|
||||
<HoverEditButton
|
||||
title="编辑意图理解"
|
||||
onClick={onEditIntent}
|
||||
revealClassName="group-hover/intent-analysis:pointer-events-auto group-hover/intent-analysis:opacity-100"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{intent ? (
|
||||
<div className="space-y-3 py-4">
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">任务意图</p>
|
||||
<p className="mt-1 text-sm leading-6 text-slate-700 dark:text-[#f2f7ff]">
|
||||
{desensitize(intent.objective)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">关键约束</p>
|
||||
<ul className="mt-1 list-disc space-y-1 pl-4 text-sm leading-6 text-slate-600 marker:text-slate-400 dark:text-[#d8e6f8] dark:marker:text-[#83b6ed]">
|
||||
{intent.constraints.map((constraint, index) => (
|
||||
<li key={`${constraint}-${index}`}>{desensitize(constraint)}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{intent.assumptions && intent.assumptions.length > 0 && (
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">关键假设</p>
|
||||
<ul className="mt-1 list-disc space-y-1 pl-4 text-sm leading-6 text-slate-600 marker:text-slate-400 dark:text-[#d8e6f8] dark:marker:text-[#83b6ed]">
|
||||
{intent.assumptions.map((assumption, index) => (
|
||||
<li key={`${assumption}-${index}`}>{desensitize(assumption)}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="py-4 text-sm leading-6 text-muted-foreground dark:text-[#d8e6f8]">
|
||||
情报分析岗完成意图识别后,将在这里展示任务意图与关键约束。
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{children && (
|
||||
<div className="flex min-h-0 flex-col px-4 pb-4">
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function TaskInformationCard({
|
||||
task,
|
||||
editable,
|
||||
@@ -2667,12 +2936,12 @@ function TaskInformationCard({
|
||||
}) {
|
||||
return (
|
||||
<section className="group relative shrink-0 overflow-hidden rounded-[4px] border border-[#ECE7E4] bg-white shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[rgba(43,143,254,0.6)] dark:bg-[#032650] dark:shadow-none">
|
||||
<div className="relative flex h-10 items-center border-b border-slate-200 bg-primary/10 px-4 dark:border-b-[#2b8ffe]/60 dark:bg-[linear-gradient(89.65deg,#0062d9_0.03%,rgba(0,98,217,0.78)_52%,rgba(0,98,217,0)_100%)] dark:after:absolute dark:after:inset-x-0 dark:after:bottom-[-1px] dark:after:h-px dark:after:bg-[#45c3f3]/85 dark:after:content-['']">
|
||||
<div className="relative flex h-10 items-center border-b border-slate-200 !bg-[linear-gradient(89.65deg,#0062d9_0.03%,#0062d900_99.97%)] px-4 dark:border-b-[#2b8ffe]/60 dark:after:absolute dark:after:inset-x-0 dark:after:bottom-[-1px] dark:after:h-px dark:after:bg-[#45c3f3]/85 dark:after:content-['']">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-3 h-5 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||
/>
|
||||
<h2 className="text-base font-semibold tracking-wide text-slate-900 dark:text-white">任务信息</h2>
|
||||
<h2 className="text-base font-[550] tracking-wide text-white">任务信息</h2>
|
||||
<div className="relative z-10 ml-auto flex items-center gap-2">
|
||||
{editable && onEdit && <HoverEditButton title="编辑任务信息" onClick={onEdit} />}
|
||||
<button
|
||||
@@ -2764,6 +3033,24 @@ function TaskInformationEditDialog({
|
||||
placeholder="请输入任务描述"
|
||||
/>
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<label className="block space-y-1.5">
|
||||
<span className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">任务来源</span>
|
||||
<TInput
|
||||
value={task.source}
|
||||
onChange={(value) => onChange({ ...task, source: String(value) })}
|
||||
placeholder="例如:本系统创建"
|
||||
/>
|
||||
</label>
|
||||
<label className="block space-y-1.5">
|
||||
<span className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">下发时间</span>
|
||||
<TInput
|
||||
value={task.issuedAt}
|
||||
onChange={(value) => onChange({ ...task, issuedAt: String(value) })}
|
||||
placeholder="例如:2026-07-22 17:42:00"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label className="block space-y-1.5">
|
||||
<span className="text-xs font-semibold text-slate-500 dark:text-[#9ebfe2]">任务方向</span>
|
||||
<TInput
|
||||
@@ -2792,46 +3079,31 @@ function BusinessChainSelector({
|
||||
value,
|
||||
onChange,
|
||||
onConfigure,
|
||||
className,
|
||||
}: {
|
||||
chains: BusinessChain[];
|
||||
loading: boolean;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onConfigure: (chainId: string) => void;
|
||||
className?: string;
|
||||
}) {
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
const [relationOpenId, setRelationOpenId] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<section className="rounded-2xl border border-[#ECE7E4] bg-white p-4 shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[#2b8ffe]/30 dark:bg-[#042b59]/55 dark:shadow-none">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex size-5 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
||||
<GitBranch className="size-3" strokeWidth={2.5} />
|
||||
</span>
|
||||
<h2 className="text-sm font-semibold tracking-wide text-slate-900 dark:text-[#e8edf5]">
|
||||
业务链
|
||||
</h2>
|
||||
<span className="ml-auto flex size-7 items-center justify-center rounded-full bg-neutral-100 text-sm font-bold tabular-nums text-neutral-500 dark:bg-[#0b386d] dark:text-[#83c1ff]">
|
||||
<section className={cn("group/business-chain flex min-h-0 flex-col", className)}>
|
||||
<div className="relative flex h-10 items-center border-b border-slate-200 dark:border-[#2b8ffe]/60">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-3 h-6 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||
/>
|
||||
<h3 className="text-lg font-[550] tracking-wide text-slate-900 dark:text-white">业务链</h3>
|
||||
<span className="ml-auto flex size-6 items-center justify-center rounded-[4px] bg-slate-100 text-xs font-semibold tabular-nums text-slate-500 dark:bg-[#0b386d] dark:text-[#83c1ff]">
|
||||
{chains.length}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
title={expanded ? "收起业务链" : "展开业务链"}
|
||||
aria-expanded={expanded}
|
||||
onClick={() => setExpanded((current) => !current)}
|
||||
className="flex size-7 items-center justify-center rounded-full border border-[#E6C9C4] bg-white text-neutral-400 transition-colors hover:border-primary/40 hover:text-primary dark:border-[#2b8ffe]/60 dark:bg-[#042b59] dark:text-[#83c1ff]"
|
||||
>
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"size-3.5 transition-transform duration-300",
|
||||
expanded && "rotate-180",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{expanded && (
|
||||
<div className="mt-3 max-h-[340px] space-y-2.5 overflow-y-auto pr-1 custom-scrollbar">
|
||||
<div className="business-chain-scrollbar min-h-0 flex-1 space-y-2.5 overflow-y-auto py-4 pr-1 custom-scrollbar">
|
||||
{loading ? (
|
||||
<div className="flex h-20 items-center justify-center gap-2 text-xs text-slate-400 dark:text-[#8aa0c4]">
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
@@ -2850,7 +3122,7 @@ function BusinessChainSelector({
|
||||
<div
|
||||
key={chain.id}
|
||||
className={cn(
|
||||
"overflow-hidden rounded-xl border transition-all duration-200",
|
||||
"overflow-hidden rounded-[4px] border transition-all duration-200",
|
||||
selected
|
||||
? "border-primary/30 bg-primary/5 ring-1 ring-primary/20 shadow-sm dark:border-[#2b8ffe] dark:bg-[#0b4fae]/35 dark:ring-[#2b8ffe]/30"
|
||||
: "border-[#ECE7E4] bg-neutral-50/50 hover:border-neutral-300 dark:border-[#2b8ffe]/25 dark:bg-[#032650]/45 dark:hover:border-[#2b8ffe]/60",
|
||||
@@ -2918,7 +3190,7 @@ function BusinessChainSelector({
|
||||
current === chain.id ? null : chain.id,
|
||||
)
|
||||
}
|
||||
className="flex items-center justify-center gap-1 rounded-full border border-primary/15 bg-primary/5 px-3 py-1 text-[10.5px] font-semibold text-primary transition-colors hover:bg-primary/10 dark:border-[#2b8ffe]/40 dark:bg-[#2b8ffe]/10 dark:text-[#83c1ff]"
|
||||
className="flex items-center justify-center gap-1 rounded-[4px] border border-primary/15 bg-primary/5 px-3 py-1 text-[10.5px] font-semibold text-primary transition-colors hover:bg-primary/10 dark:border-[#2b8ffe]/40 dark:bg-[#2b8ffe]/10 dark:text-[#83c1ff]"
|
||||
>
|
||||
<Layers3 className="size-3" />
|
||||
<span>{relationOpen ? "收起关系图" : "展开关系图"}</span>
|
||||
@@ -2945,7 +3217,7 @@ function BusinessChainSelector({
|
||||
</p>
|
||||
)}
|
||||
{relationOpen && (
|
||||
<div className="mt-2 flex flex-wrap items-center justify-center gap-1 rounded-xl border border-dashed border-[#E8D0CC]/70 bg-[#FAF6F5] p-2.5 dark:border-[#2b8ffe]/30 dark:bg-[#032650]/75">
|
||||
<div className="mt-2 flex flex-wrap items-center justify-center gap-1 rounded-[4px] border border-dashed border-[#E8D0CC]/70 bg-[#FAF6F5] p-2.5 dark:border-[#2b8ffe]/30 dark:bg-[#032650]/75">
|
||||
{stageGroups.flatMap((stage, stageIndex) => {
|
||||
const content = stage.seats.map((seat) => {
|
||||
const avatarType = getAvatarTypeFor(
|
||||
@@ -2980,8 +3252,7 @@ function BusinessChainSelector({
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -3006,12 +3277,12 @@ function IntentUnderstandingCard({
|
||||
collapsed ? "flex-none" : "flex-1",
|
||||
)}
|
||||
>
|
||||
<div className="relative flex h-10 shrink-0 items-center border-b border-slate-200 bg-primary/10 px-4 dark:border-b-[#2b8ffe]/60 dark:bg-[linear-gradient(89.65deg,#0062d9_0.03%,rgba(0,98,217,0.78)_52%,rgba(0,98,217,0)_100%)] dark:after:absolute dark:after:inset-x-0 dark:after:bottom-[-1px] dark:after:h-px dark:after:bg-[#45c3f3]/85 dark:after:content-['']">
|
||||
<div className="relative flex h-10 shrink-0 items-center border-b border-slate-200 !bg-[linear-gradient(89.65deg,#0062d9_0.03%,#0062d900_99.97%)] px-4 dark:border-b-[#2b8ffe]/60 dark:after:absolute dark:after:inset-x-0 dark:after:bottom-[-1px] dark:after:h-px dark:after:bg-[#45c3f3]/85 dark:after:content-['']">
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-3 h-5 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||
/>
|
||||
<h2 className="text-base font-semibold tracking-wide text-slate-900 dark:text-white">意图理解</h2>
|
||||
<h2 className="text-base font-[550] tracking-wide text-white">意图理解</h2>
|
||||
<div className="relative z-10 ml-auto flex items-center gap-2">
|
||||
{editable && onEdit && <HoverEditButton title="编辑意图理解" onClick={onEdit} />}
|
||||
<button
|
||||
@@ -3171,6 +3442,7 @@ function BusinessChainFlow({
|
||||
onSelectSeat,
|
||||
onSelectBuiltin,
|
||||
className,
|
||||
embedded = false,
|
||||
}: {
|
||||
chain: BusinessChain | null;
|
||||
nodes: PositionRoundtableNode[];
|
||||
@@ -3181,22 +3453,36 @@ function BusinessChainFlow({
|
||||
onSelectSeat?: (seat: BusinessChain["seats"][number], stageIndex: number, seatIndex: number) => void;
|
||||
onSelectBuiltin: (kind: "summary" | "action_plan") => void;
|
||||
className?: string;
|
||||
embedded?: boolean;
|
||||
}) {
|
||||
const [flowDialogOpen, setFlowDialogOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<section
|
||||
className={cn(
|
||||
"flex h-[540px] shrink-0 flex-col overflow-hidden rounded-2xl border border-[#ECE7E4] bg-white p-4 shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[#2b8ffe]/30 dark:bg-[#042b59]/55 dark:shadow-none",
|
||||
"flex h-[540px] shrink-0 flex-col overflow-hidden rounded-[4px] border border-[#ECE7E4] bg-white p-4 shadow-[0_4px_20px_rgba(0,0,0,0.02)] dark:border-[#2b8ffe]/30 dark:bg-[#042b59]/55 dark:shadow-none",
|
||||
embedded && "h-[540px] rounded-none border-0 bg-transparent p-0 shadow-none",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="mb-3 flex items-center justify-between gap-2">
|
||||
<div
|
||||
className={cn(
|
||||
"mb-3 flex items-center justify-between gap-2",
|
||||
embedded && "relative mb-4 h-10 shrink-0 border-b border-slate-200 dark:border-[#2b8ffe]/60",
|
||||
)}
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span className="flex size-5 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
||||
<Layers3 className="size-3" strokeWidth={2.5} />
|
||||
</span>
|
||||
<h2 className="truncate text-sm font-semibold tracking-wide text-slate-900 dark:text-[#e8edf5]">
|
||||
{embedded ? (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="h-6 w-1 shrink-0 rounded-l-[2px] rounded-br-[2px] bg-[linear-gradient(167.47deg,#a2e1f9_0%,#45c3f3_100%)]"
|
||||
/>
|
||||
) : (
|
||||
<span className="flex size-5 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
||||
<Layers3 className="size-3" strokeWidth={2.5} />
|
||||
</span>
|
||||
)}
|
||||
<h2 className={cn("truncate font-semibold tracking-wide text-slate-900 dark:text-[#e8edf5]", embedded ? "text-lg font-[550] dark:text-white" : "text-sm")}>
|
||||
业务链条
|
||||
</h2>
|
||||
</div>
|
||||
@@ -3359,7 +3645,7 @@ function ChainFlowCanvas({
|
||||
onPointerCancel={onPanEnd}
|
||||
onDoubleClick={() => setView({ tx: 0, ty: 0 })}
|
||||
className={cn(
|
||||
"overflow-hidden rounded-xl border border-slate-100 bg-slate-50/70 touch-none dark:border-[#2b8ffe]/25 dark:bg-[#032650]/75",
|
||||
"overflow-hidden rounded-[4px] border border-slate-100 bg-slate-50/70 touch-none dark:border-[#2b8ffe]/25 dark:bg-[#032650]/75",
|
||||
panning ? "cursor-grabbing" : "cursor-grab",
|
||||
className,
|
||||
)}
|
||||
@@ -3639,7 +3925,7 @@ function RightWorkspace({
|
||||
onSessionUpdate: (session: PositionRoundtableSession) => void;
|
||||
onCollapse: () => void;
|
||||
onPreviewArtifact: (artifact: PositionArtifactPreview) => void;
|
||||
onOpenBuiltinChat: (kind: ClosureStageKind) => void;
|
||||
onOpenBuiltinChat?: (kind: ClosureStageKind) => void;
|
||||
tab: WorkspaceTab;
|
||||
onTabChange: (tab: WorkspaceTab) => void;
|
||||
}) {
|
||||
@@ -4116,7 +4402,7 @@ function AnalysisResults({
|
||||
session: PositionRoundtableSession | null;
|
||||
onSessionUpdate: (session: PositionRoundtableSession) => void;
|
||||
onPreviewArtifact: (artifact: PositionArtifactPreview) => void;
|
||||
onOpenBuiltinChat: (kind: ClosureStageKind) => void;
|
||||
onOpenBuiltinChat?: (kind: ClosureStageKind) => void;
|
||||
}) {
|
||||
const [positionFilter, setPositionFilter] = useState("all");
|
||||
const [nodeFilter, setNodeFilter] = useState("all");
|
||||
@@ -4154,6 +4440,10 @@ function AnalysisResults({
|
||||
const seat = seatsByAgentId.get(node.agentId);
|
||||
const agentName = seat?.name ?? node.agentId;
|
||||
const artifactCount = node.artifactManifest.length;
|
||||
const previewableArtifacts = node.artifactManifest.filter(
|
||||
(artifact) => typeof artifact.path === "string" && artifact.path.trim().length > 0,
|
||||
);
|
||||
const hasPreviewableArtifact = previewableArtifacts.length > 0;
|
||||
const productName =
|
||||
node.status === "done"
|
||||
? artifactCount > 0
|
||||
@@ -4179,18 +4469,20 @@ function AnalysisResults({
|
||||
: node.status === "rejected"
|
||||
? "该产物已被驳回,请重新问答并交付。"
|
||||
: "点击左侧流程图或右侧智能体发起问答。");
|
||||
items.push({
|
||||
id: `${node.nodeKey}:delivery:${node.revision}`,
|
||||
kind: "delivery",
|
||||
node,
|
||||
agentName,
|
||||
productName,
|
||||
summary,
|
||||
positionId: node.positionId,
|
||||
updatedAt: node.updatedAt,
|
||||
status: node.status,
|
||||
});
|
||||
node.artifactManifest.forEach((artifact, index) => {
|
||||
if (!hasPreviewableArtifact) {
|
||||
items.push({
|
||||
id: `${node.nodeKey}:delivery:${node.revision}`,
|
||||
kind: "delivery",
|
||||
node,
|
||||
agentName,
|
||||
productName,
|
||||
summary,
|
||||
positionId: node.positionId,
|
||||
updatedAt: node.updatedAt,
|
||||
status: node.status,
|
||||
});
|
||||
}
|
||||
previewableArtifacts.forEach((artifact, index) => {
|
||||
const path = typeof artifact.path === "string" ? artifact.path : "";
|
||||
const name =
|
||||
typeof artifact.name === "string" && artifact.name.trim()
|
||||
@@ -4401,7 +4693,7 @@ function AnalysisResults({
|
||||
onChange={(value) => setKindFilter(String(value) as "all" | ResultKind)}
|
||||
/>
|
||||
</div>
|
||||
{nodes.length > 0 && (
|
||||
{onOpenBuiltinChat && nodes.length > 0 && (
|
||||
<div className="rounded-xl border border-slate-200 bg-slate-50/70 p-3 dark:border-[#2b8ffe]/30 dark:bg-[#032650]/65">
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<div className="min-w-0">
|
||||
|
||||
@@ -599,6 +599,23 @@
|
||||
padding-box;
|
||||
}
|
||||
|
||||
/* 岗位会商左侧业务链:列表较窄,避免滚动条抢占卡片内容宽度。 */
|
||||
.business-chain-scrollbar {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #2b8ffe transparent;
|
||||
}
|
||||
|
||||
.business-chain-scrollbar::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.business-chain-scrollbar::-webkit-scrollbar-thumb {
|
||||
border: 0;
|
||||
background: linear-gradient(180deg, rgb(85 166 254 / 0.82), rgb(0 98 217 / 0.9))
|
||||
padding-box;
|
||||
}
|
||||
|
||||
.workspace-shell {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user