diff --git a/apps/admin/src/pages/Students/index.tsx b/apps/admin/src/pages/Students/index.tsx index a4f07fb..430ba07 100644 --- a/apps/admin/src/pages/Students/index.tsx +++ b/apps/admin/src/pages/Students/index.tsx @@ -12,6 +12,10 @@ import { Popconfirm, Upload, App, + Row, + Col, + Card, + Descriptions, } from 'antd'; import { PlusOutlined, @@ -43,6 +47,25 @@ const statusMap: Record = { archived: { text: '已归档', color: '#999' }, }; +interface EnrollmentInfo { + classId: number; + className: string; + classType: string; + startDate: string; + endDate: string; + joinDate: string; + leaveDate: string; + status: string; + attendanceStats: { + total: number; + present: number; + absent: number; + late: number; + leave: number; + rate: number; + }; +} + const StudentsPage: React.FC = () => { const { modal } = App.useApp(); const [data, setData] = useState([]); @@ -54,6 +77,7 @@ const StudentsPage: React.FC = () => { const [showArchived, setShowArchived] = useState(false); const [archivedCount, setArchivedCount] = useState(0); const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [enrollmentData, setEnrollmentData] = useState>({}); const [form] = Form.useForm(); const handleViewSensitive = (studentId: number, field: string, value: string) => { @@ -395,7 +419,59 @@ const StudentsPage: React.FC = () => { onChange: (keys) => setSelectedRowKeys(keys as number[]), getCheckboxProps: (record: any) => ({ disabled: record.status === 'archived' }), }} - /> + expandable={{ + rowExpandable: () => true, + expandedRowRender: (record) => { + const enrollments = enrollmentData[record.id]; + if (!enrollments) return null; + if (enrollments.length < 2) { + return ( +
+ 当前仅 {enrollments.length} 个班型,无可对比数据 +
+ ); + } + return ( + + + {enrollments.map((enr, idx) => ( + + + + {enr.className || '-'} + {enr.startDate || enr.joinDate || '-'} + {enr.endDate || enr.leaveDate || '-'} + + + {enr.status || '-'} + + + + + + ))} + + + ); + }, + onExpand: async (expanded, record) => { + if (expanded && !enrollmentData[record.id]) { + try { + const res = await api.get<{ enrollments: EnrollmentInfo[] }>( + `/students/${record.id}/compare-classes`, + ); + setEnrollmentData((prev) => ({ ...prev, [record.id]: res.enrollments })); + } catch { + setEnrollmentData((prev) => ({ ...prev, [record.id]: [] })); + } + } + }, + }} + />