- Implement knowledge base list, create, and detail pages - Add dialog provider and components for confirmation and alerts - Include knowledge card and grid view components - Enhance header with user menu and logout functionality - Implement knowledge operations hooks for CRUD operations
275 lines
7.7 KiB
TypeScript
275 lines
7.7 KiB
TypeScript
import React, { useState, useCallback, useMemo } from 'react';
|
||
import {
|
||
Box,
|
||
Typography,
|
||
TextField,
|
||
Select,
|
||
MenuItem,
|
||
FormControl,
|
||
InputLabel,
|
||
Button,
|
||
InputAdornment,
|
||
Pagination,
|
||
Stack,
|
||
CircularProgress,
|
||
} from '@mui/material';
|
||
import {
|
||
Search as SearchIcon,
|
||
Add as AddIcon,
|
||
Refresh as RefreshIcon,
|
||
} from '@mui/icons-material';
|
||
import { useNavigate } from 'react-router-dom';
|
||
import { useKnowledgeList, useKnowledgeOperations } from '@/hooks/knowledge_hooks';
|
||
import { useUserData } from '@/hooks/useUserData';
|
||
import KnowledgeGridView from '@/components/knowledge/KnowledgeGridView';
|
||
import type { IKnowledge } from '@/interfaces/database/knowledge';
|
||
import { useDialog } from '@/hooks/useDialog';
|
||
|
||
const KnowledgeBaseList: React.FC = () => {
|
||
const navigate = useNavigate();
|
||
|
||
const {deleteKnowledge} = useKnowledgeOperations();
|
||
|
||
// 搜索和筛选状态
|
||
const [searchTerm, setSearchTerm] = useState('');
|
||
const [teamFilter, setTeamFilter] = useState('all');
|
||
const pageSize = 12; // 每页显示12个知识库
|
||
|
||
// 获取用户数据(包含租户列表)
|
||
const { tenantList } = useUserData();
|
||
|
||
// 使用knowledge_hooks获取数据
|
||
const {
|
||
knowledgeBases,
|
||
total,
|
||
loading,
|
||
error,
|
||
currentPage,
|
||
setKeywords,
|
||
setCurrentPage,
|
||
refresh,
|
||
} = useKnowledgeList({
|
||
keywords: searchTerm,
|
||
page: 1,
|
||
page_size: pageSize,
|
||
});
|
||
|
||
// 处理搜索
|
||
const handleSearch = useCallback((value: string) => {
|
||
setSearchTerm(value);
|
||
setKeywords(value);
|
||
}, [setKeywords]);
|
||
|
||
// 处理团队筛选
|
||
const handleTeamFilterChange = useCallback((value: string) => {
|
||
setTeamFilter(value);
|
||
setCurrentPage(1); // 筛选时重置到第一页
|
||
}, [setCurrentPage]);
|
||
|
||
// 处理分页变化
|
||
const handlePageChange = useCallback((_: React.ChangeEvent<unknown>, page: number) => {
|
||
setCurrentPage(page);
|
||
}, [setCurrentPage]);
|
||
|
||
// 处理刷新
|
||
const handleRefresh = useCallback(() => {
|
||
refresh();
|
||
}, [refresh]);
|
||
|
||
// 处理创建知识库
|
||
const handleCreateKnowledge = useCallback(() => {
|
||
navigate('/knowledge/create');
|
||
}, [navigate]);
|
||
|
||
const dialog = useDialog();
|
||
|
||
// 处理删除知识库
|
||
const handleDeleteKnowledge = useCallback(async (kb: IKnowledge) => {
|
||
// 需要确认删除
|
||
dialog.confirm({
|
||
title: '确认删除',
|
||
content: `是否确认删除知识库 ${kb.name}?`,
|
||
onConfirm: async () => {
|
||
try {
|
||
await deleteKnowledge(kb.id);
|
||
// 删除成功后刷新列表
|
||
refresh();
|
||
} catch (err) {
|
||
console.error('Failed to delete knowledge:', err);
|
||
// 可以添加错误提示
|
||
}
|
||
},
|
||
});
|
||
}, [deleteKnowledge, refresh, dialog]);
|
||
|
||
// 处理查看知识库详情
|
||
const handleViewKnowledge = useCallback((kb: IKnowledge) => {
|
||
navigate(`/knowledge/${kb.id}`);
|
||
}, [navigate]);
|
||
|
||
// 根据团队筛选过滤知识库
|
||
const filteredKnowledgeBases = useMemo(() => {
|
||
if (!knowledgeBases) return [];
|
||
|
||
if (teamFilter === 'all') {
|
||
return knowledgeBases;
|
||
}
|
||
|
||
// 根据租户ID筛选
|
||
if (teamFilter !== 'all') {
|
||
return knowledgeBases.filter(kb => {
|
||
// 如果选择的是特定租户ID,则筛选该租户的知识库
|
||
if (teamFilter === 'me') {
|
||
// 假设'me'表示当前用户的知识库,这里可以根据实际业务逻辑调整
|
||
return kb.permission === 'me' || kb.tenant_id === tenantList?.[0].tenant_id;
|
||
}
|
||
// 如果是具体的租户ID
|
||
return kb.tenant_id === teamFilter;
|
||
});
|
||
}
|
||
|
||
return knowledgeBases;
|
||
}, [knowledgeBases, teamFilter, tenantList]);
|
||
|
||
// 计算总页数(基于筛选后的结果)
|
||
const totalPages = Math.ceil(filteredKnowledgeBases.length / pageSize);
|
||
|
||
// 获取当前页的数据
|
||
const currentPageData = useMemo(() => {
|
||
const startIndex = (currentPage - 1) * pageSize;
|
||
const endIndex = startIndex + pageSize;
|
||
return filteredKnowledgeBases.slice(startIndex, endIndex);
|
||
}, [filteredKnowledgeBases, currentPage, pageSize]);
|
||
|
||
// 构建团队筛选选项
|
||
const teamFilterOptions = useMemo(() => {
|
||
const options = [
|
||
{ value: 'all', label: '全部' },
|
||
];
|
||
|
||
// 添加租户选项
|
||
if (tenantList && tenantList.length > 0) {
|
||
tenantList.forEach(tenant => {
|
||
options.push({
|
||
value: tenant.tenant_id,
|
||
label: tenant.nickname || tenant.tenant_id,
|
||
});
|
||
});
|
||
}
|
||
|
||
return options;
|
||
}, [tenantList]);
|
||
|
||
return (
|
||
<Box sx={{ p: 3 }}>
|
||
{/* 页面标题 */}
|
||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||
<Typography variant="h4" fontWeight={600}>
|
||
知识库管理
|
||
</Typography>
|
||
<Button
|
||
variant="contained"
|
||
startIcon={<AddIcon />}
|
||
onClick={handleCreateKnowledge}
|
||
sx={{ borderRadius: 2 }}
|
||
>
|
||
新建知识库
|
||
</Button>
|
||
</Box>
|
||
|
||
{/* 搜索和筛选区域 */}
|
||
<Box sx={{ display: 'flex', gap: 2, mb: 3, alignItems: 'center' }}>
|
||
<TextField
|
||
placeholder="搜索知识库..."
|
||
value={searchTerm}
|
||
onChange={(e) => handleSearch(e.target.value)}
|
||
sx={{ flex: 1, maxWidth: 400 }}
|
||
InputProps={{
|
||
startAdornment: (
|
||
<InputAdornment position="start">
|
||
<SearchIcon />
|
||
</InputAdornment>
|
||
),
|
||
}}
|
||
/>
|
||
|
||
<FormControl sx={{ minWidth: 120 }}>
|
||
<InputLabel>团队筛选</InputLabel>
|
||
<Select
|
||
value={teamFilter}
|
||
label="团队筛选"
|
||
onChange={(e) => handleTeamFilterChange(e.target.value)}
|
||
>
|
||
{teamFilterOptions.map(option => (
|
||
<MenuItem key={option.value} value={option.value}>
|
||
{option.label}
|
||
</MenuItem>
|
||
))}
|
||
</Select>
|
||
</FormControl>
|
||
|
||
<Button
|
||
variant="outlined"
|
||
startIcon={<RefreshIcon />}
|
||
onClick={handleRefresh}
|
||
disabled={loading}
|
||
>
|
||
刷新
|
||
</Button>
|
||
</Box>
|
||
|
||
{/* 错误提示 */}
|
||
{error && (
|
||
<Box sx={{ mb: 3, p: 2, bgcolor: 'error.light', borderRadius: 1 }}>
|
||
<Typography color="error">
|
||
加载知识库列表失败: {error}
|
||
</Typography>
|
||
</Box>
|
||
)}
|
||
|
||
{/* 加载状态 */}
|
||
{loading && (
|
||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
|
||
<CircularProgress />
|
||
</Box>
|
||
)}
|
||
|
||
{/* 知识库列表 */}
|
||
{!loading && (
|
||
<>
|
||
<KnowledgeGridView
|
||
knowledgeBases={currentPageData}
|
||
onView={handleViewKnowledge}
|
||
onDelete={handleDeleteKnowledge}
|
||
loading={loading}
|
||
searchTerm={searchTerm}
|
||
teamFilter={teamFilter}
|
||
onCreateKnowledge={handleCreateKnowledge}
|
||
/>
|
||
|
||
{/* 分页组件 */}
|
||
{totalPages > 1 && (
|
||
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}>
|
||
<Stack spacing={2}>
|
||
<Pagination
|
||
count={totalPages}
|
||
page={currentPage}
|
||
onChange={handlePageChange}
|
||
color="primary"
|
||
size="large"
|
||
showFirstButton
|
||
showLastButton
|
||
/>
|
||
<Typography variant="body2" color="text.secondary" textAlign="center">
|
||
共 {filteredKnowledgeBases.length} 个知识库,第 {currentPage} 页,共 {totalPages} 页
|
||
</Typography>
|
||
</Stack>
|
||
</Box>
|
||
)}
|
||
</>
|
||
)}
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
export default KnowledgeBaseList; |