Files
Agent-System/web/src/components/layout/WorkbenchView.tsx
T
weichen 3d6dcc3ae5 feat(web): 用小队子页面展示思考与工具过程
气泡只聚合过程摘要并链到 hash 子页面;成员编制来自只读 squad 端点,思考与工具默认折叠。
2026-09-28 10:30:53 +00:00

303 lines
11 KiB
TypeScript

import type { Dispatch, ReactNode, SetStateAction } from 'react';
import { AppShell } from './AppShell';
import { UserProjectRightsModal } from '../user/UserProjectRightsModal';
import { UserSettingsModal } from '../user/UserSettingsModal';
import { DocVisibilityModal } from '../doc/DocVisibilityModal';
import { CreateProjectModal, type CreateProjectConfirmData } from '../project/CreateProjectModal';
import { ProjectMembersModal } from '../project/ProjectMembersModal';
import { ProjectSettingsModal } from '../project/ProjectSettingsModal';
import { FirstLoginPasswordModal } from '../auth/FirstLoginPasswordModal';
import { authClient } from '../../api/auth';
import type { LoginResult } from '../../types/auth';
import type { DocPermissionRole, UserProfile, UserRole } from '../../types/docgov';
import type { DocVisibilityRule, RoleDefinition } from '../../types/rbac';
import type { CreateProjectSubmitPhase } from '../../lib/createProjectSubmitGuard';
import type {
DeliverableCategory,
DeliverableItem,
ProjectInfo,
ProjectMember,
ProjectMemberRole,
ProjectSession,
} from '../../types/workbench';
export type WorkbenchWorkflowState = {
activeKey: string;
version: number;
creatorId: number;
lifecycleStatus: string;
};
export interface WorkbenchViewProps {
visibleSessions: ProjectSession[];
activeSessionId: string;
sessions: ProjectSession[];
deliverables: DeliverableItem[];
currentProjectId: number;
setActiveSessionId: (id: string) => void;
setActiveDocId: (id: string) => void;
setActiveCategory: (category: DeliverableCategory) => void;
setCreateProjectModalOpen: (open: boolean) => void;
canCreateProject: boolean;
currentUser: UserProfile;
currentDocRole: UserRole;
setRightsModalOpen: Dispatch<SetStateAction<boolean>>;
setUserSettingsOpen: Dispatch<SetStateAction<boolean>>;
setView: (view: 'workbench' | 'admin' | 'subagents') => void;
setProjectMembersModalOpen: Dispatch<SetStateAction<boolean>>;
currentWfState: WorkbenchWorkflowState;
workflowGovernanceEnabled: boolean;
setProjectSettingsOpen: Dispatch<SetStateAction<boolean>>;
setProjectWorkflowsState: Dispatch<SetStateAction<Record<number, WorkbenchWorkflowState>>>;
workflowBanner: ReactNode;
chatContent: ReactNode;
docContent: ReactNode;
rightsModalOpen: boolean;
visibleDeliverables: DeliverableItem[];
activeDocId: string;
canAssignLead: boolean;
handleSelectDoc: (id: string) => void;
handleRoleChangeForDoc: (docId: string, role: DocPermissionRole) => void | Promise<void>;
session: LoginResult | null;
setSession: Dispatch<SetStateAction<LoginResult | null>>;
handleLogout: () => void;
userSettingsOpen: boolean;
roles: RoleDefinition[];
setAllUsers: Dispatch<SetStateAction<UserProfile[]>>;
setCurrentUser: Dispatch<SetStateAction<UserProfile>>;
docVisibilityModalOpen: boolean;
setDocVisibilityModalOpen: Dispatch<SetStateAction<boolean>>;
targetDocForVisibility: DeliverableItem | null;
handleSaveDocVisibilityRule: (docId: string, rule: DocVisibilityRule | undefined) => void;
createProjectModalOpen: boolean;
createProjectSubmitting: boolean;
setCreateProjectError: Dispatch<SetStateAction<string | null>>;
handleCreateProjectConfirm: (data: CreateProjectConfirmData) => void | Promise<void>;
createProjectError: string | null;
createProjectSubmitPhase: CreateProjectSubmitPhase | null;
allUsers: UserProfile[];
projectMembersModalOpen: boolean;
currentProject: ProjectInfo | undefined;
canManageProjectMembers: boolean;
projectMembersError: string | null;
handleAddProjectMember: (member: Omit<ProjectMember, 'joinedAt' | 'status'>) => void | Promise<void>;
handleUpdateProjectMemberRole: (userId: number, role: ProjectMemberRole) => void | Promise<void>;
handleRemoveProjectMember: (userId: number) => void | Promise<void>;
handleRestoreProjectMember: (userId: number) => void | Promise<void>;
projectSettingsOpen: boolean;
currentActiveSession: ProjectSession | undefined;
}
export function WorkbenchView({
visibleSessions,
activeSessionId,
sessions,
deliverables,
currentProjectId,
setActiveSessionId,
setActiveDocId,
setActiveCategory,
setCreateProjectModalOpen,
canCreateProject,
currentUser,
currentDocRole,
setRightsModalOpen,
setUserSettingsOpen,
setView,
setProjectMembersModalOpen,
currentWfState,
workflowGovernanceEnabled,
setProjectSettingsOpen,
setProjectWorkflowsState,
workflowBanner,
chatContent,
docContent,
rightsModalOpen,
visibleDeliverables,
activeDocId,
canAssignLead,
handleSelectDoc,
handleRoleChangeForDoc,
session,
setSession,
handleLogout,
userSettingsOpen,
roles,
setAllUsers,
setCurrentUser,
docVisibilityModalOpen,
setDocVisibilityModalOpen,
targetDocForVisibility,
handleSaveDocVisibilityRule,
createProjectModalOpen,
createProjectSubmitting,
setCreateProjectError,
handleCreateProjectConfirm,
createProjectError,
createProjectSubmitPhase,
allUsers,
projectMembersModalOpen,
currentProject,
canManageProjectMembers,
projectMembersError,
handleAddProjectMember,
handleUpdateProjectMemberRole,
handleRemoveProjectMember,
handleRestoreProjectMember,
projectSettingsOpen,
currentActiveSession,
}: WorkbenchViewProps) {
return (
<>
<AppShell
sessions={visibleSessions}
activeSessionId={activeSessionId}
onSelectSession={(sessId) => {
if (window.location.hash) {
history.replaceState(null, '', window.location.pathname + window.location.search);
}
setActiveSessionId(sessId);
const targetSess = sessions.find((s) => s.sessionId === sessId);
if (targetSess && targetSess.projectId !== currentProjectId) {
const targetProjDocs = deliverables.filter((d) => d.projectId === targetSess.projectId);
if (targetProjDocs.length > 0) {
setActiveDocId(targetProjDocs[0].docId);
setActiveCategory(targetProjDocs[0].category);
}
}
}}
onNewSession={() => setCreateProjectModalOpen(true)}
canCreateProject={canCreateProject}
currentUser={currentUser}
currentRole={currentDocRole}
currentUserName={currentUser.name}
onChangeRole={(_, __) => {}}
onOpenRightsModal={() => setRightsModalOpen(true)}
onOpenUserSettings={() => setUserSettingsOpen(true)}
onOpenAdminSettings={() => setView('admin')}
onOpenProjectMembers={() => setProjectMembersModalOpen(true)}
activeWorkflowKey={currentWfState.activeKey}
workflowVersion={currentWfState.version}
lifecycleStatus={currentWfState.lifecycleStatus}
isCreator={currentWfState.creatorId === currentUser.id}
governanceEnabled={workflowGovernanceEnabled}
onOpenProjectSettings={() => setProjectSettingsOpen(true)}
onWorkflowChanged={(newKey, newVer) => {
setProjectWorkflowsState((prev) => ({
...prev,
[currentProjectId]: {
...currentWfState,
activeKey: newKey,
version: newVer,
lifecycleStatus: 'active',
},
}));
}}
workflowBanner={workflowBanner}
chatContent={chatContent}
docContent={docContent}
/>
<UserProjectRightsModal
isOpen={rightsModalOpen}
onClose={() => setRightsModalOpen(false)}
currentUser={currentUser}
currentProjectTitle={sessions.find((s) => s.sessionId === activeSessionId)?.title || '当前项目'}
deliverables={visibleDeliverables}
activeDocId={activeDocId}
canAssignLead={canAssignLead}
onSelectDoc={handleSelectDoc}
onRoleChangeForDoc={handleRoleChangeForDoc}
/>
<FirstLoginPasswordModal
isOpen={!!session?.mustChangePassword}
user={currentUser}
onPasswordChanged={() => {
if (!session) return;
const updated = {
...session,
mustChangePassword: false,
user: { ...session.user, mustChangePassword: false },
};
setSession(updated);
authClient.saveSession(updated);
}}
onLogout={handleLogout}
/>
<UserSettingsModal
isOpen={userSettingsOpen}
onClose={() => setUserSettingsOpen(false)}
currentUser={currentUser}
roles={roles}
onUpdateProfile={(updatedProfile) => {
setAllUsers((prev) =>
prev.map((u) => (u.id === currentUser.id ? { ...u, ...updatedProfile } : u))
);
setCurrentUser((prev) => ({ ...prev, ...updatedProfile }));
}}
onLogout={handleLogout}
/>
<DocVisibilityModal
isOpen={docVisibilityModalOpen}
onClose={() => setDocVisibilityModalOpen(false)}
doc={targetDocForVisibility}
roles={roles}
currentUserName={currentUser.name}
onSaveRule={handleSaveDocVisibilityRule}
/>
<CreateProjectModal
isOpen={createProjectModalOpen}
onClose={() => {
if (createProjectSubmitting) return;
setCreateProjectModalOpen(false);
setCreateProjectError(null);
}}
onSubmit={handleCreateProjectConfirm}
submitError={createProjectError}
submitting={createProjectSubmitting}
submitPhase={createProjectSubmitPhase}
currentUser={currentUser}
availableUsers={allUsers}
/>
<ProjectMembersModal
isOpen={projectMembersModalOpen}
onClose={() => setProjectMembersModalOpen(false)}
project={currentProject}
currentUser={currentUser}
availableUsers={allUsers}
canManage={canManageProjectMembers}
membersError={projectMembersError}
onAddMember={handleAddProjectMember}
onUpdateMemberRole={handleUpdateProjectMemberRole}
onRemoveMember={handleRemoveProjectMember}
onRestoreMember={handleRestoreProjectMember}
/>
<ProjectSettingsModal
open={projectSettingsOpen}
projectId={currentProjectId}
projectName={currentActiveSession?.title || '当前项目'}
canAssignLead={canAssignLead}
members={(currentProject?.members || [])
.filter((m) => m.status === 'active')
.map((m) => ({
userId: m.userId,
name:
allUsers.find((u) => u.id === m.userId)?.name ||
m.userName ||
`用户 ${m.userId}`,
}))}
onClose={() => setProjectSettingsOpen(false)}
onActivated={(activeKey) => {
setProjectWorkflowsState((prev) => ({
...prev,
[currentProjectId]: {
...currentWfState,
activeKey,
lifecycleStatus: 'active',
},
}));
}}
/>
</>
);
}