diff --git a/apps/senlinai-acro-react/scripts/structure-check.mjs b/apps/senlinai-acro-react/scripts/structure-check.mjs new file mode 100644 index 0000000..a6197e8 --- /dev/null +++ b/apps/senlinai-acro-react/scripts/structure-check.mjs @@ -0,0 +1,37 @@ +import { existsSync, readFileSync } from 'node:fs' + +const requiredFiles = [ + 'src/app/App.tsx', + 'src/pages/login.tsx', + 'src/pages/project.tsx', + 'src/pages/overview.tsx', + 'src/features/projects/project-data.tsx', + 'src/features/projects/project-rail.tsx', + 'src/features/projects/project-sidebar.tsx', + 'src/features/projects/project-topbar.tsx', + 'src/features/projects/project-inspector.tsx', + 'src/features/projects/project-statusbar.tsx', + 'src/features/projects/project-types.ts', +] + +const failures = requiredFiles.filter((file) => !existsSync(file)).map((file) => `missing ${file}`) + +if (existsSync('src/App.tsx')) { + failures.push('legacy src/App.tsx should be removed after page split') +} + +if (existsSync('src/app/App.tsx')) { + const appSource = readFileSync('src/app/App.tsx', 'utf8') + for (const forbidden of ['function LoginPage', 'function Workbench', 'function OverviewContent']) { + if (appSource.includes(forbidden)) { + failures.push(`src/app/App.tsx still contains ${forbidden}`) + } + } +} + +if (failures.length) { + console.error(failures.join('\n')) + process.exit(1) +} + +console.log('structure ok') diff --git a/apps/senlinai-acro-react/src/App.tsx b/apps/senlinai-acro-react/src/App.tsx deleted file mode 100644 index 8a92c79..0000000 --- a/apps/senlinai-acro-react/src/App.tsx +++ /dev/null @@ -1,569 +0,0 @@ -import type { CSSProperties, ReactElement } from 'react' -import { useMemo, useState } from 'react' -import { - Avatar, - Badge, - Button, - Card, - ConfigProvider, - Divider, - Form, - Grid, - Input, - Layout, - Message, - Space, - Tabs, - Tag, - Typography, -} from '@arco-design/web-react' -import { - IconApps, - IconArrowLeft, - IconArrowRight, - IconBook, - IconCalendar, - IconCheckCircleFill, - IconClockCircle, - IconDashboard, - IconEmail, - IconFile, - IconHome, - IconLaunch, - IconLink, - IconList, - IconMessage, - IconMoon, - IconMore, - IconNotification, - IconPlus, - IconQuestionCircle, - IconRobot, - IconSafe, - IconSearch, - IconSettings, - IconStorage, - IconSun, - IconThunderbolt, - IconUserGroup, -} from '@arco-design/web-react/icon' -import '@arco-design/web-react/dist/css/arco.css' -import './App.css' - -const { Header, Sider, Content, Footer } = Layout -const { Row, Col } = Grid -const { Title, Text, Paragraph } = Typography - -type Screen = 'login' | 'workbench' -type Theme = 'light' | 'dark' -type ChannelKey = 'overview' | 'inbox' | 'tasks' | 'ai' | 'notes' | 'cron' | 'research' | 'design' - -const projects = [ - { id: 'a1', name: '项目 A1', short: 'A1', badge: 635, urgent: true, color: '#165DFF' }, - { id: 'a2', name: '项目 A2', short: 'PM', badge: 36, color: '#00B42A' }, - { id: 'b1', name: '项目 B1', short: 'DS', badge: 4, urgent: true, color: '#722ED1' }, - { id: 'c3', name: '项目 C3', short: 'OP', badge: 45, color: '#FF7D00' }, -] - -const channels: Array<{ key: ChannelKey; label: string; count?: number; icon: ReactElement }> = [ - { key: 'overview', label: '概况', count: 635, icon: }, - { key: 'inbox', label: 'Inbox 消息流', count: 36, icon: }, - { key: 'tasks', label: '工作计划', count: 12, icon: }, - { key: 'ai', label: 'AI 会话', count: 4, icon: }, - { key: 'notes', label: '笔记资料', count: 343, icon: }, - { key: 'cron', label: 'Cron 计划任务', count: 45, icon: }, - { key: 'research', label: '客户研究频道', icon: }, - { key: 'design', label: '产品设计频道', icon: }, -] - -const inbox = [ - { title: '竞争对手定价更新监控', meta: '来源:产品研究 · 竞品追踪', owner: '李明', time: '2 小时前', tag: '市场情报' }, - { title: '客户反馈:导出功能报错', meta: '来源:客户支持 · 工单 #CS-1287', owner: '王芳', time: '4 小时前', tag: '客户反馈' }, - { title: 'API 调用策略评估建议', meta: '来源:后端架构讨论', owner: '张伟', time: '6 小时前', tag: '架构' }, - { title: '更新需求文档 v1.3.2', meta: '来源:产品需求 · 文档评审', owner: '陈晨', time: '昨天', tag: '需求文档' }, -] - -const tasks = [ - { title: '智能报表导出功能', owner: '张伟', progress: '60%', due: '2026-07-24', tag: '进行中' }, - { title: '企业版权限体系梳理', owner: '李明', progress: '30%', due: '2026-07-28', tag: '进行中' }, - { title: 'Q3 营销策略制定', owner: '王芳', progress: '45%', due: '2026-07-31', tag: '进行中' }, -] - -const aiSessions = [ - { title: '生成 Q3 营销策略初稿', summary: '基于市场分析和竞品数据,输出 Q3 营销策略重点...', owner: '张伟', time: '今天 08:47' }, - { title: '产品定价模型讨论', summary: '针对企业版与标准版定价模型进行对比分析...', owner: '李明', time: '昨天 16:05' }, - { title: '客户流失原因分析', summary: '分析近三个月客户流失数据与反馈原因...', owner: '王芳', time: '昨天 10:22' }, - { title: '行业趋势与机会洞察', summary: '围绕 AI 在企业协同领域的趋势与机会...', owner: '张伟', time: '7 月 18 日' }, -] - -const notes = [ - { title: '用户权限体系设计文档 v2.1', type: '文档', updated: '2026-07-19', owner: '李明' }, - { title: '导出功能技术方案评审', type: '文档', updated: '2026-07-18', owner: '张伟' }, - { title: '2026 Q3 竞品功能对比表', type: '表格', updated: '2026-07-18', owner: '王芳' }, -] - -const discussions = [ - { name: '李明', time: '2026-07-20 09:12', text: '关于导出报错的问题,已在测试环境复现,初步定位为权限校验逻辑异常。' }, - { name: '张伟', time: '2026-07-20 09:18', text: '@李明 麻烦同步修复预计完成时间,我这边需要同步给客户。' }, - { name: '王芳', time: '2026-07-20 10:05', text: '补充:客户反馈影响范围主要集中在企业版用户。' }, - { name: '系统通知', time: '2026-07-20 10:30', text: '已将此讨论关联到任务:智能报表导出功能。' }, -] - -function App() { - const [screen, setScreen] = useState('login') - const [theme, setTheme] = useState('light') - const [activeProject, setActiveProject] = useState(projects[0]) - const [activeChannel, setActiveChannel] = useState('overview') - const [selectedItem, setSelectedItem] = useState('Inbox 待处理(36)') - - const dark = theme === 'dark' - - return ( - - - {screen === 'login' ? ( - setScreen('workbench')} /> - ) : ( - setTheme(dark ? 'light' : 'dark')} - /> - )} - - - ) -} - -function LoginPage({ onLogin }: { onLogin: () => void }) { - const [server, setServer] = useState('https://10.0.0.15:8080') - - return ( - - - - - S - SenlinAI - - 私有部署 · 安全可控的智能协作平台 - 知识沉淀 · 团队协作 · AI 助理 - - v1.0.0 - 隐私政策 - 服务协议 - 关于 SenlinAI - - - - - 登录到您的 SenlinAI - 连接私有工作台 - - - } /> - - - - - - - - - - - 记住服务器地址 - - 无法连接? - - - 登录 - - - - - 连接正常 - 已连接到 {server} - - 更换服务器 - - - - - - 安全 · 专注 · 高效 - - SenlinAI 是面向知识工作者与内部团队的私有部署工作台,帮助团队在统一空间中收集信息、整理知识、协同决策。 - - } title="私有部署,数据自有" desc="所有数据与模型运行在您的环境中,安全可控。" /> - } title="知识沉淀,结构清晰" desc="从收集到整理,形成可复用的团队知识库。" /> - } title="协同高效,聚焦成果" desc="围绕项目与任务协同,减少沟通成本,专注交付。" /> - - - - ) -} - -function ValuePoint({ icon, title, desc }: { icon: ReactElement; title: string; desc: string }) { - return ( - - {icon} - - {title} - {desc} - - - ) -} - -function Workbench({ - activeProject, - activeChannel, - selectedItem, - theme, - onSelectProject, - onSelectChannel, - onSelectItem, - onToggleTheme, -}: { - activeProject: (typeof projects)[number] - activeChannel: ChannelKey - selectedItem: string - theme: Theme - onSelectProject: (project: (typeof projects)[number]) => void - onSelectChannel: (key: ChannelKey) => void - onSelectItem: (title: string) => void - onToggleTheme: () => void -}) { - return ( - - - - S - SenlinAI - - - } placeholder="搜索项目、频道、文档、任务、联系人..." /> - 搜索 - - - : } - onClick={onToggleTheme} - /> - } disabled /> - } disabled /> - - } /> - - } /> - } /> - - - - - - } /> - - {projects.map((project) => ( - - onSelectProject(project)} - style={{ '--project-color': project.color } as CSSProperties} - title={project.name} - > - {project.short} - - - ))} - - } title="新建项目" /> - - - - - - - {activeProject.name} - - } size="mini" /> - - - - {channels.map((channel) => ( - onSelectChannel(channel.key)} - > - {channel.icon}{channel.label} - {channel.count !== undefined ? : } - - ))} - - - - - 最近会话 - {['需求评审要点整理', '生成 Q3 营销策略初稿', 'npx 安装 skill 至 codex', '产品定价模型讨论'].map((title, index) => ( - onSelectItem(title)}> - {title} - {index === 0 ? '09:15' : index === 1 ? '08:47' : '昨天'} - - ))} - - - - - 标签 - - {['全部', '需求', '设计', '研发', '运营', 'AI'].map((tag) => ( - {tag} - ))} - - - - - - - - - - - - - - - - - c.key === activeChannel)?.label ?? '概况'} /> - - - - - - - 复制链接 - 标记已处理 - 归档对象 - - - - - - - - - ) -} - -function IconInteractionFallback() { - return -} - -function OverviewContent({ onSelectItem }: { onSelectItem: (title: string) => void }) { - const metrics = useMemo(() => [ - { title: 'Inbox 待处理', value: 36, delta: '较昨日 -8', icon: , color: 'arcoblue' }, - { title: '进行中的任务', value: 12, delta: '较昨日 -2', icon: , color: 'green' }, - { title: 'AI 会话活跃', value: 4, delta: '较昨日 +1', icon: , color: 'purple' }, - { title: '笔记资料总数', value: 343, delta: '较昨日 +5', icon: , color: 'cyan' }, - { title: '计划任务', value: 45, delta: '较昨日 +0', icon: , color: 'orange' }, - ], []) - - return ( - - - - 项目整体快照,帮助你快速了解项目动态 - 概况 - - - }>2026-07-20 - } /> - - - - - {metrics.map((metric) => ( - - - - {metric.icon} - - {metric.title} - {metric.value} - {metric.delta} - - - - - ))} - - - - - - - - ) -} - -function QueueSection({ title, items, onSelectItem }: { title: string; items: typeof inbox; onSelectItem: (title: string) => void }) { - return ( - - - {title} - 查看全部 - - {items.map((item) => ( - onSelectItem(item.title)}> - {item.title} - {item.meta} - {item.tag} - {item.owner} - {item.time} - - ))} - - ) -} - -function TaskSection({ onSelectItem }: { onSelectItem: (title: string) => void }) { - return ( - - - 进行中的任务(12) - 查看全部 - - {tasks.map((task) => ( - onSelectItem(task.title)}> - {task.title} - {task.tag} - 进度 {task.progress} - 负责人 {task.owner} - 截止 {task.due} - - ))} - - ) -} - -function AISessionSection({ onSelectItem }: { onSelectItem: (title: string) => void }) { - return ( - - - AI 会话(4) - 查看全部 - - {aiSessions.map((session) => ( - onSelectItem(session.title)}> - {session.title} - {session.summary} - {session.owner} - {session.time} - - ))} - - ) -} - -function NoteSection({ onSelectItem }: { onSelectItem: (title: string) => void }) { - return ( - - - 最近笔记资料(5) - 查看全部 - - {notes.map((note) => ( - onSelectItem(note.title)}> - {note.title} - {note.type} - 更新于 {note.updated} - {note.owner} - - ))} - - ) -} - -function InspectorDiscussion({ selectedItem }: { selectedItem: string }) { - return ( - - - 关于「{selectedItem}」 - } /> - - 注:同一对象的讨论与协作记录 - - - {discussions.map((item) => ( - - {item.name.slice(0, 1)} - - - {item.name} - {item.time} - - {item.text} - 回复 - - - ))} - - - - - - } /> - } /> - - Message.success('评论已发送(原型)')}>发送 - - - - ) -} - -function IconAttachmentFallback() { - return -} - -function Property({ label, value }: { label: string; value: string }) { - return ( - - {label} - {value} - - ) -} - -export default App diff --git a/apps/senlinai-acro-react/src/app/App.tsx b/apps/senlinai-acro-react/src/app/App.tsx new file mode 100644 index 0000000..b89f6b4 --- /dev/null +++ b/apps/senlinai-acro-react/src/app/App.tsx @@ -0,0 +1,41 @@ +import { useState } from 'react' +import { ConfigProvider } from '@arco-design/web-react' +import '@arco-design/web-react/dist/css/arco.css' +import '../App.css' +import { LoginPage } from '../pages/login' +import { ProjectPage } from '../pages/project' +import { projects } from '../features/projects/project-data' +import type { ChannelKey, Screen, Theme } from '../features/projects/project-types' + +function App() { + const [screen, setScreen] = useState('login') + const [theme, setTheme] = useState('light') + const [activeProject, setActiveProject] = useState(projects[0]) + const [activeChannel, setActiveChannel] = useState('overview') + const [selectedItem, setSelectedItem] = useState('Inbox 待处理(36)') + + const dark = theme === 'dark' + + return ( + + + {screen === 'login' ? ( + setScreen('workbench')} /> + ) : ( + setTheme(dark ? 'light' : 'dark')} + /> + )} + + + ) +} + +export default App diff --git a/apps/senlinai-acro-react/src/features/projects/project-data.tsx b/apps/senlinai-acro-react/src/features/projects/project-data.tsx new file mode 100644 index 0000000..d010aac --- /dev/null +++ b/apps/senlinai-acro-react/src/features/projects/project-data.tsx @@ -0,0 +1,53 @@ +import { IconClockCircle, IconEmail, IconFile, IconHome, IconLink, IconList, IconRobot, IconUserGroup } from '@arco-design/web-react/icon' +import type { Project, ProjectChannel } from './project-types' + +export const projects: Project[] = [ + { id: 'a1', name: '项目 A1', short: 'A1', badge: 635, urgent: true, color: '#165DFF' }, + { id: 'a2', name: '项目 A2', short: 'PM', badge: 36, color: '#00B42A' }, + { id: 'b1', name: '项目 B1', short: 'DS', badge: 4, urgent: true, color: '#722ED1' }, + { id: 'c3', name: '项目 C3', short: 'OP', badge: 45, color: '#FF7D00' }, +] + +export const channels: ProjectChannel[] = [ + { key: 'overview', label: '概况', count: 635, icon: }, + { key: 'inbox', label: 'Inbox 消息流', count: 36, icon: }, + { key: 'tasks', label: '工作计划', count: 12, icon: }, + { key: 'ai', label: 'AI 会话', count: 4, icon: }, + { key: 'notes', label: '笔记资料', count: 343, icon: }, + { key: 'cron', label: 'Cron 计划任务', count: 45, icon: }, + { key: 'research', label: '客户研究频道', icon: }, + { key: 'design', label: '产品设计频道', icon: }, +] + +export const inbox = [ + { title: '竞争对手定价更新监控', meta: '来源:产品研究 · 竞品追踪', owner: '李明', time: '2 小时前', tag: '市场情报' }, + { title: '客户反馈:导出功能报错', meta: '来源:客户支持 · 工单 #CS-1287', owner: '王芳', time: '4 小时前', tag: '客户反馈' }, + { title: 'API 调用策略评估建议', meta: '来源:后端架构讨论', owner: '张伟', time: '6 小时前', tag: '架构' }, + { title: '更新需求文档 v1.3.2', meta: '来源:产品需求 · 文档评审', owner: '陈晨', time: '昨天', tag: '需求文档' }, +] + +export const tasks = [ + { title: '智能报表导出功能', owner: '张伟', progress: '60%', due: '2026-07-24', tag: '进行中' }, + { title: '企业版权限体系梳理', owner: '李明', progress: '30%', due: '2026-07-28', tag: '进行中' }, + { title: 'Q3 营销策略制定', owner: '王芳', progress: '45%', due: '2026-07-31', tag: '进行中' }, +] + +export const aiSessions = [ + { title: '生成 Q3 营销策略初稿', summary: '基于市场分析和竞品数据,输出 Q3 营销策略重点...', owner: '张伟', time: '今天 08:47' }, + { title: '产品定价模型讨论', summary: '针对企业版与标准版定价模型进行对比分析...', owner: '李明', time: '昨天 16:05' }, + { title: '客户流失原因分析', summary: '分析近三个月客户流失数据与反馈原因...', owner: '王芳', time: '昨天 10:22' }, + { title: '行业趋势与机会洞察', summary: '围绕 AI 在企业协同领域的趋势与机会...', owner: '张伟', time: '7 月 18 日' }, +] + +export const notes = [ + { title: '用户权限体系设计文档 v2.1', type: '文档', updated: '2026-07-19', owner: '李明' }, + { title: '导出功能技术方案评审', type: '文档', updated: '2026-07-18', owner: '张伟' }, + { title: '2026 Q3 竞品功能对比表', type: '表格', updated: '2026-07-18', owner: '王芳' }, +] + +export const discussions = [ + { name: '李明', time: '2026-07-20 09:12', text: '关于导出报错的问题,已在测试环境复现,初步定位为权限校验逻辑异常。' }, + { name: '张伟', time: '2026-07-20 09:18', text: '@李明 麻烦同步修复预计完成时间,我这边需要同步给客户。' }, + { name: '王芳', time: '2026-07-20 10:05', text: '补充:客户反馈影响范围主要集中在企业版用户。' }, + { name: '系统通知', time: '2026-07-20 10:30', text: '已将此讨论关联到任务:智能报表导出功能。' }, +] diff --git a/apps/senlinai-acro-react/src/features/projects/project-inspector.tsx b/apps/senlinai-acro-react/src/features/projects/project-inspector.tsx new file mode 100644 index 0000000..de2b565 --- /dev/null +++ b/apps/senlinai-acro-react/src/features/projects/project-inspector.tsx @@ -0,0 +1,70 @@ +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' + +const { Sider } = Layout +const { Title, Text, Paragraph } = Typography + +export function ProjectInspector({ activeProject, activeChannel, selectedItem }: { activeProject: Project; activeChannel: ChannelKey; selectedItem: string }) { + return ( + + + + c.key === activeChannel)?.label ?? '概况'} /> + 复制链接标记已处理归档对象 + + + ) +} + +function InspectorDiscussion({ selectedItem }: { selectedItem: string }) { + return ( + + + 关于「{selectedItem}」 + } /> + + 注:同一对象的讨论与协作记录 + + + {discussions.map((item) => ( + + {item.name.slice(0, 1)} + + + {item.name} + {item.time} + + {item.text} + 回复 + + + ))} + + + + + + } /> + } /> + + Message.success('评论已发送(原型)')}>发送 + + + + ) +} + +function IconAttachmentFallback() { + return +} + +function Property({ label, value }: { label: string; value: string }) { + return ( + + {label} + {value} + + ) +} diff --git a/apps/senlinai-acro-react/src/features/projects/project-rail.tsx b/apps/senlinai-acro-react/src/features/projects/project-rail.tsx new file mode 100644 index 0000000..cc33df7 --- /dev/null +++ b/apps/senlinai-acro-react/src/features/projects/project-rail.tsx @@ -0,0 +1,25 @@ +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' + +const { Sider } = Layout + +export function ProjectRail({ activeProject, onSelectProject }: { activeProject: Project; onSelectProject: (project: Project) => void }) { + return ( + + } /> + + {projects.map((project) => ( + + onSelectProject(project)} style={{ '--project-color': project.color } as CSSProperties} title={project.name}> + {project.short} + + + ))} + + } title="新建项目" /> + + ) +} diff --git a/apps/senlinai-acro-react/src/features/projects/project-sidebar.tsx b/apps/senlinai-acro-react/src/features/projects/project-sidebar.tsx new file mode 100644 index 0000000..e0e35af --- /dev/null +++ b/apps/senlinai-acro-react/src/features/projects/project-sidebar.tsx @@ -0,0 +1,35 @@ +import { Badge, Button, Divider, Layout, Space, Tag, Typography } from '@arco-design/web-react' +import { IconDashboard, IconMore, IconSettings } from '@arco-design/web-react/icon' +import { channels } from './project-data' +import type { ChannelKey, Project } 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 }) { + return ( + + {activeProject.name}} size="mini" /> + + {channels.map((channel) => ( + onSelectChannel(channel.key)}> + {channel.icon}{channel.label} + {channel.count !== undefined ? : } + + ))} + + + + 最近会话 + {['需求评审要点整理', '生成 Q3 营销策略初稿', 'npx 安装 skill 至 codex', '产品定价模型讨论'].map((title, index) => ( + onSelectItem(title)}>{title}{index === 0 ? '09:15' : index === 1 ? '08:47' : '昨天'} + ))} + + + + 标签 + {['全部', '需求', '设计', '研发', '运营', 'AI'].map((tag) => {tag})} + + + ) +} diff --git a/apps/senlinai-acro-react/src/features/projects/project-statusbar.tsx b/apps/senlinai-acro-react/src/features/projects/project-statusbar.tsx new file mode 100644 index 0000000..5969f5d --- /dev/null +++ b/apps/senlinai-acro-react/src/features/projects/project-statusbar.tsx @@ -0,0 +1,19 @@ +import { Avatar, Button, Layout, Space, Typography } from '@arco-design/web-react' +import { IconApps, IconCalendar, IconCheckCircleFill, IconMessage, IconRobot, IconSettings, IconStorage, IconThunderbolt, IconUserGroup } from '@arco-design/web-react/icon' + +const { Footer } = Layout +const { Text } = Typography + +export function ProjectStatusbar() { + return ( + + ) +} + +function IconInteractionFallback() { + return +} diff --git a/apps/senlinai-acro-react/src/features/projects/project-topbar.tsx b/apps/senlinai-acro-react/src/features/projects/project-topbar.tsx new file mode 100644 index 0000000..4f01e84 --- /dev/null +++ b/apps/senlinai-acro-react/src/features/projects/project-topbar.tsx @@ -0,0 +1,29 @@ +import { Badge, Button, Input, Layout, Space, Typography } from '@arco-design/web-react' +import { IconApps, IconArrowLeft, IconArrowRight, IconMoon, IconNotification, IconQuestionCircle, IconSearch, IconSun } from '@arco-design/web-react/icon' +import type { Theme } from './project-types' + +const { Header } = Layout +const { Text } = Typography + +export function ProjectTopbar({ theme, onToggleTheme }: { theme: Theme; onToggleTheme: () => void }) { + return ( + + + S + SenlinAI + + + } placeholder="搜索项目、频道、文档、任务、联系人..." /> + 搜索 + + + : } onClick={onToggleTheme} /> + } disabled /> + } disabled /> + } /> + } /> + } /> + + + ) +} diff --git a/apps/senlinai-acro-react/src/features/projects/project-types.ts b/apps/senlinai-acro-react/src/features/projects/project-types.ts new file mode 100644 index 0000000..3951b49 --- /dev/null +++ b/apps/senlinai-acro-react/src/features/projects/project-types.ts @@ -0,0 +1,21 @@ +import type { ReactElement } from 'react' + +export type Screen = 'login' | 'workbench' +export type Theme = 'light' | 'dark' +export type ChannelKey = 'overview' | 'inbox' | 'tasks' | 'ai' | 'notes' | 'cron' | 'research' | 'design' + +export type Project = { + id: string + name: string + short: string + badge: number + urgent?: boolean + color: string +} + +export type ProjectChannel = { + key: ChannelKey + label: string + count?: number + icon: ReactElement +} diff --git a/apps/senlinai-acro-react/src/main.tsx b/apps/senlinai-acro-react/src/main.tsx index bef5202..e99750c 100644 --- a/apps/senlinai-acro-react/src/main.tsx +++ b/apps/senlinai-acro-react/src/main.tsx @@ -1,7 +1,7 @@ import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' -import App from './App.tsx' +import App from './app/App.tsx' createRoot(document.getElementById('root')!).render( diff --git a/apps/senlinai-acro-react/src/pages/login.tsx b/apps/senlinai-acro-react/src/pages/login.tsx new file mode 100644 index 0000000..10ec27c --- /dev/null +++ b/apps/senlinai-acro-react/src/pages/login.tsx @@ -0,0 +1,87 @@ +import type { ReactElement } from 'react' +import { useState } from 'react' +import { Button, Card, Form, Input, Space, Typography } from '@arco-design/web-react' +import { IconBook, IconCheckCircleFill, IconLaunch, IconSafe, IconUserGroup } from '@arco-design/web-react/icon' + +const { Title, Text, Paragraph } = Typography + +export function LoginPage({ onLogin }: { onLogin: () => void }) { + const [server, setServer] = useState('https://10.0.0.15:8080') + + return ( + + + + + S + SenlinAI + + 私有部署 · 安全可控的智能协作平台 + 知识沉淀 · 团队协作 · AI 助理 + + v1.0.0 + 隐私政策 + 服务协议 + 关于 SenlinAI + + + + + 登录到您的 SenlinAI + 连接私有工作台 + + + } /> + + + + + + + + + + + 记住服务器地址 + + 无法连接? + + + 登录 + + + + + 连接正常 + 已连接到 {server} + + 更换服务器 + + + + + + 安全 · 专注 · 高效 + + SenlinAI 是面向知识工作者与内部团队的私有部署工作台,帮助团队在统一空间中收集信息、整理知识、协同决策。 + + } title="私有部署,数据自有" desc="所有数据与模型运行在您的环境中,安全可控。" /> + } title="知识沉淀,结构清晰" desc="从收集到整理,形成可复用的团队知识库。" /> + } title="协同高效,聚焦成果" desc="围绕项目与任务协同,减少沟通成本,专注交付。" /> + + + + ) +} + +function ValuePoint({ icon, title, desc }: { icon: ReactElement; title: string; desc: string }) { + return ( + + {icon} + + {title} + {desc} + + + ) +} diff --git a/apps/senlinai-acro-react/src/pages/overview.tsx b/apps/senlinai-acro-react/src/pages/overview.tsx new file mode 100644 index 0000000..b286d03 --- /dev/null +++ b/apps/senlinai-acro-react/src/pages/overview.tsx @@ -0,0 +1,132 @@ +import { useMemo } from 'react' +import { Button, Card, Grid, Space, Tag, Typography } from '@arco-design/web-react' +import { IconCalendar, IconCheckCircleFill, IconClockCircle, IconEmail, IconFile, IconMore, IconRobot } from '@arco-design/web-react/icon' +import { aiSessions, inbox, notes, tasks } from '../features/projects/project-data' + +const { Row, Col } = Grid +const { Title, Text } = Typography + +export function OverviewPage({ onSelectItem }: { onSelectItem: (title: string) => void }) { + const metrics = useMemo(() => [ + { title: 'Inbox 待处理', value: 36, delta: '较昨日 -8', icon: , color: 'arcoblue' }, + { title: '进行中的任务', value: 12, delta: '较昨日 -2', icon: , color: 'green' }, + { title: 'AI 会话活跃', value: 4, delta: '较昨日 +1', icon: , color: 'purple' }, + { title: '笔记资料总数', value: 343, delta: '较昨日 +5', icon: , color: 'cyan' }, + { title: '计划任务', value: 45, delta: '较昨日 +0', icon: , color: 'orange' }, + ], []) + + return ( + + + + 项目整体快照,帮助你快速了解项目动态 + 概况 + + + }>2026-07-20 + } /> + + + + + {metrics.map((metric) => ( + + + + {metric.icon} + + {metric.title} + {metric.value} + {metric.delta} + + + + + ))} + + + + + + + + ) +} + +function QueueSection({ title, items, onSelectItem }: { title: string; items: typeof inbox; onSelectItem: (title: string) => void }) { + return ( + + + {title} + 查看全部 + + {items.map((item) => ( + onSelectItem(item.title)}> + {item.title} + {item.meta} + {item.tag} + {item.owner} + {item.time} + + ))} + + ) +} + +function TaskSection({ onSelectItem }: { onSelectItem: (title: string) => void }) { + return ( + + + 进行中的任务(12) + 查看全部 + + {tasks.map((task) => ( + onSelectItem(task.title)}> + {task.title} + {task.tag} + 进度 {task.progress} + 负责人 {task.owner} + 截止 {task.due} + + ))} + + ) +} + +function AISessionSection({ onSelectItem }: { onSelectItem: (title: string) => void }) { + return ( + + + AI 会话(4) + 查看全部 + + {aiSessions.map((session) => ( + onSelectItem(session.title)}> + {session.title} + {session.summary} + {session.owner} + {session.time} + + ))} + + ) +} + +function NoteSection({ onSelectItem }: { onSelectItem: (title: string) => void }) { + return ( + + + 最近笔记资料(5) + 查看全部 + + {notes.map((note) => ( + onSelectItem(note.title)}> + {note.title} + {note.type} + 更新于 {note.updated} + {note.owner} + + ))} + + ) +} diff --git a/apps/senlinai-acro-react/src/pages/project.tsx b/apps/senlinai-acro-react/src/pages/project.tsx new file mode 100644 index 0000000..fde7a32 --- /dev/null +++ b/apps/senlinai-acro-react/src/pages/project.tsx @@ -0,0 +1,55 @@ +import { Layout } from '@arco-design/web-react' +import { OverviewPage } from './overview' +import { ProjectInspector } from '../features/projects/project-inspector' +import { ProjectRail } from '../features/projects/project-rail' +import { ProjectSidebar } from '../features/projects/project-sidebar' +import { ProjectStatusbar } from '../features/projects/project-statusbar' +import { ProjectTopbar } from '../features/projects/project-topbar' +import type { ChannelKey, Project, Theme } from '../features/projects/project-types' + +const { Content } = Layout + +export function ProjectPage({ + activeProject, + activeChannel, + selectedItem, + theme, + onSelectProject, + onSelectChannel, + onSelectItem, + onToggleTheme, +}: { + activeProject: Project + activeChannel: ChannelKey + selectedItem: string + theme: Theme + onSelectProject: (project: Project) => void + onSelectChannel: (key: ChannelKey) => void + onSelectItem: (title: string) => void + onToggleTheme: () => void +}) { + return ( + + + + + + + + + + + + + + + + + + ) +}