267 lines
8.1 KiB
TypeScript
267 lines
8.1 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { message, Spin } from 'antd';
|
|
import DatasetLayout, { type MenuTab } from './layout';
|
|
import DocumentList from './document-list';
|
|
import DocumentDetail from './document-detail';
|
|
import RetrieveTest from './retrieve-test';
|
|
import DatasetSettings from './dataset-settings';
|
|
import type { Dataset } from '~/api/dify-dataset/type/datasetTypes';
|
|
import type { Document } from '~/api/dify-dataset/type/documentTypes';
|
|
import { fetchDatasets } from '~/api/dify-dataset/api/datasetApi';
|
|
import { fetchDocuments } from '~/api/dify-dataset/api/documentApi';
|
|
import '../../styles/components/dify-dataset-manager/index.css';
|
|
|
|
/**
|
|
* 知识库管理主组件
|
|
* 带左侧菜单栏的完整布局
|
|
*/
|
|
export default function DatasetManager() {
|
|
// 知识库状态
|
|
const [dataset, setDataset] = useState<Dataset | null>(null);
|
|
const [loadingDataset, setLoadingDataset] = useState(true);
|
|
|
|
// 文档状态
|
|
const [documents, setDocuments] = useState<Document[]>([]);
|
|
const [loadingDocuments, setLoadingDocuments] = useState(false);
|
|
const [documentTotal, setDocumentTotal] = useState(0);
|
|
const [documentPage, setDocumentPage] = useState(1);
|
|
const [documentPageSize] = useState(20);
|
|
|
|
// 初始化状态
|
|
const [inited, setInited] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// 菜单状态
|
|
const [activeTab, setActiveTab] = useState<MenuTab>('documents');
|
|
|
|
// 选中的文档(用于查看文档详情)
|
|
const [selectedDocument, setSelectedDocument] = useState<Document | null>(null);
|
|
|
|
/**
|
|
* 加载知识库(获取第一个知识库)
|
|
*/
|
|
const loadDataset = async () => {
|
|
setLoadingDataset(true);
|
|
try {
|
|
console.log('[DatasetManager] 加载知识库...');
|
|
const response = await fetchDatasets(1, 1);
|
|
console.log('[DatasetManager] 知识库响应:', response);
|
|
|
|
if (response && response.data && response.data.length > 0) {
|
|
const firstDataset = response.data[0];
|
|
setDataset(firstDataset);
|
|
// 立即加载文档
|
|
await loadDocuments(firstDataset.id, 1);
|
|
} else {
|
|
setError('未找到知识库,请先在Dify中创建知识库');
|
|
}
|
|
} catch (err: any) {
|
|
console.error('[DatasetManager] 加载知识库失败:', err);
|
|
setError(err.message || '加载知识库失败');
|
|
message.error('加载知识库失败');
|
|
} finally {
|
|
setLoadingDataset(false);
|
|
setInited(true);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 加载文档列表
|
|
*/
|
|
const loadDocuments = async (datasetId: string, page: number = 1) => {
|
|
if (!datasetId) return;
|
|
|
|
setLoadingDocuments(true);
|
|
try {
|
|
console.log('[DatasetManager] 加载文档列表:', { datasetId, page });
|
|
const response = await fetchDocuments(datasetId, page, documentPageSize);
|
|
console.log('[DatasetManager] 文档列表响应:', response);
|
|
|
|
if (response && response.data) {
|
|
setDocuments(response.data);
|
|
setDocumentTotal(response.total);
|
|
setDocumentPage(page);
|
|
}
|
|
} catch (err: any) {
|
|
console.error('[DatasetManager] 加载文档列表失败:', err);
|
|
message.error('加载文档列表失败');
|
|
} finally {
|
|
setLoadingDocuments(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 处理文档页码变化
|
|
*/
|
|
const handlePageChange = (page: number) => {
|
|
if (dataset) {
|
|
loadDocuments(dataset.id, page);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 处理文档删除
|
|
*/
|
|
const handleDocumentDeleted = (documentId: string) => {
|
|
setDocuments((prev) => prev.filter((doc) => doc.id !== documentId));
|
|
setDocumentTotal((prev) => prev - 1);
|
|
|
|
// 更新知识库的文档数量
|
|
if (dataset) {
|
|
setDataset({
|
|
...dataset,
|
|
document_count: dataset.document_count - 1
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 处理文档状态变化
|
|
*/
|
|
const handleDocumentStatusChanged = (documentId: string, enabled: boolean) => {
|
|
setDocuments((prev) =>
|
|
prev.map((doc) =>
|
|
doc.id === documentId ? { ...doc, enabled } : doc
|
|
)
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 刷新文档列表
|
|
*/
|
|
const handleRefresh = () => {
|
|
if (dataset) {
|
|
loadDocuments(dataset.id, documentPage);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 查看文档详情(分段管理)
|
|
*/
|
|
const handleViewDocument = (doc: Document) => {
|
|
console.log('[DatasetManager] 查看文档详情:', doc);
|
|
setSelectedDocument(doc);
|
|
};
|
|
|
|
/**
|
|
* 返回文档列表
|
|
*/
|
|
const handleBackToDocuments = () => {
|
|
setSelectedDocument(null);
|
|
};
|
|
|
|
/**
|
|
* 处理菜单切换
|
|
*/
|
|
const handleTabChange = (tab: MenuTab) => {
|
|
setActiveTab(tab);
|
|
// 切换菜单时清除选中的文档
|
|
if (tab !== 'documents') {
|
|
setSelectedDocument(null);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 处理知识库更新
|
|
*/
|
|
const handleDatasetUpdated = (updatedDataset: Dataset) => {
|
|
setDataset(updatedDataset);
|
|
};
|
|
|
|
// 初始化
|
|
useEffect(() => {
|
|
loadDataset();
|
|
}, []);
|
|
|
|
// 加载中状态
|
|
if (!inited || loadingDataset) {
|
|
return (
|
|
<div className="dataset-manager-wrapper">
|
|
<div className="dataset-loading-state">
|
|
<Spin size="large" />
|
|
<span className="loading-text">正在加载知识库...</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 错误状态
|
|
if (error) {
|
|
return (
|
|
<div className="dataset-manager-wrapper">
|
|
<div className="dataset-error-state">
|
|
<i className="ri-error-warning-line error-icon"></i>
|
|
<h3>加载失败</h3>
|
|
<p>{error}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 渲染右侧内容区
|
|
*/
|
|
const renderContent = () => {
|
|
// 文档菜单
|
|
if (activeTab === 'documents') {
|
|
// 如果选中了文档,显示文档详情
|
|
if (selectedDocument) {
|
|
return (
|
|
<DocumentDetail
|
|
datasetId={dataset?.id || ''}
|
|
document={selectedDocument}
|
|
/>
|
|
);
|
|
}
|
|
// 否则显示文档列表
|
|
return (
|
|
<DocumentList
|
|
datasetId={dataset?.id || ''}
|
|
datasetName={dataset?.name || ''}
|
|
documents={documents}
|
|
loading={loadingDocuments}
|
|
total={documentTotal}
|
|
page={documentPage}
|
|
pageSize={documentPageSize}
|
|
onPageChange={handlePageChange}
|
|
onDocumentDeleted={handleDocumentDeleted}
|
|
onDocumentStatusChanged={handleDocumentStatusChanged}
|
|
onRefresh={handleRefresh}
|
|
onViewDocument={handleViewDocument}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// 召回测试菜单
|
|
if (activeTab === 'retrieve') {
|
|
return <RetrieveTest datasetId={dataset?.id || ''} />;
|
|
}
|
|
|
|
// 设置菜单
|
|
if (activeTab === 'settings') {
|
|
return (
|
|
<DatasetSettings
|
|
dataset={dataset}
|
|
onDatasetUpdated={handleDatasetUpdated}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
return (
|
|
<div className="dataset-manager-wrapper">
|
|
<DatasetLayout
|
|
dataset={dataset}
|
|
activeTab={activeTab}
|
|
onTabChange={handleTabChange}
|
|
showBackButton={activeTab === 'documents' && !!selectedDocument}
|
|
onBack={handleBackToDocuments}
|
|
>
|
|
{renderContent()}
|
|
</DatasetLayout>
|
|
</div>
|
|
);
|
|
}
|