feat(review-ai): 平板 AI 助手面板 MVP
新增功能: - src/api/review/ai.ts:5 个 AI API(摘要、重建、会话、流式问答、清空) - 平板页右侧 AI 助手面板(toggleable 抽屉,三栏布局) - 摘要状态卡片:未生成/生成中(轮询4s)/已完成/失败 - 结构化摘要展示:概述/目标/范围/计划/预算/来源文件 - 共享问答区:历史消息加载 + 流式生成气泡 - 4个快捷问题按钮 - 停止生成 / 清空当前项目问答 按钮 - 回车快捷发送 Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>pull/874/head
parent
302772ecb9
commit
e6cf930062
|
|
@ -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<ReviewAiSummaryVO> =>
|
||||||
|
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<ReviewAiConversationVO> =>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue