# DEV-015: Session 视图层设计

本文档描述 PLAN-029-XH-unified-session-architecture 的视图层实现:路由、ChatPanel 嵌入、chat ↔ workspace 切换。它面向前端开发者,假设读者已阅读 RFC-001 与 ADR-001。

# 1. 路由设计

# 1.1. 路由表

// packages/ui/src/router/index.ts
{
  path: '/chat',
  component: AppLayout,
  children: [
    { path: '', redirect: '/chat/default' },
    { path: 'new', redirect: '/chat/default' },
    { path: ':sessionId', name: 'chat', component: ChatView },
  ],
},
{
  path: '/workspace/:sessionId?',
  name: 'workspace',
  component: AppLayout,
  children: [
    { path: '', name: 'workspace-session', component: WorkspaceView },
  ],
},

# 1.2. 设计决策

  • 保持 /chat/:sessionId/workspace/:sessionId 并存,不统一为 /session/:id
  • /workspace/:sessionId? 使用可选参数,兼容旧入口 /workspace
  • 两个路由共用 AppLayout,侧边栏与全局状态保持一致。

# 1.3. 默认首页

/ 仍然重定向到 /chat,chat 仍是用户最常入口。workspace 适合从 chat 切换进入。

# 2. 组件分层

# 2.1. ChatPanel.vue(可嵌入的对话面板)

packages/ui/src/components/chat/ChatPanel.vue

  • 职责:提供消息列表 + 输入框 + SSE 流式消息。
  • 接收 sessionId prop,内部读取 useChatStore / useAgentStore / useSessionStore
  • 不包含全屏布局头、不包含导航按钮。
  • 上传附件时同时调用 sessionStore.addAttachment(...),把附件提升到 Session 层。

# 2.2. ChatView.vue(全屏对话视图)

packages/ui/src/components/chat/ChatView.vue

  • 职责:全屏 chat 页面。
  • 包含顶部标题栏(Session 标题、Agent 状态)。
  • 内部嵌入 ChatPanel :session-id="currentSessionId"
  • 负责从路由参数或 currentSessionId 解析当前 Session,无 Session 时自动创建。

# 2.3. WorkspaceView.vue(文件编辑 + 内嵌对话)

packages/ui/src/components/workspace/WorkspaceView.vue

  • 职责:workspace 主页面。
  • 左侧:文件树;中间:代码编辑器;右侧:嵌入 ChatPanel
  • 从路由参数或 currentSessionId 解析当前 Session,无 Session 时自动创建。
  • activeFilePath 变化时,调用 workspaceStore.syncActiveFileToSession() 把文件上下文回写 Session 层。

# 2.4. WorkspaceToolbar.vue(workspace 工具栏)

packages/ui/src/components/workspace/WorkspaceToolbar.vue

  • 新增 Session 下拉选择器:切换当前 Session 并路由到 /workspace/:sessionId
  • 新增「切换回 chat」按钮:跳转到 /chat/:currentSessionId
  • 保留刷新、上传按钮。

# 2.5. Sidebar.vue(侧边栏)

packages/ui/src/components/sidebar/Sidebar.vue

  • 「New Chat」按钮:创建新 Session 并跳转 /chat/:sessionId
  • Session 列表:点击后跳转 /chat/:sessionId
  • 「Workspace」按钮:跳转 /workspace/:currentSessionId;若无当前 Session,先创建。

# 3. ChatPanel 嵌入细节

# 3.1. 为什么抽取 ChatPanel

  • ChatView.vue 包含全屏标题栏和布局逻辑,不适合直接嵌入 workspace。
  • ChatPanel.vue 只保留消息列表、输入框、SSEStream,占用空间小,可嵌入右侧面板。
  • 避免代码重复:ChatView 和 WorkspaceView 共用同一套消息流逻辑。

# 3.2. 嵌入布局

<!-- WorkspaceView.vue -->
<div class="flex h-full">
  <div class="w-60 shrink-0 border-r"><FileTreePanel /></div>
  <div class="flex-1 flex flex-col"><WorkspaceToolbar /><FileEditor /></div>
  <div class="w-96 border-l flex flex-col shrink-0">
    <ChatPanel :session-id="sessionId" />
  </div>
</div>

当前右侧面板宽度固定为 w-96(384px)。后续可扩展为可拖拽调整宽度。

# 4. Chat ↔ Workspace 切换

# 4.1. 同一 Session 切换

所有切换入口都基于 sessionStore.currentSessionId

  • 从 chat 切换到 workspace:点击 Sidebar 的 Workspace 按钮或 WorkspaceToolbar 的返回按钮。
  • 从 workspace 切换回 chat:点击 WorkspaceToolbar 的「当前 Session 标题」按钮。

# 4.2. 状态同步保证

  • Session 元数据:currentSessionIdtitlecontextuseSessionStore 统一维护。
  • 消息历史:ChatPanel 始终通过 chatStore.getMessages(sessionId) 读取,跨视图一致。
  • 附件:sessionStore.currentSessionAttachments 在两个视图中共享。
  • 文件上下文:workspaceStore.syncActiveFileToSession() 把 workspace 状态写回 Session 层,chat 中可读取。

# 5. 视图层数据流

# 6. 开发约定

  • 新增视图组件时,优先从 useSessionStore 读取 Session 元数据,不要直接操作 useChatStore 的 Session 相关状态。
  • ChatPanel 是唯一的内嵌对话组件,不要直接在其他地方嵌入 ChatView
  • 附件写入入口统一在 ChatPanel.handleSend;workspace 对附件只读。
  • 文件上下文写入入口统一在 useWorkspaceStore.syncActiveFileToSession

# 7. 后续可扩展点

扩展 说明 承接
右侧面板可拖拽宽度 提升 workspace 布局灵活性 后续 UI 改进
统一路由 /session/:id 长期评估,当前保持双路由 未来 PLAN
workspace 文件拖拽到 chat 需要附件后端支持 PLAN-031
多 Session 并列 同时打开多个 workspace/chat 未来设计

# 8. 参考

最后更新: 2026-07-31 19:58:33 +0800 在 GitHub 上编辑此页