fix(admin): 排课提交保留 teacherId、同名新生导入提示、冲突检测排除 cancelled
All checks were successful
CI / check (pull_request) Successful in 4m0s
Dependency Check / check (pull_request) Successful in 3m7s

- teacherId 用隐藏 Form.Item 注册:validateFields 返回、resetFields 清理,
  修复提交丢失老师选择与新建弹窗残留上次编辑老师(重构引入的回归)
- 教师添加返回空数组(全部科目已存在)时提示「未新增」而非「已添加」
- 冲突检测排除所有非 active 排课(与后端 cancelled 等状态语义一致)
- 创建弹窗打开时复位教师加载 loading,防过期请求导致永久 spinner
This commit is contained in:
2026-08-11 12:38:31 +08:00
parent 122f3843a2
commit 66c039ee87
4 changed files with 18 additions and 3 deletions

View File

@@ -176,11 +176,16 @@ const ClassDetailPage: React.FC = () => {
} }
setAddingTeacher(true); setAddingTeacher(true);
try { try {
await api.post(`/classes/${id}/teachers`, { const result = await api.post<ClassTeacher[]>(`/classes/${id}/teachers`, {
userId: teacherUserId, userId: teacherUserId,
roleType: teacherRole, roleType: teacherRole,
subjects: teacherSubjects, subjects: teacherSubjects,
}); });
// 后端对全部科目已存在的情况返回空数组(不写审计/通知),前端如实提示避免误以为有新增
if (result.length === 0) {
message.warning('该教师已分配相同科目,未新增');
return;
}
setTeacherModalOpen(false); setTeacherModalOpen(false);
fetchDetail(); fetchDetail();
message.success('已添加'); message.success('已添加');

View File

@@ -185,6 +185,12 @@ export const ScheduleModal: React.FC<ScheduleModalProps> = ({
> >
{mode !== 'detail' ? ( {mode !== 'detail' ? (
<Form form={form} layout="vertical" scrollToFirstError style={{ marginTop: 16 }}> <Form form={form} layout="vertical" scrollToFirstError style={{ marginTop: 16 }}>
{/* teacherId 由父组件 handleTeacherChange 通过 setFieldValue 写入(下拉为复合 key 受控组件,
不直接绑 name用隐藏字段注册保证 validateFields 返回、resetFields 清理(否则
提交丢失 teacherId且新开创建弹窗残留上次编辑的老师 */}
<Form.Item name="teacherId" hidden>
<Input />
</Form.Item>
<Form.Item name="classId" label="班级" rules={[{ required: true, message: '请选择班级' }]}> <Form.Item name="classId" label="班级" rules={[{ required: true, message: '请选择班级' }]}>
<Select <Select
placeholder="选择班级" placeholder="选择班级"

View File

@@ -282,8 +282,10 @@ const SchedulesPage: React.FC = () => {
setSelectedSchedules([]); setSelectedSchedules([]);
setEditingSchedule(null); setEditingSchedule(null);
setModalMode('create'); setModalMode('create');
// 作废编辑/默认值流程中在途的教师请求,避免过期响应回填刚重置的表单 // 作废编辑/默认值流程中在途的教师请求,避免过期响应回填刚重置的表单
// 本路径不发新请求,同时复位 loading防止过期响应因 seq 不匹配而不清 loading 导致永久 spinner
teacherReqSeq.current += 1; teacherReqSeq.current += 1;
setClassTeachersLoading(false);
form.resetFields(); form.resetFields();
form.setFieldsValue({ classroomId, weekDay, attendanceAdvanceMinutes: 30 }); form.setFieldsValue({ classroomId, weekDay, attendanceAdvanceMinutes: 30 });
setModalOpen(true); setModalOpen(true);
@@ -674,6 +676,7 @@ const SchedulesPage: React.FC = () => {
setEditingSchedule(null); setEditingSchedule(null);
setModalMode('create'); setModalMode('create');
teacherReqSeq.current += 1; // 作废在途教师请求,防止过期响应污染新建表单 teacherReqSeq.current += 1; // 作废在途教师请求,防止过期响应污染新建表单
setClassTeachersLoading(false);
form.resetFields(); form.resetFields();
form.setFieldsValue({ form.setFieldsValue({
classroomId: selectedCell?.classroomId, classroomId: selectedCell?.classroomId,

View File

@@ -57,7 +57,8 @@ export function findTimeConflicts(
if (!newStartDate || !newEndDate) return []; if (!newStartDate || !newEndDate) return [];
return (weekMatrix[classroomId]?.[weekDay] || []).filter((schedule) => { return (weekMatrix[classroomId]?.[weekDay] || []).filter((schedule) => {
if (schedule.id !== undefined && schedule.id !== null && schedule.id === editingId) return false; if (schedule.id !== undefined && schedule.id !== null && schedule.id === editingId) return false;
if (schedule.status === 'inactive') return false; // 与后端 checkConflict 语义一致:仅 active 排课参与冲突(后端还有 cancelled 等状态)
if (schedule.status !== 'active') return false;
// 前面已保证 newStartDate/newEndDate 均存在(缺失直接返回空) // 前面已保证 newStartDate/newEndDate 均存在(缺失直接返回空)
if (!dateRangesOverlap(schedule, newStartDate, newEndDate)) return false; if (!dateRangesOverlap(schedule, newStartDate, newEndDate)) return false;
const existingStart = toMinutes(schedule.startTime); const existingStart = toMinutes(schedule.startTime);