fix(admin): 排课提交保留 teacherId、同名新生导入提示、冲突检测排除 cancelled
- teacherId 用隐藏 Form.Item 注册:validateFields 返回、resetFields 清理, 修复提交丢失老师选择与新建弹窗残留上次编辑老师(重构引入的回归) - 教师添加返回空数组(全部科目已存在)时提示「未新增」而非「已添加」 - 冲突检测排除所有非 active 排课(与后端 cancelled 等状态语义一致) - 创建弹窗打开时复位教师加载 loading,防过期请求导致永久 spinner
This commit is contained in:
@@ -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('已添加');
|
||||||
|
|||||||
@@ -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="选择班级"
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user