-
知识库管理
-
- {/* Stats */}
- {stats && (
-
- {statCardDefs.map((s, i) => (
-
-
- {typeof s.value === 'number' ? s.value.toLocaleString() : s.value}
-
-
{s.label}
-
- ))}
-
- )}
-
- {/* Toolbar */}
-
- setKeyword(e.target.value)}
- style={inputStyle} />
-
-
-
-
-
-
- {/* Table */}
-
-
-
-
-
- | 名称 | 来源 | 文档数 | 存储 | 状态 | 同步 | 创建时间 | 操作 |
-
-
-
- {loading ? (
- | 加载中... |
- ) : knowledgeBases.length === 0 ? (
- | 暂无知识库数据 |
- ) : knowledgeBases.map(kb => (
-
- |
- {kb.name}
- {kb.description && {kb.description.substring(0, 60)} }
- |
- {kb.is_official ? 官方 : {kb.owner_name || '用户'}} |
- {kb.document_count} |
- {kb.total_storage_mb || 0} MB |
- {statusBadge(kb.status)} |
- {syncStatusBadge(kb.external_sync_status)} |
- {kb.created_at?.substring(0, 10)} |
-
-
-
-
-
- |
-
- ))}
-
-
-
-
-
- {/* Pagination */}
- {totalPages > 1 && (
-
-
- 第 {page}/{totalPages} 页 (共 {total} 条)
-
-
- )}
-
- {/* KB Modal */}
- {showModal && (
-
setShowModal(false)}>
-
e.stopPropagation()}>
-
{editingKB ? '编辑知识库' : '创建知识库'}
-
-
- setKBForm({ ...kbForm, name: e.target.value })} style={fullInput} />
-
-
-
-
-
-
-
-
-
-
-
-
-
- )}
-
- {/* Detail Drawer */}
- {showDetail && selectedKB && (
-
setShowDetail(false)}>
-
e.stopPropagation()}>
-
-
{selectedKB.name}
-
-
-
-
-
来源: {selectedKB.is_official ? '官方' : selectedKB.owner_name || '用户'}
- {selectedKB.description &&
描述: {selectedKB.description}
}
-
状态: {statusBadge(selectedKB.status)} | 文档: {selectedKB.document_count} | 存储: {selectedKB.total_storage_mb || 0} MB
-
-
- {/* Upload */}
-
-
-
{ const f = e.target.files?.[0]; if (f) handleFileUpload(selectedKB.id, f); e.target.value = ''; }}
- accept=".pdf,.docx,.doc,.txt,.md,.csv,.json,.xml" />
-
-
支持 PDF / DOCX / TXT / MD / CSV / JSON
-
- {uploadMsg && (
-
- {uploadMsg}
-
- )}
-
-
- {/* Graph Stats */}
- {selectedKB.graph_stats && (selectedKB.graph_stats.nodes > 0 || selectedKB.graph_stats.edges > 0) && (
-
-
知识图谱
-
节点数: {selectedKB.graph_stats.nodes} | 边数: {selectedKB.graph_stats.edges}
-
- )}
-
- {/* External Integration */}
- {selectedKB.external_service_id && (
-
-
外部集成
-
同步状态: {syncStatusBadge(selectedKB.external_sync_status)}
-
外部ID: {selectedKB.external_id || '未关联'}
-
- )}
-
- {/* Documents */}
-
文档列表 ({detailDocTotal})
-
-
-
-
- | 文件名 | 类型 | 大小 | 状态 | 操作 |
-
-
-
- {detailDocs.map(doc => (
-
- | {doc.filename || doc.file_name} |
- {doc.file_type || '-'} |
- {formatSize(doc.file_size || 0)} |
- {statusBadge(doc.status)} |
-
-
-
- |
-
- ))}
- {detailDocs.length === 0 && (
- | 暂无文档,请上传 |
- )}
-
-
-
-
-
- )}
-
- {/* Document Preview Modal */}
- {previewDoc && (
-
{ setPreviewDoc(null); setPreviewContent(''); }}>
-
e.stopPropagation()}>
-
-
{previewDoc.filename || previewDoc.file_name}
-
-
-
- 类型: {previewDoc.file_type || '-'} | 大小: {formatSize(previewDoc.file_size || 0)}
-
-
- {previewContent}
-
-
-
- )}
-
- {/* External Service Modal */}
- {showExtModal && (
-
setShowExtModal(false)}>
-
e.stopPropagation()}>
-
外部知识库服务配置
- {extServices.length > 0 && (
-
-
- | 名称 | 类型 | URL | 状态 | 操作 |
-
- {extServices.map(svc => (
-
- | {svc.name} |
- {svc.service_type === 'fastgpt' ? 'FastGPT' : svc.service_type === 'dify' ? 'Dify' : 'RAGFlow'} |
- {svc.api_url} |
- {statusBadge(svc.status)} |
-
-
-
- |
-
- ))}
-
-
-
- )}
-
-
添加新服务
-
-
setExtForm({ ...extForm, name: e.target.value })} placeholder="如: 生产环境 FastGPT" style={fullInput} />
-
-
-
setExtForm({ ...extForm, api_url: e.target.value })} placeholder="https://your-instance.com/api/v1" style={fullInput} />
-
setExtForm({ ...extForm, api_key: e.target.value })} placeholder="Bearer Token 或 API Key" style={fullInput} />
-
-
-
-
-
-
-
- )}
-
- );
-};
-
-// Shared styles
-const inputStyle: React.CSSProperties = { padding: '8px 12px', border: '1px solid #D1D5DB', borderRadius: 6, fontSize: 13, width: 200, boxSizing: 'border-box' };
-const selectStyle: React.CSSProperties = { padding: '8px', border: '1px solid #D1D5DB', borderRadius: 6, fontSize: 13, background: '#fff' };
-const fullInput: React.CSSProperties = { width: '100%', padding: '8px 12px', border: '1px solid #D1D5DB', borderRadius: 6, fontSize: 13, boxSizing: 'border-box' };
-const labelStyle: React.CSSProperties = { display: 'block', marginBottom: 4, fontWeight: 600, fontSize: 13, color: '#374151' };
-const overlayStyle: React.CSSProperties = { position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' };
-const modalStyle: React.CSSProperties = { background: '#fff', borderRadius: 12, padding: 24, width: 500, maxWidth: '90vw', maxHeight: '90vh', overflow: 'auto' };
-const drawerStyle: React.CSSProperties = { background: '#fff', borderRadius: 12, padding: 24, width: 560, maxWidth: '90vw', maxHeight: '85vh', overflow: 'auto' };
-
-const btnStyle = (color: string, size: 'normal' | 'small' = 'normal'): React.CSSProperties => ({
- background: color, color: '#fff', border: 'none', borderRadius: 6, cursor: 'pointer',
- fontSize: size === 'small' ? 12 : 14, padding: size === 'small' ? '4px 10px' : '8px 16px',
- fontWeight: 500, display: 'inline-flex', alignItems: 'center', gap: 4, whiteSpace: 'nowrap',
-});
-
-export default Knowledge;
+import React, { useState, useEffect, useRef } from 'react';
+import { adminAPI } from '@/api';
+
+interface KnowledgeBaseItem {
+ id: string;
+ name: string;
+ user_id: string;
+ owner_name?: string;
+ description?: string;
+ directory_path: string;
+ document_count: number;
+ knowledge_graph_count: number;
+ is_official: boolean;
+ status: string;
+ external_service_id?: string;
+ external_id?: string;
+ external_sync_status: string;
+ external_config?: any;
+ total_storage_bytes?: number;
+ total_storage_mb?: number;
+ created_at: string;
+ updated_at: string;
+ doc_stats?: Record