diff --git a/src/pages/certificate-management/index.tsx b/src/pages/certificate-management/index.tsx new file mode 100644 index 0000000..601d270 --- /dev/null +++ b/src/pages/certificate-management/index.tsx @@ -0,0 +1,475 @@ +import { useState, useEffect } from 'react' +import { + Card, + Table, + Button, + Modal, + Form, + Input, + DatePicker, + Select, + Space, + Tag, + message, + Popconfirm, + Typography, + Row, + Col, + Statistic, + InputNumber +} from 'antd' +import { + PlusOutlined, + EditOutlined, + DeleteOutlined, + ExclamationCircleOutlined, + SafetyCertificateOutlined, + SearchOutlined +} from '@ant-design/icons' +import dayjs from 'dayjs' + +const { Title, Paragraph } = Typography +const { Option } = Select +const { RangePicker } = DatePicker + +interface Certificate { + id: string + name: string + type: string + vendor: string + issueDate: dayjs.Dayjs + expiryDate: dayjs.Dayjs + status: 'active' | 'expired' | 'expiring_soon' + description: string + certificateNo: string +} + +const CertificateManagement: React.FC = () => { + const [certificates, setCertificates] = useState([]) + const [isModalVisible, setIsModalVisible] = useState(false) + const [editingCertificate, setEditingCertificate] = useState(null) + const [searchText, setSearchText] = useState('') + const [form] = Form.useForm() + + // 模拟初始数据 + useEffect(() => { + const initialData: Certificate[] = [ + { + id: '1', + name: 'Cisco CCNP Enterprise', + type: '厂商认证', + vendor: 'Cisco', + issueDate: dayjs('2023-01-15'), + expiryDate: dayjs('2026-01-15'), + status: 'active', + description: '思科网络专业认证', + certificateNo: 'CSCO-2023-001234' + }, + { + id: '2', + name: 'AWS Solutions Architect', + type: '云平台认证', + vendor: 'Amazon', + issueDate: dayjs('2023-06-01'), + expiryDate: dayjs('2025-06-01'), + status: 'expiring_soon', + description: 'AWS解决方案架构师认证', + certificateNo: 'AWS-2023-005678' + }, + { + id: '3', + name: 'VMware VCP-DCV', + type: '虚拟化认证', + vendor: 'VMware', + issueDate: dayjs('2022-03-20'), + expiryDate: dayjs('2024-03-20'), + status: 'expired', + description: 'VMware数据中心虚拟化认证', + certificateNo: 'VMW-2022-009012' + }, + { + id: '4', + name: 'Microsoft Azure Administrator', + type: '云平台认证', + vendor: 'Microsoft', + issueDate: dayjs('2024-01-10'), + expiryDate: dayjs('2027-01-10'), + status: 'active', + description: 'Azure管理员认证', + certificateNo: 'MSFT-2024-003456' + }, + { + id: '5', + name: 'PMP项目管理专业人士', + type: '项目管理认证', + vendor: 'PMI', + issueDate: dayjs('2023-08-15'), + expiryDate: dayjs('2026-08-15'), + status: 'active', + description: '项目管理专业人士认证', + certificateNo: 'PMI-2023-007890' + } + ] + setCertificates(initialData) + }, []) + + // 计算统计数据 + const stats = { + total: certificates.length, + active: certificates.filter(c => c.status === 'active').length, + expiringSoon: certificates.filter(c => c.status === 'expiring_soon').length, + expired: certificates.filter(c => c.status === 'expired').length + } + + // 获取状态标签 + const getStatusTag = (status: string) => { + const config = { + active: { color: 'green', text: '有效' }, + expiring_soon: { color: 'orange', text: '即将过期' }, + expired: { color: 'red', text: '已过期' } + } + const { color, text } = config[status as keyof typeof config] + return {text} + } + + // 计算剩余天数 + const getDaysRemaining = (expiryDate: dayjs.Dayjs) => { + const days = expiryDate.diff(dayjs(), 'day') + return days + } + + // 打开新增/编辑弹窗 + const showModal = (certificate?: Certificate) => { + if (certificate) { + setEditingCertificate(certificate) + form.setFieldsValue({ + ...certificate, + dateRange: [certificate.issueDate, certificate.expiryDate] + }) + } else { + setEditingCertificate(null) + form.resetFields() + } + setIsModalVisible(true) + } + + // 提交表单 + const handleSubmit = async () => { + try { + const values = await form.validateFields() + const [issueDate, expiryDate] = values.dateRange + + const certificateData: Certificate = { + id: editingCertificate?.id || Date.now().toString(), + name: values.name, + type: values.type, + vendor: values.vendor, + issueDate, + expiryDate, + description: values.description, + certificateNo: values.certificateNo, + status: calculateStatus(expiryDate) + } + + if (editingCertificate) { + setCertificates(prev => + prev.map(c => c.id === editingCertificate.id ? certificateData : c) + ) + message.success('证书更新成功') + } else { + setCertificates(prev => [...prev, certificateData]) + message.success('证书添加成功') + } + + setIsModalVisible(false) + form.resetFields() + } catch (error) { + console.error('表单验证失败:', error) + } + } + + // 计算状态 + const calculateStatus = (expiryDate: dayjs.Dayjs): Certificate['status'] => { + const days = expiryDate.diff(dayjs(), 'day') + if (days < 0) return 'expired' + if (days <= 90) return 'expiring_soon' + return 'active' + } + + // 删除证书 + const handleDelete = (id: string) => { + setCertificates(prev => prev.filter(c => c.id !== id)) + message.success('证书删除成功') + } + + // 搜索过滤 + const filteredCertificates = certificates.filter(cert => + cert.name.toLowerCase().includes(searchText.toLowerCase()) || + cert.vendor.toLowerCase().includes(searchText.toLowerCase()) || + cert.certificateNo.toLowerCase().includes(searchText.toLowerCase()) + ) + + // 表格列定义 + const columns = [ + { + title: '证书名称', + dataIndex: 'name', + key: 'name', + render: (text: string) => {text} + }, + { + title: '证书编号', + dataIndex: 'certificateNo', + key: 'certificateNo', + width: 180 + }, + { + title: '类型', + dataIndex: 'type', + key: 'type', + width: 120, + render: (type: string) => ( + {type} + ) + }, + { + title: '厂商', + dataIndex: 'vendor', + key: 'vendor', + width: 120 + }, + { + title: '颁发日期', + dataIndex: 'issueDate', + key: 'issueDate', + width: 120, + render: (date: dayjs.Dayjs) => date.format('YYYY-MM-DD') + }, + { + title: '到期日期', + dataIndex: 'expiryDate', + key: 'expiryDate', + width: 120, + render: (date: dayjs.Dayjs) => { + const days = getDaysRemaining(date) + return ( + + {date.format('YYYY-MM-DD')} + {days > 0 && days <= 90 && ( + {days}天 + )} + {days <= 0 && ( + 已过期 + )} + + ) + } + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + width: 100, + render: (status: string) => getStatusTag(status) + }, + { + title: '备注', + dataIndex: 'description', + key: 'description', + ellipsis: true + }, + { + title: '操作', + key: 'action', + width: 150, + render: (_: unknown, record: Certificate) => ( + + + + } + > + `共 ${total} 条记录` + }} + /> + + + {/* 新增/编辑弹窗 */} + { + setIsModalVisible(false) + form.resetFields() + }} + width={600} + > +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default CertificateManagement diff --git a/src/router/index.tsx b/src/router/index.tsx index 231190a..a9ab59a 100644 --- a/src/router/index.tsx +++ b/src/router/index.tsx @@ -30,7 +30,7 @@ const router: RouteObject[] = [ children: [ { path: '/', - element: LazyLoad(lazy(() => import('@/pages/home'))) + element: LazyLoad(lazy(() => import('@/pages/certificate-management'))) }, { path: '/test1',