feat(students): add multi-enrollment comparison view in student list expand row

This commit is contained in:
2026-07-06 09:49:13 +08:00
parent 6ba7213cc4
commit 2c0ca583a3
2 changed files with 79 additions and 1 deletions

View File

@@ -12,6 +12,10 @@ import {
Popconfirm,
Upload,
App,
Row,
Col,
Card,
Descriptions,
} from 'antd';
import {
PlusOutlined,
@@ -43,6 +47,25 @@ const statusMap: Record<string, { text: string; color: string }> = {
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<any[]>([]);
@@ -54,6 +77,7 @@ const StudentsPage: React.FC = () => {
const [showArchived, setShowArchived] = useState(false);
const [archivedCount, setArchivedCount] = useState(0);
const [selectedRowKeys, setSelectedRowKeys] = useState<number[]>([]);
const [enrollmentData, setEnrollmentData] = useState<Record<number, EnrollmentInfo[]>>({});
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 (
<div style={{ padding: 8, color: '#999', fontSize: 13 }}>
{enrollments.length}
</div>
);
}
return (
<Card title="多班型对比" size="small" style={{ margin: '8px 0' }}>
<Row gutter={16}>
{enrollments.map((enr, idx) => (
<Col span={12} key={enr.classId}>
<Card
size="small"
title={enr.classType || `班型 ${idx + 1}`}
style={{ background: idx === 0 ? '#f0f5ff' : '#f6ffed' }}
>
<Descriptions column={1} size="small">
<Descriptions.Item label="班级">{enr.className || '-'}</Descriptions.Item>
<Descriptions.Item label="开班日期">{enr.startDate || enr.joinDate || '-'}</Descriptions.Item>
<Descriptions.Item label="结课日期">{enr.endDate || enr.leaveDate || '-'}</Descriptions.Item>
<Descriptions.Item label="状态">
<Tag color={enr.status === 'active' ? 'green' : 'default'}>
{enr.status || '-'}
</Tag>
</Descriptions.Item>
</Descriptions>
</Card>
</Col>
))}
</Row>
</Card>
);
},
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]: [] }));
}
}
},
}}
/>
<style>{`.archived-row { opacity: 0.6; background: #fafafa !important; }`}</style>
<Modal
title={editing ? '编辑学生' : '添加学生'}

View File

@@ -193,6 +193,8 @@ export class StudentsService {
classId: e.classId,
className: e.class?.name || '',
classType: e.class?.classType || '',
startDate: e.class?.startDate || '',
endDate: e.class?.endDate || '',
joinDate: e.joinDate,
leaveDate: e.leaveDate,
status: e.status,