diff --git a/apps/admin/src/components/StudentProfileContent/EnrollmentsTab.tsx b/apps/admin/src/components/StudentProfileContent/EnrollmentsTab.tsx index 6b257e55..153e4b95 100644 --- a/apps/admin/src/components/StudentProfileContent/EnrollmentsTab.tsx +++ b/apps/admin/src/components/StudentProfileContent/EnrollmentsTab.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { App, Button, DatePicker, Form, Input, Modal, Select, Table, Tag } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { PlusOutlined } from '@ant-design/icons'; +import { useNavigate } from 'react-router'; import { useQuery } from '@tanstack/react-query'; import dayjs from 'dayjs'; import api from '../../api'; @@ -47,6 +48,7 @@ export const EnrollmentsTab: React.FC = studentId, }) => { const { modal } = App.useApp(); + const navigate = useNavigate(); const { hasPermission } = usePermission(); const canPurgeArchive = hasPermission('archive:purge'); const [modalOpen, setModalOpen] = useState(false); @@ -60,8 +62,8 @@ export const EnrollmentsTab: React.FC = { invalidate: [['archive', studentId]] }, ); const saveEnrollmentCellMutation = useApiMutation( - async ({ id, field, value }: { id: number; field: string; value: unknown }) => - api.put(`/archive/enrollments/${id}`, { [field]: value }), + async ({ id, payload }: { id: number; payload: Record }) => + api.put(`/archive/enrollments/${id}`, payload), { invalidate: [['archive', studentId]] }, ); const purgeEnrollmentMutation = useApiMutation( @@ -73,7 +75,6 @@ export const EnrollmentsTab: React.FC = data: classes = [], } = useQuery({ queryKey: queryKeys.classes.list({ archived: false }), - enabled: modalOpen, queryFn: async () => { try { return await api.get('/classes', { params: { isArchived: false } }); @@ -88,6 +89,11 @@ export const EnrollmentsTab: React.FC = [data], ); + const enrolledClassIds = useMemo( + () => new Set(data.map((e) => e.classId).filter((id): id is number => id != null)), + [data], + ); + const { data: teachers = [], } = useQuery({ @@ -138,6 +144,7 @@ export const EnrollmentsTab: React.FC = const selectedClass = classes.find((c) => c.id === classId); await addEnrollmentMutation.mutateAsync({ ...rest, + classId: classId ?? undefined, className: selectedClass?.name, startDate: values.startDate?.format('YYYY-MM-DD'), endDate: values.endDate?.format('YYYY-MM-DD'), @@ -154,7 +161,20 @@ export const EnrollmentsTab: React.FC = const saveCell = async (record: EnrollmentRecord, field: string, value: unknown) => { try { - await saveEnrollmentCellMutation.mutateAsync({ id: record.id, field, value }); + if (field === ENROLLMENT_FIELDS.className) { + // 单元格编辑提交的是选中班级 id(含撤销回退的空值),映射回 className+classId 双字段 + const classId = value === '' || value === null || value === undefined ? null : Number(value); + const cls = classId != null ? classes.find((c) => c.id === classId) : undefined; + await saveEnrollmentCellMutation.mutateAsync({ + id: record.id, + payload: { className: cls?.name ?? '', classId }, + }); + } else { + await saveEnrollmentCellMutation.mutateAsync({ + id: record.id, + payload: { [field]: value }, + }); + } message.success('报读记录已保存'); } catch { // 错误提示由 useApiMutation 统一处理 @@ -202,8 +222,23 @@ export const EnrollmentsTab: React.FC = title: '班级名称', dataIndex: 'className', render: (v: string, r) => ( - - {v || '-'} + ({ + value: c.id, + label: c.name, + disabled: enrolledClassIds.has(c.id) && c.id !== r.classId, + }))} + onSave={saveCell} + > + {r.classId != null ? ( + navigate(`/classes/${r.classId}`)}>{v || '-'} + ) : ( + v || '-' + )} ), }, @@ -340,7 +375,7 @@ export const EnrollmentsTab: React.FC = options={classes.map((c) => ({ value: c.id, label: enrolledClassNames.has(c.name) ? `${c.name}(已报读)` : c.name, - disabled: enrolledClassNames.has(c.name), + disabled: enrolledClassNames.has(c.name) || enrolledClassIds.has(c.id), }))} /> diff --git a/apps/admin/src/components/StudentProfileContent/shared.ts b/apps/admin/src/components/StudentProfileContent/shared.ts index 2aed0ef3..10d9eace 100644 --- a/apps/admin/src/components/StudentProfileContent/shared.ts +++ b/apps/admin/src/components/StudentProfileContent/shared.ts @@ -30,6 +30,7 @@ export interface EnrollmentRecord { courseCategory: string; classType: string; className?: string; + classId?: number | null; headTeacher?: string; subjectTeacher?: string; startDate?: string;