242 lines
7.5 KiB
TypeScript
242 lines
7.5 KiB
TypeScript
import { Layout, theme, message } from 'antd';
|
|
import { useEffect, useState } from 'react';
|
|
import DatasetSidebar from './sidebar';
|
|
import DocumentList from './document-list';
|
|
import type { Dataset, Document } from '~/api/dify-dataset';
|
|
import { fetchDatasets, fetchDocuments } from '~/api/dify-dataset';
|
|
import '../../styles/components/dify-dataset-manager/index.css';
|
|
|
|
const { Content } = Layout;
|
|
|
|
/**
|
|
* 知识库管理主组件
|
|
*/
|
|
export default function DatasetManager() {
|
|
// 主题
|
|
const {
|
|
token: { colorBgContainer },
|
|
} = theme.useToken();
|
|
|
|
// 侧边栏状态
|
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
|
const [isMobile, setIsMobile] = useState(false);
|
|
|
|
// 知识库状态
|
|
const [datasets, setDatasets] = useState<Dataset[]>([]);
|
|
const [currentDatasetId, setCurrentDatasetId] = useState<string>('');
|
|
const [loadingDatasets, setLoadingDatasets] = 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 loadDatasets = async () => {
|
|
setLoadingDatasets(true);
|
|
try {
|
|
console.log('[DatasetManager] 加载知识库列表...');
|
|
const response = await fetchDatasets(1, 100);
|
|
console.log('[DatasetManager] 知识库列表响应:', response);
|
|
|
|
if (response && response.data) {
|
|
setDatasets(response.data);
|
|
|
|
// 如果有知识库,默认选中第一个
|
|
if (response.data.length > 0 && !currentDatasetId) {
|
|
setCurrentDatasetId(response.data[0].id);
|
|
}
|
|
}
|
|
} catch (err: any) {
|
|
console.error('[DatasetManager] 加载知识库列表失败:', err);
|
|
setError(err.message || '加载知识库列表失败');
|
|
message.error('加载知识库列表失败');
|
|
} finally {
|
|
setLoadingDatasets(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 handleDatasetSelect = (datasetId: string) => {
|
|
if (datasetId !== currentDatasetId) {
|
|
setCurrentDatasetId(datasetId);
|
|
setDocumentPage(1);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 处理文档页码变化
|
|
*/
|
|
const handlePageChange = (page: number) => {
|
|
loadDocuments(currentDatasetId, page);
|
|
};
|
|
|
|
/**
|
|
* 处理文档删除
|
|
*/
|
|
const handleDocumentDeleted = (documentId: string) => {
|
|
setDocuments((prev) => prev.filter((doc) => doc.id !== documentId));
|
|
setDocumentTotal((prev) => prev - 1);
|
|
|
|
// 更新知识库的文档数量
|
|
setDatasets((prev) =>
|
|
prev.map((ds) =>
|
|
ds.id === currentDatasetId
|
|
? { ...ds, document_count: ds.document_count - 1 }
|
|
: ds
|
|
)
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 处理文档状态变化
|
|
*/
|
|
const handleDocumentStatusChanged = (documentId: string, enabled: boolean) => {
|
|
setDocuments((prev) =>
|
|
prev.map((doc) =>
|
|
doc.id === documentId ? { ...doc, enabled } : doc
|
|
)
|
|
);
|
|
};
|
|
|
|
/**
|
|
* 刷新文档列表
|
|
*/
|
|
const handleRefresh = () => {
|
|
loadDocuments(currentDatasetId, documentPage);
|
|
};
|
|
|
|
/**
|
|
* 处理侧边栏切换
|
|
*/
|
|
const handleSidebarToggle = () => {
|
|
setSidebarCollapsed(!sidebarCollapsed);
|
|
};
|
|
|
|
// 初始化
|
|
useEffect(() => {
|
|
loadDatasets();
|
|
}, []);
|
|
|
|
// 当选中的知识库变化时,加载文档列表
|
|
useEffect(() => {
|
|
if (currentDatasetId) {
|
|
loadDocuments(currentDatasetId, 1);
|
|
}
|
|
}, [currentDatasetId]);
|
|
|
|
// 检查屏幕尺寸
|
|
useEffect(() => {
|
|
const checkScreenSize = () => {
|
|
setIsMobile(window.innerWidth < 992);
|
|
};
|
|
|
|
checkScreenSize();
|
|
window.addEventListener('resize', checkScreenSize);
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', checkScreenSize);
|
|
};
|
|
}, []);
|
|
|
|
// 获取当前选中的知识库
|
|
const currentDataset = datasets.find((ds) => ds.id === currentDatasetId);
|
|
|
|
// 如果有错误,显示错误页面
|
|
if (error && !inited) {
|
|
return (
|
|
<div className="dataset-manager-container">
|
|
<div className="dataset-empty">
|
|
<h3>加载失败</h3>
|
|
<p>{error}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Layout style={{ height: '100%', display: 'flex', flexDirection: 'row' }}>
|
|
{/* 移动端遮罩层 */}
|
|
{!sidebarCollapsed && isMobile && (
|
|
<div
|
|
className="fixed inset-0 bg-black bg-opacity-50 z-[999]"
|
|
onClick={handleSidebarToggle}
|
|
/>
|
|
)}
|
|
|
|
{/* 侧边栏 */}
|
|
<DatasetSidebar
|
|
collapsed={sidebarCollapsed}
|
|
onToggle={handleSidebarToggle}
|
|
datasets={datasets}
|
|
currentDatasetId={currentDatasetId}
|
|
onDatasetSelect={handleDatasetSelect}
|
|
loading={loadingDatasets}
|
|
/>
|
|
|
|
{/* 主内容区域 */}
|
|
<Layout style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
<Content
|
|
style={{
|
|
background: colorBgContainer,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
flex: 1,
|
|
minHeight: 0,
|
|
}}
|
|
>
|
|
<DocumentList
|
|
datasetId={currentDatasetId}
|
|
datasetName={currentDataset?.name || ''}
|
|
documents={documents}
|
|
loading={loadingDocuments}
|
|
total={documentTotal}
|
|
page={documentPage}
|
|
pageSize={documentPageSize}
|
|
onPageChange={handlePageChange}
|
|
onDocumentDeleted={handleDocumentDeleted}
|
|
onDocumentStatusChanged={handleDocumentStatusChanged}
|
|
onRefresh={handleRefresh}
|
|
/>
|
|
</Content>
|
|
</Layout>
|
|
</Layout>
|
|
);
|
|
}
|