# 研发工单 v1.3.0 - 03-04 任务建议与确认 UI > 父工单:[03-会议任务拆分](./03-会议任务拆分.md) > > 依赖:03-01、03-03(后端 API 就绪) --- ## 一、目标 实现会议任务拆分的完整前端 UI:在 TaskItem 中为 Meeting 类型待办项提供录音/纪要/拆分入口,实现建议审阅与确认对话框,打通从会议内容输入到批量创建子任务的端到端用户体验。 ## 二、范围 **包含**: - Meeting 类型待办项的特殊 UI(图标、录音/纪要/拆分入口) - 录音控件的嵌入(调用 03-02 的 `AudioRecorder.vue` + `useAudioRecorder`) - 纪要输入区域(textarea 保存会议文字内容) - AI 拆分请求与加载状态 - 建议审阅对话框(`MeetingBreakdownDialog.vue`) - 建议勾选、编辑、调整优先级、手动补充 - 确认后批量创建子任务 - 前端类型扩展(`taskType`、`meetingNotes`、`audioDuration`) **不包含**: - 录音控件本身(由 03-02 提供) - 后端逻辑(由 03-01、03-03 提供) ## 三、前置条件 | 条件 | 说明 | |---|---| | 03-01 完成 | 后端 API 就绪(meeting 端点 + TaskType) | | 03-02 完成 | AudioRecorder 组件和 useAudioRecorder 可用 | | 03-03 完成 | breakdown/breakdown/confirm 端点可用 | ## 四、详细规格 ### 4.1 前端类型扩展 ```typescript // src/Hua.Todo.Web/src/types/task.ts /// 待办项类型 type TaskType = 0 | 1; // 0=Normal, 1=Meeting interface Task { // ... 已有字段 ... /** 待办项类型 */ taskType?: TaskType; /** 会议纪要/转写文字 */ meetingNotes?: string; /** 录音时长(秒) */ audioDuration?: number; } interface CreateTaskDto { title: string; priority: TaskPriority; parentTaskId?: number; /** 待办项类型,默认 0 */ taskType?: TaskType; } ``` ### 4.2 会议相关类型 ```typescript // 新增或扩展 meeting.ts 中的类型 /// 会议任务拆分建议 interface MeetingTaskSuggestion { title: string; priority: TaskPriority; reason: string; } /// 拆分响应 interface BreakdownResponse { taskId: number; suggestions: MeetingTaskSuggestion[]; } /// 待确认的子任务项 interface PendingSubTask { title: string; priority: TaskPriority; /** 是否被用户勾选 */ checked: boolean; /** LLM 拆分的原始建议(含原因) */ suggestion?: MeetingTaskSuggestion; /** 是否为用户手动添加的条目 */ isManual?: boolean; } ``` ### 4.3 TaskItem.vue 扩展 为 Meeting 类型的待办项显示额外操作入口: ``` ┌──────────────────────────────────────────────┐ │ 📋 ☐ 周一产品评审会 🔴 H ··· │ │ 🎙 会议 · 📝 有纪要 · 0个子任务 │ │ [🎤] [📝] [🤖] │ │ ┌─ ✅ 整理需求文档 🔴 │ ← 已有子任务(若有) │ └─ ☐ 安排评审会议 🟡 │ └──────────────────────────────────────────────┘ [🎤] = 录音按钮 [📝] = 打开纪要编辑区 [🤖] = AI 拆分(需有会议内容) ``` 条件渲染逻辑: - `task.taskType === 1`(Meeting)时,显示会议图标 `🎙` 标签 - 显示三个操作按钮在任务行右侧 - 拆分子任务入口:点击 `[🤖]` → 触发 `handleAiBreakdown()` ### 4.4 会议纪要编辑区(内联) 点击 `[📝]` 后展开内联编辑区: ``` ┌──────────────────────────────────────────────┐ │ 📋 ☐ 周一产品评审会 │ │ │ │ 会议纪要: │ │ ┌──────────────────────────────────────┐ │ │ │ 今天的产品评审会主要讨论了三个议题: │ │ │ │ 第一,确定 Q3 产品路线图... │ │ │ │ 第二,... │ │ │ │ │ │ │ └──────────────────────────────────────┘ │ │ 录音时长:30分15秒 │ │ │ │ [🔊 开始录音] [🤖 AI拆分] [💾 保存纪要] │ └──────────────────────────────────────────────┘ ``` ### 4.5 AI 拆分流程(前端) ```typescript // 在 TaskItem.vue 或拆分逻辑中 async function handleAiBreakdown(task: Task) { // 1. 检查是否有会议内容 if (!task.meetingNotes) { showToast('请先输入纪要或上传录音', 'warning'); return; } // 2. 调用 AI 拆分 isLoading.value = true; try { const resp = await meetingApi.requestBreakdown(task.id); if (resp.data.suggestions.length === 0) { showToast('未从会议内容中提取到待办项,请确认纪要内容', 'info'); return; } // 3. 打开审阅对话框 openBreakdownDialog(task, resp.data.suggestions); } catch (err) { showToast('AI拆分失败,请稍后重试', 'error'); } finally { isLoading.value = false; } } ``` ### 4.6 MeetingBreakdownDialog.vue 审阅确认对话框: ``` ┌──────────────────────────────────────────────┐ │ 会议任务拆分 — 周一产品评审会 [✕] │ │ │ │ AI 根据会议内容生成了以下待办项建议: │ │ 请勾选需要创建的条目,可编辑标题和优先级。 │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ ☑ 整理产品需求文档 [🔴 高 ▼] │ │ │ │ 📝 会议中提到需要在本周五前完成 │ │ │ │ │ │ │ │ ☑ 安排技术方案评审 [🟡 中 ▼] │ │ │ │ 📝 张三负责在下周三前输出技术方案 │ │ │ │ │ │ │ │ ☑ 跟进客户反馈 [🟢 低 ▼] │ │ │ │ 📝 李四反馈了三个客户问题 │ │ │ │ │ │ │ │ ☐ 项目总结报告 [🟡 中 ▼] │ │ │ │ 📝 月末需要汇总各模块进度 │ │ │ └──────────────────────────────────────┘ │ │ │ │ [+ 手动添加待办项] │ │ │ │ 已选 3 项 │ │ [取消] [✅ 确认创建 (3)] │ └──────────────────────────────────────────────┘ ``` 交互: - 每条建议默认勾选 - 点击标题可编辑(内联 input) - 优先级下拉可调整 - 取消勾选的条目不会被创建 - `[+ 手动添加]` 新增空白条目 - 底部显示已选数量 ### 4.7 确认创建 ```typescript async function confirmBreakdown() { const selected = pendingSubTasks.value .filter(s => s.checked) .map(s => ({ title: s.title, priority: s.priority })); if (selected.length === 0) { showToast('请至少选择一个待办项', 'warning'); return; } isCreating.value = true; try { const resp = await meetingApi.confirmBreakdown(task.id, selected); showToast(`已成功创建 ${resp.data.createdCount} 个待办项`, 'success'); closeDialog(); emit('updated'); // 刷新任务列表 } catch (err) { showToast('创建失败,请重试', 'error'); } finally { isCreating.value = false; } } ``` ### 4.8 创建任务时的类型选择 在 `TaskList.vue` 的快速创建表单中增加类型选择: ``` ┌──────────────────────────────────────────────┐ │ [+ 新待办项] [_____输入标题_____] │ │ 优先级: [🟡 中 ▼] 类型: [📋 普通 ▼] │ │ [🎙 会议] │ │ [+ 添加] │ └──────────────────────────────────────────────┘ ``` 默认类型为 `Normal`(普通),选择 `Meeting` 后创建的待办项为会议类型。 ## 五、验收标准 | 验收项 | 验证方法 | 预期结果 | |---|---|---| | 创建 Meeting 类型 | 新建时选择"会议"类型 | 列表中显示 🎙 图标 | | 会议操作按钮 | 展开 Meeting 类型任务 | 显示录音/纪要/拆分三个按钮 | | 普通类型不显示 | 查看普通待办项 | 无会议相关按钮 | | 录音入口 | 点击录音按钮 | 弹出/展开 AudioRecorder 组件 | | 纪要编辑 | 输入会议纪要后保存 | `meetingNotes` 保存成功 | | AI 拆分请求 | 点击 AI 拆分按钮 | 加载状态 → 审阅对话框弹出 | | 无内容拒绝拆分 | 无纪要时点击拆分 | 提示"请先输入纪要" | | 建议审阅 | 审阅对话框中勾选/取消/编辑 | 操作流畅,状态正确 | | 手动添加 | 点击"+ 手动添加" | 新增空白条目,可编辑 | | 批量创建 | 确认勾选的条目 | 子任务创建到父任务下 | | 离线降级提示 | 离线时点击拆分 | 提示"AI 拆分仅在线可用" | | 响应式 | 缩小浏览器窗口 | 对话框和控件正常显示 | ## 六、风险 | 风险 | 影响 | 应对 | |---|---|---| | 组件嵌套复杂 | TaskItem 已有子任务递归,再加会议 UI 后层级深 | 会议功能用可选插槽方式注入,不影响普通任务渲染 | | 录音与拆分同时操作 | 用户可能在转写期间又点拆分 | 操作按钮在加载中时禁用 | | 大量建议渲染 | LLM 返回 10+ 条建议时列表长 | 审阅列表设最大高度 + 滚动;建议上限 15 条 | ## 七、Touch List | 文件路径 | 修改类型 | |---|---| | `src/Hua.Todo.Web/src/types/task.ts` | 修改(新增 TaskType、meetingNotes、audioDuration) | | `src/Hua.Todo.Web/src/api/meeting.ts` | 新增 | | `src/Hua.Todo.Web/src/components/AudioRecorder.vue` | 新增(由 03-02 产出,本工单集成) | | `src/Hua.Todo.Web/src/components/MeetingBreakdownDialog.vue` | 新增 | | `src/Hua.Todo.Web/src/components/TaskItem.vue` | 修改(Meeting 类型条件渲染) | | `src/Hua.Todo.Web/src/components/TaskList.vue` | 修改(新建任务时类型选择) | --- **工单编号**:03-04 **标题**:任务建议与确认 UI **版本**:v1.3.0 **创建日期**:2026-06-16