Add workspace landing page
This commit is contained in:
@@ -1,18 +1,45 @@
|
||||
import { Avatar, Button, Divider, Input, Layout, Message, Space, Tabs, Typography } from '@arco-design/web-react'
|
||||
import { IconLink, IconMessage, IconMore } from '@arco-design/web-react/icon'
|
||||
import { channels, discussions } from './project-data'
|
||||
import type { ChannelKey, Project } from './project-types'
|
||||
import type { ChannelKey, Project, WorkbenchView } from './project-types'
|
||||
|
||||
const { Sider } = Layout
|
||||
const { Title, Text, Paragraph } = Typography
|
||||
|
||||
export function ProjectInspector({ activeProject, activeChannel, selectedItem }: { activeProject: Project; activeChannel: ChannelKey; selectedItem: string }) {
|
||||
export function ProjectInspector({
|
||||
activeView,
|
||||
activeProject,
|
||||
activeChannel,
|
||||
selectedItem,
|
||||
}: {
|
||||
activeView: WorkbenchView
|
||||
activeProject: Project
|
||||
activeChannel: ChannelKey
|
||||
selectedItem: string
|
||||
}) {
|
||||
const workspaceMode = activeView === 'workspace'
|
||||
|
||||
return (
|
||||
<Sider className="inspector" width={360}>
|
||||
<Tabs defaultActiveTab="discussion">
|
||||
<Tabs.TabPane key="discussion" title="讨论"><InspectorDiscussion selectedItem={selectedItem} /></Tabs.TabPane>
|
||||
<Tabs.TabPane key="props" title="属性"><div className="property-list"><Property label="所属项目" value={activeProject.name} /><Property label="频道" value={channels.find((c) => c.key === activeChannel)?.label ?? '概况'} /><Property label="创建人" value="张伟" /><Property label="更新时间" value="2026-07-20 10:30" /></div></Tabs.TabPane>
|
||||
<Tabs.TabPane key="more" title="更多"><Space direction="vertical" className="more-actions"><Button long>复制链接</Button><Button long>标记已处理</Button><Button long status="danger">归档对象</Button></Space></Tabs.TabPane>
|
||||
<Tabs.TabPane key="discussion" title="讨论">
|
||||
<InspectorDiscussion selectedItem={selectedItem} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="props" title="属性">
|
||||
<div className="property-list">
|
||||
<Property label="所属项目" value={workspaceMode ? '全部项目' : activeProject.name} />
|
||||
<Property label="频道" value={workspaceMode ? '工作台总览' : channels.find((channel) => channel.key === activeChannel)?.label ?? '概况'} />
|
||||
<Property label="创建人" value="张伟" />
|
||||
<Property label="更新时间" value="2026-07-20 10:30" />
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="more" title="更多">
|
||||
<Space direction="vertical" className="more-actions">
|
||||
<Button long>复制链接</Button>
|
||||
<Button long>标记已处理</Button>
|
||||
<Button long status="danger">归档对象</Button>
|
||||
</Space>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Sider>
|
||||
)
|
||||
|
||||
@@ -2,18 +2,34 @@ import type { CSSProperties } from 'react'
|
||||
import { Badge, Button, Layout, Space } from '@arco-design/web-react'
|
||||
import { IconDashboard, IconPlus } from '@arco-design/web-react/icon'
|
||||
import { projects } from './project-data'
|
||||
import type { Project } from './project-types'
|
||||
import type { Project, WorkbenchView } from './project-types'
|
||||
|
||||
const { Sider } = Layout
|
||||
|
||||
export function ProjectRail({ activeProject, onSelectProject }: { activeProject: Project; onSelectProject: (project: Project) => void }) {
|
||||
export function ProjectRail({
|
||||
activeProject,
|
||||
activeView,
|
||||
onSelectWorkspace,
|
||||
onSelectProject,
|
||||
}: {
|
||||
activeProject: Project
|
||||
activeView: WorkbenchView
|
||||
onSelectWorkspace: () => void
|
||||
onSelectProject: (project: Project) => void
|
||||
}) {
|
||||
return (
|
||||
<Sider className="project-rail" width={96}>
|
||||
<Button className="dashboard-button" icon={<IconDashboard />} />
|
||||
<Button
|
||||
className={activeView === 'workspace' ? 'dashboard-button active' : 'dashboard-button'}
|
||||
icon={<IconDashboard />}
|
||||
onClick={onSelectWorkspace}
|
||||
aria-label="工作台"
|
||||
title="工作台"
|
||||
/>
|
||||
<Space className="project-stack" direction="vertical" size={12}>
|
||||
{projects.map((project) => (
|
||||
<Badge key={project.id} count={project.badge} dot={false} className={project.urgent ? 'project-badge urgent' : 'project-badge'}>
|
||||
<button className={activeProject.id === project.id ? 'project-button active' : 'project-button'} onClick={() => onSelectProject(project)} style={{ '--project-color': project.color } as CSSProperties} title={project.name}>
|
||||
<button className={activeView === 'project' && activeProject.id === project.id ? 'project-button active' : 'project-button'} onClick={() => onSelectProject(project)} style={{ '--project-color': project.color } as CSSProperties} title={project.name}>
|
||||
{project.short}
|
||||
</button>
|
||||
</Badge>
|
||||
|
||||
@@ -1,34 +1,81 @@
|
||||
import { Badge, Button, Divider, Layout, Space, Tag, Typography } from '@arco-design/web-react'
|
||||
import { IconDashboard, IconMore, IconSettings } from '@arco-design/web-react/icon'
|
||||
import { IconApps, IconDashboard, IconMore, IconSettings } from '@arco-design/web-react/icon'
|
||||
import { channels } from './project-data'
|
||||
import type { ChannelKey, Project } from './project-types'
|
||||
import type { ChannelKey, Project, WorkbenchView } from './project-types'
|
||||
|
||||
const { Sider } = Layout
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export function ProjectSidebar({ activeProject, activeChannel, onSelectChannel, onSelectItem }: { activeProject: Project; activeChannel: ChannelKey; onSelectChannel: (key: ChannelKey) => void; onSelectItem: (title: string) => void }) {
|
||||
export function ProjectSidebar({
|
||||
activeView,
|
||||
activeProject,
|
||||
activeChannel,
|
||||
onSelectChannel,
|
||||
onSelectItem,
|
||||
}: {
|
||||
activeView: WorkbenchView
|
||||
activeProject: Project
|
||||
activeChannel: ChannelKey
|
||||
onSelectChannel: (key: ChannelKey) => void
|
||||
onSelectItem: (title: string) => void
|
||||
}) {
|
||||
const workspaceMode = activeView === 'workspace'
|
||||
|
||||
return (
|
||||
<Sider className="channel-sidebar" width={248}>
|
||||
<div className="project-title"><Space><IconDashboard /><Title heading={5}>{activeProject.name}</Title></Space><Button icon={<IconSettings />} size="mini" /></div>
|
||||
<div className="channel-list">
|
||||
{channels.map((channel) => (
|
||||
<button key={channel.key} className={activeChannel === channel.key ? 'channel-button active' : 'channel-button'} onClick={() => onSelectChannel(channel.key)}>
|
||||
<span className="channel-left">{channel.icon}<Text>{channel.label}</Text></span>
|
||||
{channel.count !== undefined ? <Badge count={channel.count} maxCount={999} /> : <IconMore />}
|
||||
</button>
|
||||
))}
|
||||
<div className="project-title">
|
||||
<Space>
|
||||
{workspaceMode ? <IconApps /> : <IconDashboard />}
|
||||
<Title heading={5}>{workspaceMode ? '工作台' : activeProject.name}</Title>
|
||||
</Space>
|
||||
<Button icon={<IconSettings />} size="mini" />
|
||||
</div>
|
||||
|
||||
<div className="channel-list">
|
||||
{workspaceMode ? (
|
||||
<>
|
||||
<button className="channel-button active">
|
||||
<span className="channel-left"><IconDashboard /><Text>工作台总览</Text></span>
|
||||
<Badge count={4} />
|
||||
</button>
|
||||
<button className="channel-button">
|
||||
<span className="channel-left"><IconApps /><Text>全部项目</Text></span>
|
||||
<IconMore />
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
channels.map((channel) => (
|
||||
<button
|
||||
key={channel.key}
|
||||
className={activeChannel === channel.key ? 'channel-button active' : 'channel-button'}
|
||||
onClick={() => onSelectChannel(channel.key)}
|
||||
>
|
||||
<span className="channel-left">{channel.icon}<Text>{channel.label}</Text></span>
|
||||
{channel.count !== undefined ? <Badge count={channel.count} maxCount={999} /> : <IconMore />}
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
<section className="recent-sessions">
|
||||
<Text className="section-title">最近会话</Text>
|
||||
{['需求评审要点整理', '生成 Q3 营销策略初稿', 'npx 安装 skill 至 codex', '产品定价模型讨论'].map((title, index) => (
|
||||
<button key={title} onClick={() => onSelectItem(title)}><Text>{title}</Text><Text type="secondary">{index === 0 ? '09:15' : index === 1 ? '08:47' : '昨天'}</Text></button>
|
||||
<button key={title} onClick={() => onSelectItem(title)}>
|
||||
<Text>{title}</Text>
|
||||
<Text type="secondary">{index === 0 ? '09:15' : index === 1 ? '08:47' : '昨天'}</Text>
|
||||
</button>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<Divider />
|
||||
<section className="tag-cloud">
|
||||
<Text className="section-title">标签</Text>
|
||||
<Space wrap>{['全部', '需求', '设计', '研发', '运营', 'AI'].map((tag) => <Tag key={tag} color={tag === '全部' ? 'arcoblue' : 'gray'}>{tag}</Tag>)}</Space>
|
||||
<Space wrap>
|
||||
{['全部', '需求', '设计', '研发', '运营', 'AI'].map((tag) => (
|
||||
<Tag key={tag} color={tag === '全部' ? 'arcoblue' : 'gray'}>{tag}</Tag>
|
||||
))}
|
||||
</Space>
|
||||
</section>
|
||||
</Sider>
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { ReactElement } from 'react'
|
||||
|
||||
export type Screen = 'login' | 'workbench'
|
||||
export type Theme = 'light' | 'dark'
|
||||
export type WorkbenchView = 'workspace' | 'project'
|
||||
export type ChannelKey = 'overview' | 'inbox' | 'tasks' | 'ai' | 'notes' | 'cron' | 'research' | 'design'
|
||||
|
||||
export type Project = {
|
||||
|
||||
Reference in New Issue
Block a user