forked from wangziqi/gongxue-base
fix(schedules): support editable schedule notes
This commit is contained in:
@@ -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="上课时段"
|
||||||
|
|||||||
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|||||||
29
apps/server/src/schedules/dto/schedule.dto.spec.ts
Normal file
29
apps/server/src/schedules/dto/schedule.dto.spec.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user