fix(schedules): support editable schedule notes

This commit is contained in:
2026-07-13 12:09:53 +08:00
parent dfd3cf6772
commit 6396d3e934
5 changed files with 70 additions and 0 deletions

View File

@@ -947,6 +947,19 @@ const SchedulesPage: React.FC = () => {
/> />
</Form.Item> </Form.Item>
<Form.Item
name="notes"
label="备注"
rules={[{ max: 500, message: '备注不能超过500字' }]}
>
<Input.TextArea
rows={3}
maxLength={500}
showCount
placeholder="可填写排课说明、设备需求或临时调整原因"
/>
</Form.Item>
<Form.Item <Form.Item
name="timeRange" name="timeRange"
label="上课时段" label="上课时段"

View File

@@ -15,10 +15,12 @@ describe('schedule edit form mapping', () => {
endTime: '18:00', endTime: '18:00',
startDate: '2026-07-01', startDate: '2026-07-01',
endDate: '2026-07-31', endDate: '2026-07-31',
notes: '需要投影设备',
}); });
expect(values.classroomId).toBe(1); expect(values.classroomId).toBe(1);
expect(values.weekDay).toBe(5); expect(values.weekDay).toBe(5);
expect(values.notes).toBe('需要投影设备');
expect(values.timeRange.map((item) => item.format('HH:mm'))).toEqual(['14:00', '18:00']); expect(values.timeRange.map((item) => item.format('HH:mm'))).toEqual(['14:00', '18:00']);
expect(values.dateRange.map((item) => item.format('YYYY-MM-DD'))).toEqual([ expect(values.dateRange.map((item) => item.format('YYYY-MM-DD'))).toEqual([
'2026-07-01', '2026-07-01',
@@ -36,6 +38,7 @@ describe('schedule edit form mapping', () => {
teacherId: 4, teacherId: 4,
timeRange: [dayjs('2026-01-01 13:30'), dayjs('2026-01-01 17:20')], timeRange: [dayjs('2026-01-01 13:30'), dayjs('2026-01-01 17:20')],
dateRange: [dayjs('2026-08-01'), dayjs('2026-08-31')], dateRange: [dayjs('2026-08-01'), dayjs('2026-08-31')],
notes: ' 临时调整教室 ',
}), }),
).toEqual({ ).toEqual({
classId: 1, classId: 1,
@@ -47,6 +50,24 @@ describe('schedule edit form mapping', () => {
endTime: '17:20', endTime: '17:20',
startDate: '2026-08-01', startDate: '2026-08-01',
endDate: '2026-08-31', endDate: '2026-08-31',
notes: '临时调整教室',
}); });
}); });
}); });
describe('schedule notes normalization', () => {
it('omits whitespace-only notes from the payload', () => {
expect(
buildSchedulePayload({
classId: 1,
classroomId: 2,
weekDay: 6,
subject: '作文',
timeRange: [dayjs('2026-01-01 13:30'), dayjs('2026-01-01 17:20')],
dateRange: [dayjs('2026-08-01'), dayjs('2026-08-31')],
notes: ' ',
}).notes,
).toBeUndefined();
});
});

View File

@@ -6,6 +6,7 @@ export interface ScheduleFormValues {
weekDay: number; weekDay: number;
subject: string; subject: string;
teacherId?: number; teacherId?: number;
notes?: string;
timeRange: [Dayjs, Dayjs]; timeRange: [Dayjs, Dayjs];
dateRange: [Dayjs, Dayjs]; dateRange: [Dayjs, Dayjs];
} }
@@ -17,6 +18,7 @@ export interface EditableSchedule {
weekDay: number; weekDay: number;
subject: string; subject: string;
teacherId: number | null; teacherId: number | null;
notes?: string | null;
startTime: string; startTime: string;
endTime: string; endTime: string;
startDate: string; startDate: string;
@@ -29,6 +31,7 @@ export const scheduleToFormValues = (schedule: EditableSchedule): ScheduleFormVa
weekDay: schedule.weekDay, weekDay: schedule.weekDay,
subject: schedule.subject, subject: schedule.subject,
teacherId: schedule.teacherId ?? undefined, teacherId: schedule.teacherId ?? undefined,
notes: schedule.notes ?? undefined,
timeRange: [dayjs(`2000-01-01 ${schedule.startTime}`), dayjs(`2000-01-01 ${schedule.endTime}`)], timeRange: [dayjs(`2000-01-01 ${schedule.startTime}`), dayjs(`2000-01-01 ${schedule.endTime}`)],
dateRange: [dayjs(schedule.startDate), dayjs(schedule.endDate)], dateRange: [dayjs(schedule.startDate), dayjs(schedule.endDate)],
}); });
@@ -39,6 +42,7 @@ export const buildSchedulePayload = (values: ScheduleFormValues) => ({
weekDay: values.weekDay, weekDay: values.weekDay,
subject: values.subject, subject: values.subject,
teacherId: values.teacherId, teacherId: values.teacherId,
notes: values.notes?.trim() || undefined,
startTime: values.timeRange[0].format('HH:mm'), startTime: values.timeRange[0].format('HH:mm'),
endTime: values.timeRange[1].format('HH:mm'), endTime: values.timeRange[1].format('HH:mm'),
startDate: values.dateRange[0].format('YYYY-MM-DD'), startDate: values.dateRange[0].format('YYYY-MM-DD'),

View File

@@ -0,0 +1,29 @@
import 'reflect-metadata';
import { validate } from 'class-validator';
import { CreateScheduleDto, UpdateScheduleDto } from './schedule.dto';
const createSchedule = (notes: string) =>
Object.assign(new CreateScheduleDto(), {
classId: 1,
classroomId: 2,
weekDay: 1,
startTime: '09:00',
endTime: '10:00',
startDate: '2026-07-01',
endDate: '2026-07-31',
subject: '语文',
notes,
});
describe('schedule notes validation', () => {
it('rejects notes longer than 500 characters when creating', async () => {
const errors = await validate(createSchedule('a'.repeat(501)));
expect(errors.some((error) => error.property === 'notes')).toBe(true);
});
it('rejects notes longer than 500 characters when updating', async () => {
const dto = Object.assign(new UpdateScheduleDto(), { notes: 'a'.repeat(501) });
const errors = await validate(dto);
expect(errors.some((error) => error.property === 'notes')).toBe(true);
});
});

View File

@@ -7,6 +7,7 @@ import {
Matches, Matches,
Min, Min,
Max, Max,
MaxLength,
} from 'class-validator'; } from 'class-validator';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
@@ -59,6 +60,7 @@ export class CreateScheduleDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
@MaxLength(500)
notes?: string; notes?: string;
@IsOptional() @IsOptional()
@@ -115,6 +117,7 @@ export class UpdateScheduleDto {
@IsOptional() @IsOptional()
@IsString() @IsString()
@MaxLength(500)
notes?: string; notes?: string;
} }