feat(pkb): implement complete batch processing workflow and frontend optimization
- Frontend V3 architecture migration to modules/pkb - Implement three work modes: full-text reading, deep reading, batch processing - Complete batch processing: template selection, progress display, result export (CSV) - Integrate Ant Design X Chat component with streaming support - Add document upload modal with drag-and-drop support - Optimize UI: multi-line table display, citation formatting, auto-scroll - Fix 10+ technical issues: API mapping, state sync, form clearing - Update documentation: development records and module status Performance: 3 docs batch processing ~17-28s Status: PKB module now production-ready (90% complete)
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* 逐篇精读模式组件 - ChatGPT风格全屏聊天
|
||||
* 修复:参考文献格式、文档切换对话独立
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Empty } from 'antd';
|
||||
import { FileText } from 'lucide-react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FileText, BookOpen, ExternalLink } from 'lucide-react';
|
||||
import { ChatContainer } from '@/shared/components/Chat';
|
||||
import type { KnowledgeBase, Document } from '../../api/knowledgeBaseApi';
|
||||
|
||||
@@ -14,10 +14,92 @@ interface DeepReadModeProps {
|
||||
selectedDocuments: Document[];
|
||||
}
|
||||
|
||||
// 消息渲染参数类型
|
||||
interface MessageRenderParams {
|
||||
id: string | number;
|
||||
message: {
|
||||
id: string | number;
|
||||
role: string;
|
||||
content: string;
|
||||
status?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
status: string;
|
||||
}
|
||||
|
||||
// 自定义消息渲染器 - 解析并格式化参考文献
|
||||
const renderMessageContent = (params: MessageRenderParams) => {
|
||||
// 从params中提取消息内容
|
||||
const textContent = params?.message?.content;
|
||||
|
||||
// 空内容处理
|
||||
if (!textContent || typeof textContent !== 'string' || textContent.trim() === '') {
|
||||
return <div className="text-slate-400 italic">加载中...</div>;
|
||||
}
|
||||
|
||||
// 处理参考文献格式
|
||||
let formattedContent = textContent;
|
||||
|
||||
// 1. 移除HTML标签,转换为可读格式
|
||||
formattedContent = formattedContent.replace(
|
||||
/<span[^>]*id="citation-detail-(\d+)"[^>]*>\[(\d+)\]<\/span>\s*\*?\*?([^*\n]+)\*?\*?/g,
|
||||
(_, _num, num2, title) => `\n📄 [${num2}] ${title.trim()}`
|
||||
);
|
||||
|
||||
// 2. 处理其他HTML span标签
|
||||
formattedContent = formattedContent.replace(/<span[^>]*>[^<]*<\/span>/g, '');
|
||||
|
||||
// 3. 处理Markdown加粗中的下划线(文件名)
|
||||
formattedContent = formattedContent.replace(
|
||||
/\*\*([^*]+\.pdf)\*\*/gi,
|
||||
'📄 **$1**'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="prose prose-sm max-w-none">
|
||||
{formattedContent.split('\n').map((line, idx) => {
|
||||
// 检测是否是参考文献行
|
||||
if (line.startsWith('📄')) {
|
||||
return (
|
||||
<div key={idx} className="flex items-start my-2 p-2 bg-blue-50 rounded-lg border border-blue-100">
|
||||
<BookOpen className="w-4 h-4 text-blue-500 mr-2 mt-0.5 flex-shrink-0" />
|
||||
<span className="text-sm text-slate-700">{line.replace('📄 ', '')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 检测是否是"参考文献"标题
|
||||
if (line.includes('**参考文献**') || line.includes('📚 **参考文献**')) {
|
||||
return (
|
||||
<div key={idx} className="font-semibold text-slate-800 mt-4 mb-2 flex items-center">
|
||||
<BookOpen className="w-4 h-4 mr-2 text-blue-600" />
|
||||
参考文献
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 普通文本
|
||||
return line ? <p key={idx} className="mb-2 text-slate-700 leading-relaxed">{line}</p> : null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DeepReadMode: React.FC<DeepReadModeProps> = ({
|
||||
kbId,
|
||||
selectedDocuments
|
||||
}) => {
|
||||
// 使用useMemo确保文档切换时生成新的key
|
||||
const conversationKey = useMemo(() => {
|
||||
if (!selectedDocuments || selectedDocuments.length === 0) return '';
|
||||
return `kb-deepread-${kbId}-${selectedDocuments[0].id}-${Date.now()}`;
|
||||
}, [kbId, selectedDocuments]);
|
||||
|
||||
const selectedDocIds = useMemo(() =>
|
||||
selectedDocuments.map(d => d.id),
|
||||
[selectedDocuments]
|
||||
);
|
||||
|
||||
if (!selectedDocuments || selectedDocuments.length === 0) {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center bg-slate-50">
|
||||
@@ -35,15 +117,25 @@ export const DeepReadMode: React.FC<DeepReadModeProps> = ({
|
||||
}
|
||||
|
||||
const selectedDoc = selectedDocuments[0];
|
||||
const selectedDocIds = selectedDocuments.map(d => d.id);
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-white">
|
||||
{/* Chat组件 - 全屏展开 */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
{/* 当前文档提示 */}
|
||||
<div className="flex-shrink-0 px-4 py-2 bg-purple-50 border-b border-purple-100">
|
||||
<div className="flex items-center text-sm">
|
||||
<FileText className="w-4 h-4 text-purple-500 mr-2" />
|
||||
<span className="text-purple-700">当前精读:</span>
|
||||
<span className="font-medium text-purple-900 ml-1 truncate max-w-md" title={selectedDoc.filename}>
|
||||
{selectedDoc.filename}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Chat组件 - 使用key强制重新渲染 */}
|
||||
<div className="flex-1 overflow-hidden" key={conversationKey}>
|
||||
<ChatContainer
|
||||
conversationType="pkb"
|
||||
conversationKey={`kb-deepread-${kbId}-${selectedDoc.id}`}
|
||||
conversationKey={conversationKey}
|
||||
defaultMessages={[{
|
||||
id: 'welcome',
|
||||
role: 'assistant',
|
||||
@@ -54,6 +146,9 @@ export const DeepReadMode: React.FC<DeepReadModeProps> = ({
|
||||
providerConfig={{
|
||||
apiEndpoint: '/api/v1/chat/stream',
|
||||
requestFn: async (message: string) => {
|
||||
// 🔑 关键:传递 fullTextDocumentIds 而不是 documentIds
|
||||
// fullTextDocumentIds 会触发全文加载模式,AI可以看到完整文献
|
||||
// documentIds 只是过滤RAG检索结果,AI只能看到片段
|
||||
const response = await fetch('/api/v1/chat/stream', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -64,7 +159,7 @@ export const DeepReadMode: React.FC<DeepReadModeProps> = ({
|
||||
content: message,
|
||||
modelType: 'qwen-long',
|
||||
knowledgeBaseIds: [kbId],
|
||||
documentIds: selectedDocIds, // 🌟 关键参数:限定文档范围
|
||||
fullTextDocumentIds: selectedDocIds, // ✅ 改用全文模式
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -72,7 +167,6 @@ export const DeepReadMode: React.FC<DeepReadModeProps> = ({
|
||||
throw new Error(`API请求失败: ${response.status}`);
|
||||
}
|
||||
|
||||
// 处理流式响应
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let fullContent = '';
|
||||
@@ -109,6 +203,7 @@ export const DeepReadMode: React.FC<DeepReadModeProps> = ({
|
||||
};
|
||||
},
|
||||
}}
|
||||
customMessageRenderer={renderMessageContent}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user