feat: Implement compliance document analysis and chat functionality
- Added API functions for document analysis, compliance result retrieval, and chat streaming. - Integrated document upload and analysis in CompliancePage with progress simulation. - Enhanced DocsPage to load documents from the API and handle file uploads with parsing and embedding. - Updated RagChatPage to fetch quick questions from the API and handle chat responses with streaming. - Improved StatusPage to display system statistics and configuration from the API. - Configured Vite to proxy API requests to the backend server.
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
mockPriorityActions,
|
||||
fullDocumentContent,
|
||||
} from '../../data';
|
||||
import { analyzeDocument, getComplianceResult, complianceChat } from '../../api/compliance';
|
||||
import { ChatPanel } from './ChatPanel';
|
||||
import { TPattern } from '../../components/common/TPattern';
|
||||
|
||||
@@ -111,39 +112,114 @@ export const CompliancePage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const startAnalysis = () => {
|
||||
const startAnalysis = async () => {
|
||||
if (!uploadedDoc) return;
|
||||
|
||||
|
||||
setIsAnalyzing(true);
|
||||
setAnalyzeStep(1);
|
||||
setAnalyzePercent(0);
|
||||
|
||||
setTimeout(() => {
|
||||
setAnalyzePercent(30);
|
||||
setAnalyzeAction('正在解析文档结构...');
|
||||
}, 500);
|
||||
try {
|
||||
// Get file from upload input
|
||||
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
|
||||
const file = fileInput?.files?.[0];
|
||||
console.log("123")
|
||||
|
||||
setTimeout(() => {
|
||||
setAnalyzeStep(2);
|
||||
setAnalyzePercent(50);
|
||||
setAnalyzeAction('正在识别语义段落...');
|
||||
}, 1500);
|
||||
// if (!file) {
|
||||
// // setIsAnalyzing(false);
|
||||
// // return;
|
||||
// }
|
||||
|
||||
setTimeout(() => {
|
||||
setAnalyzePercent(70);
|
||||
setAnalyzeAction('正在识别第 2/3 个语义段落...');
|
||||
}, 2500);
|
||||
console.log("456")
|
||||
// Upload and get task ID
|
||||
const uploadRes = await analyzeDocument(file);
|
||||
|
||||
setTimeout(() => {
|
||||
setAnalyzeStep(3);
|
||||
setAnalyzePercent(85);
|
||||
setAnalyzeAction('正在匹配法规条款...');
|
||||
}, 3500);
|
||||
// Simulate progress
|
||||
setTimeout(() => {
|
||||
setAnalyzePercent(30);
|
||||
setAnalyzeAction('正在解析文档结构...');
|
||||
}, 500);
|
||||
|
||||
setTimeout(() => {
|
||||
setAnalyzePercent(100);
|
||||
setAnalyzeAction('分析完成');
|
||||
setTimeout(() => {
|
||||
setAnalyzeStep(2);
|
||||
setAnalyzePercent(50);
|
||||
setAnalyzeAction('正在识别语义段落...');
|
||||
}, 1500);
|
||||
|
||||
setTimeout(() => {
|
||||
setAnalyzePercent(70);
|
||||
setAnalyzeAction('正在识别第 2/3 个语义段落...');
|
||||
}, 2500);
|
||||
|
||||
setTimeout(() => {
|
||||
setAnalyzeStep(3);
|
||||
setAnalyzePercent(85);
|
||||
setAnalyzeAction('正在匹配法规条款...');
|
||||
}, 3500);
|
||||
|
||||
// Get result after analysis completes
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const result = await getComplianceResult(uploadRes.task_id);
|
||||
if ('segments' in result) {
|
||||
// Convert API response to frontend format
|
||||
const apiChunks: ComplianceChunk[] = result.segments.map(s => ({
|
||||
id: s.id,
|
||||
index: s.index,
|
||||
intent: s.intent,
|
||||
startPos: s.start_pos,
|
||||
endPos: s.end_pos,
|
||||
content: s.content,
|
||||
regulations: s.regulations.map(r => ({
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
clause: r.clause,
|
||||
score: r.score,
|
||||
matchKeyword: r.match_keyword,
|
||||
category: r.category as 'high' | 'medium' | 'low',
|
||||
fullContent: r.full_content,
|
||||
})),
|
||||
}));
|
||||
|
||||
// Calculate risk levels for each segment
|
||||
const chunksWithRisk = apiChunks.map(chunk => {
|
||||
const regs = chunk.regulations;
|
||||
const highRegs = regs.filter(r => r.category === 'high');
|
||||
const avgHighScore = highRegs.length > 0
|
||||
? highRegs.reduce((sum, r) => sum + r.score, 0) / highRegs.length
|
||||
: 1;
|
||||
|
||||
let riskLevel: 'high' | 'medium' | 'low' = 'low';
|
||||
if (avgHighScore < 0.85 || highRegs.filter(r => r.score < 0.9).length >= 1) {
|
||||
riskLevel = 'high';
|
||||
} else if (avgHighScore < 0.92 || regs.filter(r => r.category === 'medium').length >= 2) {
|
||||
riskLevel = 'medium';
|
||||
}
|
||||
|
||||
return { ...chunk, riskLevel } as ComplianceChunk;
|
||||
});
|
||||
|
||||
setChunks(chunksWithRisk);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get compliance result:', error);
|
||||
// Fallback to mock data
|
||||
setChunks(mockComplianceChunks);
|
||||
}
|
||||
|
||||
setAnalyzePercent(100);
|
||||
setAnalyzeAction('分析完成');
|
||||
setIsAnalyzing(false);
|
||||
}, 4500);
|
||||
} catch (error) {
|
||||
console.error('Failed to analyze document:', error);
|
||||
setIsAnalyzing(false);
|
||||
setChunks(mockComplianceChunks);
|
||||
}, 4500);
|
||||
// Fallback to mock data after delay
|
||||
setTimeout(() => {
|
||||
setChunks(mockComplianceChunks);
|
||||
}, 4500);
|
||||
}
|
||||
};
|
||||
|
||||
const openChat = (chunkId: number) => {
|
||||
@@ -180,27 +256,48 @@ export const CompliancePage: React.FC = () => {
|
||||
setChatInput('');
|
||||
setChatLoading(true);
|
||||
|
||||
setTimeout(() => {
|
||||
let response = '';
|
||||
const intent = chunk.intent;
|
||||
const mockResps = mockAIResponses[intent];
|
||||
let currentResponse = '';
|
||||
|
||||
if (chatInput.includes('合规') || chatInput.includes('符合')) {
|
||||
response = mockResps?.compliance || '根据相关法规分析,该段落的合规性需进一步评估。';
|
||||
} else if (chatInput.includes('解读') || chatInput.includes('什么') || chatInput.includes('如何')) {
|
||||
response = mockResps?.interpretation || '法规要求详细解读如下...';
|
||||
} else if (chatInput.includes('修改') || chatInput.includes('建议') || chatInput.includes('完善')) {
|
||||
response = mockResps?.suggestion || '建议进行以下修改以提升合规性...';
|
||||
} else {
|
||||
response = `关于您的问题,${chunk.intent}部分涉及以下法规要点:\n\n${chunk.regulations.slice(0, 2).map(r => `• ${r.name} ${r.clause}(相关性 ${Math.round(r.score * 100)}%)`).join('\n')}\n\n您可以进一步询问合规性评估或修改建议。`;
|
||||
complianceChat(
|
||||
activeChunkId,
|
||||
chatInput,
|
||||
(data: unknown) => {
|
||||
const sseData = data as { type: string; text?: string };
|
||||
if (sseData.type === 'chunk' && sseData.text) {
|
||||
currentResponse += sseData.text;
|
||||
setChatMessages(prev => ({
|
||||
...prev,
|
||||
[activeChunkId]: [...(prev[activeChunkId] || []).slice(0, -1), { id: Date.now() + 1, role: 'assistant', content: currentResponse }],
|
||||
}));
|
||||
} else if (sseData.type === 'done') {
|
||||
setChatLoading(false);
|
||||
}
|
||||
},
|
||||
(error: Error) => {
|
||||
console.error('Compliance chat error:', error);
|
||||
setChatLoading(false);
|
||||
// Fallback to mock response
|
||||
let response = '';
|
||||
const intent = chunk.intent;
|
||||
const mockResps = mockAIResponses[intent];
|
||||
if (chatInput.includes('合规') || chatInput.includes('符合')) {
|
||||
response = mockResps?.compliance || '根据相关法规分析,该段落的合规性需进一步评估。';
|
||||
} else if (chatInput.includes('解读') || chatInput.includes('什么') || chatInput.includes('如何')) {
|
||||
response = mockResps?.interpretation || '法规要求详细解读如下...';
|
||||
} else if (chatInput.includes('修改') || chatInput.includes('建议') || chatInput.includes('完善')) {
|
||||
response = mockResps?.suggestion || '建议进行以下修改以提升合规性...';
|
||||
} else {
|
||||
response = `关于您的问题,${chunk.intent}部分涉及以下法规要点:\n\n${chunk.regulations.slice(0, 2).map(r => `• ${r.name} ${r.clause}(相关性 ${Math.round(r.score * 100)}%)`).join('\n')}\n\n您可以进一步询问合规性评估或修改建议。`;
|
||||
}
|
||||
setChatMessages(prev => ({
|
||||
...prev,
|
||||
[activeChunkId]: [...(prev[activeChunkId] || []), { id: Date.now() + 1, role: 'assistant', content: response }],
|
||||
}));
|
||||
},
|
||||
() => {
|
||||
setChatLoading(false);
|
||||
}
|
||||
|
||||
setChatMessages(prev => ({
|
||||
...prev,
|
||||
[activeChunkId]: [...(prev[activeChunkId] || []), { id: Date.now() + 1, role: 'assistant', content: response }],
|
||||
}));
|
||||
setChatLoading(false);
|
||||
}, 1000);
|
||||
);
|
||||
};
|
||||
|
||||
const dashboard = calculateRiskDashboard(chunks);
|
||||
@@ -650,6 +747,7 @@ export const CompliancePage: React.FC = () => {
|
||||
<button
|
||||
onClick={startAnalysis}
|
||||
className="t-btn"
|
||||
type="button"
|
||||
style={{
|
||||
padding: '20px 48px',
|
||||
fontSize: 16,
|
||||
@@ -658,6 +756,8 @@ export const CompliancePage: React.FC = () => {
|
||||
border: 'none',
|
||||
borderRadius: 12,
|
||||
cursor: 'pointer',
|
||||
pointerEvents: 'auto',
|
||||
zIndex: 10,
|
||||
}}
|
||||
>开始分析</button>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user