refactor(layout): migrate styled components to sx prop in Header and Sidebar
feat(pages): add new KnowledgeBaseList and Login pages with complete functionality feat(services): implement knowledge service with comprehensive API methods feat(hooks): add login hooks for authentication and form management
This commit is contained in:
265
src/pages/knowledge/KnowledgeBaseList.tsx
Normal file
265
src/pages/knowledge/KnowledgeBaseList.tsx
Normal file
@@ -0,0 +1,265 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
Grid,
|
||||
Chip,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
TextField,
|
||||
InputAdornment,
|
||||
Fab,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Search as SearchIcon,
|
||||
Add as AddIcon,
|
||||
MoreVert as MoreVertIcon,
|
||||
Folder as FolderIcon,
|
||||
Description as DocumentIcon,
|
||||
} from '@mui/icons-material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const PageContainer = styled(Box)(({ theme }) => ({
|
||||
padding: '1.5rem',
|
||||
backgroundColor: '#F8F9FA',
|
||||
minHeight: 'calc(100vh - 60px)',
|
||||
}));
|
||||
|
||||
const PageHeader = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: '1.5rem',
|
||||
}));
|
||||
|
||||
const SearchContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: '1rem',
|
||||
marginBottom: '1.5rem',
|
||||
}));
|
||||
|
||||
const KBCard = styled(Card)(({ theme }) => ({
|
||||
height: '100%',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
border: '1px solid #E5E5E5',
|
||||
'&:hover': {
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
||||
transform: 'translateY(-2px)',
|
||||
},
|
||||
}));
|
||||
|
||||
const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({
|
||||
fontSize: '0.75rem',
|
||||
height: '24px',
|
||||
backgroundColor:
|
||||
status === 'active' ? '#E8F5E8' :
|
||||
status === 'processing' ? '#FFF3CD' : '#F8D7DA',
|
||||
color:
|
||||
status === 'active' ? '#155724' :
|
||||
status === 'processing' ? '#856404' : '#721C24',
|
||||
}));
|
||||
|
||||
const StatsBox = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: '1rem',
|
||||
padding: '0.75rem',
|
||||
backgroundColor: '#F8F9FA',
|
||||
borderRadius: '6px',
|
||||
}));
|
||||
|
||||
const StatItem = styled(Box)(({ theme }) => ({
|
||||
textAlign: 'center',
|
||||
'& .number': {
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 600,
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
'& .label': {
|
||||
fontSize: '0.75rem',
|
||||
color: '#666',
|
||||
marginTop: '0.25rem',
|
||||
},
|
||||
}));
|
||||
|
||||
const mockKnowledgeBases = [
|
||||
{
|
||||
id: 1,
|
||||
name: '产品文档库',
|
||||
description: '包含所有产品相关的技术文档和用户手册',
|
||||
status: 'active',
|
||||
documents: 156,
|
||||
size: '2.3 GB',
|
||||
lastUpdated: '2024-01-15',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '客服FAQ',
|
||||
description: '常见问题解答和客服对话记录',
|
||||
status: 'processing',
|
||||
documents: 89,
|
||||
size: '1.1 GB',
|
||||
lastUpdated: '2024-01-14',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '法律合规',
|
||||
description: '法律条文、合规要求和相关政策文档',
|
||||
status: 'active',
|
||||
documents: 234,
|
||||
size: '3.7 GB',
|
||||
lastUpdated: '2024-01-13',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '培训资料',
|
||||
description: '员工培训材料和学习资源',
|
||||
status: 'inactive',
|
||||
documents: 67,
|
||||
size: '890 MB',
|
||||
lastUpdated: '2024-01-10',
|
||||
},
|
||||
];
|
||||
|
||||
const KnowledgeBaseList: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const [selectedKB, setSelectedKB] = useState<number | null>(null);
|
||||
|
||||
const handleMenuClick = (event: React.MouseEvent<HTMLElement>, kbId: number) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setSelectedKB(kbId);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
setSelectedKB(null);
|
||||
};
|
||||
|
||||
const filteredKBs = mockKnowledgeBases.filter(kb =>
|
||||
kb.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
kb.description.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageHeader>
|
||||
<Typography variant="h4" fontWeight={600} color="#333">
|
||||
知识库管理
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddIcon />}
|
||||
sx={{ borderRadius: '6px' }}
|
||||
>
|
||||
新建知识库
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
<SearchContainer>
|
||||
<TextField
|
||||
placeholder="搜索知识库..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon color="action" />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
sx={{ width: '400px' }}
|
||||
/>
|
||||
</SearchContainer>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
{filteredKBs.map((kb) => (
|
||||
<Grid key={kb.id} size={{xs:12, sm:6, md:4}}>
|
||||
<KBCard>
|
||||
<CardContent>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="flex-start">
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<FolderIcon color="primary" />
|
||||
<Typography variant="h6" fontWeight={600}>
|
||||
{kb.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<StatusChip
|
||||
status={kb.status}
|
||||
label={
|
||||
kb.status === 'active' ? '活跃' :
|
||||
kb.status === 'processing' ? '处理中' : '未激活'
|
||||
}
|
||||
size="small"
|
||||
/>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(e) => handleMenuClick(e, kb.id)}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{ mt: 1, mb: 2 }}
|
||||
>
|
||||
{kb.description}
|
||||
</Typography>
|
||||
|
||||
<StatsBox>
|
||||
<StatItem>
|
||||
<div className="number">{kb.documents}</div>
|
||||
<div className="label">文档数量</div>
|
||||
</StatItem>
|
||||
<StatItem>
|
||||
<div className="number">{kb.size}</div>
|
||||
<div className="label">存储大小</div>
|
||||
</StatItem>
|
||||
</StatsBox>
|
||||
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
|
||||
最后更新: {kb.lastUpdated}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</KBCard>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
>
|
||||
<MenuItem onClick={handleMenuClose}>编辑</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose}>查看详情</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose}>导出</MenuItem>
|
||||
<MenuItem onClick={handleMenuClose} sx={{ color: 'error.main' }}>
|
||||
删除
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<Fab
|
||||
color="primary"
|
||||
aria-label="add"
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
bottom: 24,
|
||||
right: 24,
|
||||
}}
|
||||
>
|
||||
<AddIcon />
|
||||
</Fab>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default KnowledgeBaseList;
|
||||
Reference in New Issue
Block a user