# 中间面板审批事项技术方案 ## 1. 需求概述 ### 1.1 问题 当前待审批事项(工具调用审批)仅展示在右侧面板(TaskInspector)中。当右侧面板收起时,用户无法看到或操作待审批事项,导致 Agent 工具执行被阻塞,用户体验断裂。 ### 1.2 目标 将审批事项同时展示在中间面板输入框上方,确保无论右侧面板是否收起,用户都能进行审批操作。 ### 1.3 设计要点 - **位置**:中间面板,QueuePanel 与 ChatInput 之间 - **视觉形式**:堆叠卡片,多个审批请求纵向堆叠并带偏移,直观展示待处理数量 - **内容精简**:每个卡片显示标题 + 命令摘要 + 操作按钮(拒绝/执行) - **命令折叠**:命令较长时默认折叠,可点击展开查看完整内容,可复制 - **右侧面板保留**:TaskInspector 中的审批保持不变,两处同步展示 --- ## 2. 现状分析 ### 2.1 数据流 ``` Electron IPC (onToolApprovalRequest) → useChat.pendingApprovals (ref, 模块级) → TaskView.activePendingApprovals (computed, 按 sessionId 过滤) → TaskInspector.pendingApprovals (prop) → ToolApprovalBar (渲染) ``` **关键文件**: - [useChat.ts](../desktop/src/composables/useChat.ts) — 审批数据管理(第 43 行 `pendingApprovals`,第 53-69 行 IPC 监听,第 486-493 行 `confirmApproval`) - [TaskView.vue](../desktop/src/views/task/TaskView.vue) — 数据过滤与分发(第 228-230 行 `activePendingApprovals`) - [TaskInspector.vue](../desktop/src/components/task/TaskInspector.vue) — 右侧面板渲染(第 66-75 行) - [ToolApprovalBar.vue](../desktop/src/components/chat/ToolApprovalBar.vue) — 现有审批卡片组件 ### 2.2 审批数据结构 ```typescript interface ApprovalItem { requestId: string // 唯一标识 toolName: string // 'shell' | 'write_file' | 'edit_file' description: string // 命令/文件路径内容 dangerReason?: string // 危险原因(可选) sessionId?: string // 所属会话 } ``` ### 2.3 中间面板布局(TaskView.vue 第 100-123 行) ``` .task-container (flex: 1, flex-direction: column) .messages ← 消息列表,flex: 1,可滚动 QueuePanel ← 待发送队列(可选显示) ChatInput ← 输入框 ``` --- ## 3. 方案设计 ### 3.1 新增组件:ApprovalStack 创建独立组件 `desktop/src/components/chat/ApprovalStack.vue`,与 `ToolApprovalBar` 职责分离。 **Props**: ```typescript defineProps<{ items: ApprovalItem[] }>() defineEmits<{ confirm: [requestId: string, approved: boolean] }>() ``` ### 3.2 堆叠卡片交互 **展示逻辑**: - 只有最顶层(最后一张)卡片可交互(按钮可点击、命令可展开) - 点击执行/拒绝后,该卡片移除,下一张自动露出 - 卡片移除时带淡出动画 **堆叠样式**: ```css .stack-container { position: relative; height: <最顶层卡片高度 + 偏移量>; } .stack-card { position: absolute; left: 0; right: 0; bottom: 0; transition: transform 0.3s, opacity 0.3s; } /* 后面的卡片向上偏移 + 缩小,逐层递进 */ .stack-card:nth-last-child(2) { transform: translateY(-6px) scale(0.97); opacity: 0.7; } .stack-card:nth-last-child(3) { transform: translateY(-12px) scale(0.94); opacity: 0.4; } /* 第 4 张及以后不单独偏移,统一叠在第 3 层位置 */ .stack-card:nth-last-child(n+4) { transform: translateY(-12px) scale(0.94); opacity: 0; } ``` > 偏移量和缩放比例需实际调试确认,以上为初始值。 **数量指示**:当 items.length > 1 时,在卡片右上角显示数量 badge(如 `3`)。 ### 3.3 单张卡片内容 ``` ┌──────────────────────────────────────────────────┐ │ [!] 命令执行待审批 [x3] │ │ │ │ $ rm -rf /tmp/old_cache │ │ [展开] [复制] │ │ │ │ [ 拒绝 ] [ 执行 ] │ └──────────────────────────────────────────────────┘ ``` - **标题**:复用 `ToolApprovalBar` 的 `titleMap`(shell → "命令执行待审批",write_file → "文件写入待审批") - **命令区域**: - `description` 长度 ≤ 120 字符:直接显示,单行截断 + 省略号 - `description` 长度 > 120 字符:默认折叠,显示前 120 字符 + "...",下方显示 [展开] 按钮 - 点击展开后显示完整内容,按钮变为 [收起] - [复制] 按钮始终可见 - **dangerReason**:不在此处展示(保持精简,完整信息查看右侧面板) - **操作按钮**:横向排列,左侧拒绝(灰色),右侧执行(主色) ### 3.4 位置插入 在 `TaskView.vue` 的 `.task-container` 中,QueuePanel 与 ChatInput 之间插入 ApprovalStack: ```html