fix(web): allow workbench without projects

This commit is contained in:
2026-07-24 10:33:46 +08:00
parent 9900235eaa
commit fd8a2110a1
4 changed files with 20 additions and 15 deletions

View File

@@ -123,7 +123,7 @@ function App() {
}, [session, workspaces]) }, [session, workspaces])
const dark = theme === 'dark' const dark = theme === 'dark'
const activeWorkspace = workspaces.find((workspace) => workspace.project.id === activeProjectID) ?? workspaces[0] const activeWorkspace = workspaces.find((workspace) => workspace.project.id === activeProjectID)
const activeTagOptions = activeWorkspace?.tags.filter((tag) => tag !== 'all' && tag !== '全部') ?? [] const activeTagOptions = activeWorkspace?.tags.filter((tag) => tag !== 'all' && tag !== '全部') ?? []
function selectActiveProject(projectID: string) { function selectActiveProject(projectID: string) {
@@ -142,7 +142,7 @@ function App() {
const requestedProjectID = latestProjectID !== projectIDWhenStarted const requestedProjectID = latestProjectID !== projectIDWhenStarted
? latestProjectID ? latestProjectID
: preferredProjectID ?? latestProjectID : preferredProjectID ?? latestProjectID
const nextProjectID = backendWorkspaces.find((workspace) => workspace.project.id === requestedProjectID)?.project.id ?? backendWorkspaces[0]?.project.id ?? '' const nextProjectID = backendWorkspaces.find((workspace) => workspace.project.id === requestedProjectID)?.project.id ?? ''
selectActiveProject(nextProjectID) selectActiveProject(nextProjectID)
return backendWorkspaces return backendWorkspaces
} }
@@ -152,9 +152,9 @@ function App() {
try { try {
setApiBaseUrl(input.server) setApiBaseUrl(input.server)
const nextSession = await login(input.email, input.password) const nextSession = await login(input.email, input.password)
const backendWorkspaces = await loadWorkspaces(nextSession, '') await loadWorkspaces(nextSession, '')
setSession(nextSession) setSession(nextSession)
selectActiveProject(backendWorkspaces[0]?.project.id ?? '') selectActiveProject('')
setActiveChannel('overview') setActiveChannel('overview')
setActiveView('workspace') setActiveView('workspace')
setScreen('workbench') setScreen('workbench')
@@ -385,7 +385,7 @@ function App() {
<Spin loading={loading} style={{ width: '100%' }}> <Spin loading={loading} style={{ width: '100%' }}>
<LoginPage onLogin={handleLogin} /> <LoginPage onLogin={handleLogin} />
</Spin> </Spin>
) : activeWorkspace && session ? ( ) : session ? (
<ProjectPage <ProjectPage
activeView={activeView} activeView={activeView}
activeWorkspace={activeWorkspace} activeWorkspace={activeWorkspace}

View File

@@ -14,7 +14,7 @@ export function ProjectRail({
onSelectProject, onSelectProject,
onCreateProject, onCreateProject,
}: { }: {
activeProject: Project activeProject?: Project
projects: Project[] projects: Project[]
activeView: WorkbenchView activeView: WorkbenchView
onSelectWorkspace: () => void onSelectWorkspace: () => void
@@ -42,7 +42,7 @@ export function ProjectRail({
{projects.map((project) => ( {projects.map((project) => (
<Badge key={project.id} count={project.badge} dot={false} className={project.urgent ? 'project-badge urgent' : 'project-badge'}> <Badge key={project.id} count={project.badge} dot={false} className={project.urgent ? 'project-badge urgent' : 'project-badge'}>
<button <button
className={activeView === 'project' && activeProject.id === project.id ? 'project-button active' : 'project-button'} className={activeView === 'project' && activeProject?.id === project.id ? 'project-button active' : 'project-button'}
onClick={() => onSelectProject(project)} onClick={() => onSelectProject(project)}
style={{ '--project-color': project.color } as CSSProperties} style={{ '--project-color': project.color } as CSSProperties}
title={project.name} title={project.name}

View File

@@ -452,9 +452,11 @@ export function WorkspaceExplorePage({
onClick={() => void updateSelected({ status: selected.status, starred: !selected.starred })} onClick={() => void updateSelected({ status: selected.status, starred: !selected.starred })}
/> />
<Button <Button
aria-label={`沉淀到项目${activeProjectName}`} aria-label={activeProjectName ? `沉淀到项目${activeProjectName}` : '请先选择项目'}
title={activeProjectName ? undefined : '请先选择项目'}
type="text" type="text"
icon={<IconBook />} icon={<IconBook />}
disabled={!activeProjectID}
loading={depositingItemID === selected.id} loading={depositingItemID === selected.id}
onClick={() => void depositSelected()} onClick={() => void depositSelected()}
/> />

View File

@@ -67,7 +67,7 @@ export function ProjectPage({
onDepositDatasetItem, onDepositDatasetItem,
}: { }: {
activeView: WorkbenchView activeView: WorkbenchView
activeWorkspace: ProjectWorkspace activeWorkspace?: ProjectWorkspace
workspaces: ProjectWorkspace[] workspaces: ProjectWorkspace[]
activeChannel: ChannelKey activeChannel: ChannelKey
activeTaskID: string | null activeTaskID: string | null
@@ -108,9 +108,10 @@ export function ProjectPage({
onSyncDatasetSources: () => Promise<DatasetSyncResultDTO[]> onSyncDatasetSources: () => Promise<DatasetSyncResultDTO[]>
onDepositDatasetItem: (itemId: string, projectId: string) => Promise<DatasetDepositDTO & { refreshFailed?: boolean }> onDepositDatasetItem: (itemId: string, projectId: string) => Promise<DatasetDepositDTO & { refreshFailed?: boolean }>
}) { }) {
const isProject = activeView === 'project' const isProject = activeView === 'project' && Boolean(activeWorkspace)
const projects = workspaces.map((workspace) => workspace.project) const projects = workspaces.map((workspace) => workspace.project)
const updateActiveProjectTask = (update: ProjectTaskUpdate) => { const updateActiveProjectTask = (update: ProjectTaskUpdate) => {
if (!activeWorkspace) return
onUpdateWorkspaceTask({ onUpdateWorkspaceTask({
originalProjectId: activeWorkspace.project.id, originalProjectId: activeWorkspace.project.id,
nextProjectId: activeWorkspace.project.id, nextProjectId: activeWorkspace.project.id,
@@ -138,7 +139,7 @@ export function ProjectPage({
<Layout className="workbench-main" hasSider> <Layout className="workbench-main" hasSider>
<ProjectRail <ProjectRail
activeProject={activeWorkspace.project} activeProject={activeWorkspace?.project}
projects={projects} projects={projects}
activeView={activeView} activeView={activeView}
onSelectWorkspace={onSelectWorkspace} onSelectWorkspace={onSelectWorkspace}
@@ -147,7 +148,7 @@ export function ProjectPage({
onCreateProject={onCreateProject} onCreateProject={onCreateProject}
/> />
{isProject && ( {isProject && activeWorkspace && (
<ProjectSidebar <ProjectSidebar
activeView={activeView} activeView={activeView}
activeWorkspace={activeWorkspace} activeWorkspace={activeWorkspace}
@@ -163,8 +164,8 @@ export function ProjectPage({
<WorkspacePage workspaces={workspaces} onOpenTask={onOpenTask} onUpdateTask={onUpdateWorkspaceTask} /> <WorkspacePage workspaces={workspaces} onOpenTask={onOpenTask} onUpdateTask={onUpdateWorkspaceTask} />
) : activeView === 'workspace-explore' ? ( ) : activeView === 'workspace-explore' ? (
<WorkspaceExplorePage <WorkspaceExplorePage
activeProjectID={activeWorkspace.project.id} activeProjectID={activeWorkspace?.project.id ?? ''}
activeProjectName={activeWorkspace.project.name} activeProjectName={activeWorkspace?.project.name ?? ''}
onListSources={onListDatasetSources} onListSources={onListDatasetSources}
onListItems={onListDatasetItems} onListItems={onListDatasetItems}
onCreateSource={onCreateDatasetSource} onCreateSource={onCreateDatasetSource}
@@ -174,7 +175,7 @@ export function ProjectPage({
onSyncSources={onSyncDatasetSources} onSyncSources={onSyncDatasetSources}
onDepositItem={onDepositDatasetItem} onDepositItem={onDepositDatasetItem}
/> />
) : ( ) : activeWorkspace ? (
<ProjectChannelPage <ProjectChannelPage
activeChannel={activeChannel} activeChannel={activeChannel}
activeWorkspace={activeWorkspace} activeWorkspace={activeWorkspace}
@@ -193,6 +194,8 @@ export function ProjectPage({
onListAIExperts={onListAIExperts} onListAIExperts={onListAIExperts}
onCreateAISession={onCreateAISession} onCreateAISession={onCreateAISession}
/> />
) : (
<WorkspacePage workspaces={workspaces} onOpenTask={onOpenTask} onUpdateTask={onUpdateWorkspaceTask} />
)} )}
</Content> </Content>