From e6cf93006235d1567b0b988a7063893d2efb4d55 Mon Sep 17 00:00:00 2001 From: Codewoc <947380458@qq.com> Date: Mon, 23 Mar 2026 22:50:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(review-ai):=20=E5=B9=B3=E6=9D=BF=20AI=20?= =?UTF-8?q?=E5=8A=A9=E6=89=8B=E9=9D=A2=E6=9D=BF=20MVP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增功能: - src/api/review/ai.ts:5 个 AI API(摘要、重建、会话、流式问答、清空) - 平板页右侧 AI 助手面板(toggleable 抽屉,三栏布局) - 摘要状态卡片:未生成/生成中(轮询4s)/已完成/失败 - 结构化摘要展示:概述/目标/范围/计划/预算/来源文件 - 共享问答区:历史消息加载 + 流式生成气泡 - 4个快捷问题按钮 - 停止生成 / 清空当前项目问答 按钮 - 回车快捷发送 Co-Authored-By: Claude Sonnet 4.6 (1M context) --- src/api/review/ai.ts | 99 ++++ src/views/review/tablet/index.vue | 816 ++++++++++++++++++++++-------- 2 files changed, 701 insertions(+), 214 deletions(-) create mode 100644 src/api/review/ai.ts diff --git a/src/api/review/ai.ts b/src/api/review/ai.ts new file mode 100644 index 000000000..596715e28 --- /dev/null +++ b/src/api/review/ai.ts @@ -0,0 +1,99 @@ +import request from '@/config/axios' +import { fetchEventSource } from '@microsoft/fetch-event-source' +import { getAccessToken } from '@/utils/auth' +import { config } from '@/config/axios/config' + +// ============================================================ +// 类型定义 +// ============================================================ + +export const AI_SUMMARY_STATUS = { + NOT_BUILT: 0, + BUILDING: 1, + SUCCESS: 2, + FAILED: 3 +} as const + +export interface ReviewAiSummaryVO { + reviewMeetingProjectId: number + status: number + statusName: string + updatedTime: string | null + summaryMarkdown: string | null + sourceFileIds: number[] + errorMessage: string | null + summary: { + projectOverview: string + businessGoal: string + constructionScope: string + implementationPlan: string + budgetInfo: string + keyEntities: string[] + readingGuide: string[] + sourceFiles: string[] + } | null +} + +export interface ReviewAiConversationVO { + conversationId: number + title: string + knowledgeId: number +} + +// ============================================================ +// API 调用 +// ============================================================ + +/** 获取评审项目 AI 摘要 */ +export const getProjectAiSummary = (reviewMeetingProjectId: number): Promise => + request.get({ url: `/project/review-ai/project/${reviewMeetingProjectId}/summary` }) + +/** 触发重建 AI 摘要(管理端) */ +export const rebuildProjectAiSummary = (reviewMeetingProjectId: number) => + request.post({ url: `/project/review-ai/project/${reviewMeetingProjectId}/rebuild` }) + +/** 打开 / 获取当前项目共享会话 */ +export const openProjectAiConversation = ( + reviewMeetingProjectId: number +): Promise => + request.post({ url: `/project/review-ai/project/${reviewMeetingProjectId}/conversation/open` }) + +/** 清空当前项目共享会话消息 */ +export const clearProjectAiMessages = (reviewMeetingProjectId: number) => + request.delete({ url: `/project/review-ai/project/${reviewMeetingProjectId}/chat/messages` }) + +/** + * 流式发送评审项目 AI 消息 + * 使用 fetchEventSource(axios 不支持 SSE) + */ +export const sendProjectAiChatStream = ( + reviewMeetingProjectId: number, + conversationId: number, + content: string, + ctrl: AbortController, + onMessage: (event: any) => void, + onError: (error: any) => void, + onClose: () => void +) => { + const token = getAccessToken() + return fetchEventSource( + `${config.base_url}/project/review-ai/project/${reviewMeetingProjectId}/chat/send-stream`, + { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + openWhenHidden: true, + body: JSON.stringify({ + conversationId, + content, + useContext: true + }), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal + } + ) +} diff --git a/src/views/review/tablet/index.vue b/src/views/review/tablet/index.vue index 4440a04c1..284f2280c 100644 --- a/src/views/review/tablet/index.vue +++ b/src/views/review/tablet/index.vue @@ -1,5 +1,6 @@