fix: 添加默认床位生成逻辑并优化床位添加验证
This commit is contained in:
@@ -156,6 +156,11 @@ const RoomsPage: React.FC = () => {
|
|||||||
if (filterStatus) result = result.filter((r: Record<string, unknown>) => r.status === filterStatus);
|
if (filterStatus) result = result.filter((r: Record<string, unknown>) => r.status === filterStatus);
|
||||||
return result;
|
return result;
|
||||||
}, [data, searchText, filterBuilding, filterStatus]);
|
}, [data, searchText, filterBuilding, filterStatus]);
|
||||||
|
const remainingBedSlots = useMemo(() => {
|
||||||
|
const capacity = Number(drawerRoom?.capacity) || 0;
|
||||||
|
return Math.max(capacity - beds.length, 0);
|
||||||
|
}, [drawerRoom?.capacity, beds.length]);
|
||||||
|
const defaultBatchBedCount = Math.min(4, Math.max(remainingBedSlots, 1));
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
const values = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
@@ -167,7 +172,7 @@ const RoomsPage: React.FC = () => {
|
|||||||
message.success('更新成功');
|
message.success('更新成功');
|
||||||
} else {
|
} else {
|
||||||
await api.post('/rooms', payload);
|
await api.post('/rooms', payload);
|
||||||
message.success('创建成功');
|
message.success(`创建成功,已自动生成 ${values.capacity} 张床位`);
|
||||||
}
|
}
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
@@ -634,24 +639,26 @@ const RoomsPage: React.FC = () => {
|
|||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
disabled={drawerRoom?.status === 'archived'}
|
disabled={drawerRoom?.status === 'archived' || remainingBedSlots === 0}
|
||||||
onClick={() => { setBedEditing(null); bedForm.resetFields(); setBedModalOpen(true); }}
|
onClick={() => { setBedEditing(null); bedForm.resetFields(); setBedModalOpen(true); }}
|
||||||
>
|
>
|
||||||
添加床位
|
添加床位
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="批量生成床位"
|
title={remainingBedSlots > 0 ? '批量生成床位' : '床位已达到额定人数'}
|
||||||
description={
|
description={
|
||||||
<InputNumber min={1} max={20} defaultValue={4} id="batch-bed-count" style={{ width: 80 }} />
|
remainingBedSlots > 0
|
||||||
|
? <InputNumber min={1} max={remainingBedSlots} defaultValue={defaultBatchBedCount} id="batch-bed-count" style={{ width: 80 }} />
|
||||||
|
: '如需增加床位,请先调整宿舍额定人数'
|
||||||
}
|
}
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
const input = document.getElementById('batch-bed-count') as HTMLInputElement;
|
const input = document.getElementById('batch-bed-count') as HTMLInputElement;
|
||||||
handleBatchBeds(input ? parseInt(input.value) || 4 : 4);
|
handleBatchBeds(input ? parseInt(input.value) || defaultBatchBedCount : defaultBatchBedCount);
|
||||||
}}
|
}}
|
||||||
okText="生成"
|
okText="生成"
|
||||||
disabled={drawerRoom?.status === 'archived'}
|
disabled={drawerRoom?.status === 'archived' || remainingBedSlots === 0}
|
||||||
>
|
>
|
||||||
<Button size="small" disabled={drawerRoom?.status === 'archived'}>批量生成</Button>
|
<Button size="small" disabled={drawerRoom?.status === 'archived' || remainingBedSlots === 0}>批量生成</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
|
|||||||
@@ -110,7 +110,9 @@ export class RoomsService {
|
|||||||
roomType: dto.roomType ?? parsed.roomType,
|
roomType: dto.roomType ?? parsed.roomType,
|
||||||
capacity: dto.capacity ?? parsed.capacity,
|
capacity: dto.capacity ?? parsed.capacity,
|
||||||
});
|
});
|
||||||
return this.repo.save(entity);
|
const room = await this.repo.save(entity);
|
||||||
|
await this.createDefaultBeds(room.id, room.capacity);
|
||||||
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(id: number, dto: UpdateRoomDto) {
|
async update(id: number, dto: UpdateRoomDto) {
|
||||||
@@ -312,7 +314,7 @@ export class RoomsService {
|
|||||||
}
|
}
|
||||||
// 智能解析房间号
|
// 智能解析房间号
|
||||||
const parsed = RoomsService.parseRoomNumber(row.roomNumber.trim());
|
const parsed = RoomsService.parseRoomNumber(row.roomNumber.trim());
|
||||||
await this.repo.save(
|
const room = await this.repo.save(
|
||||||
this.repo.create({
|
this.repo.create({
|
||||||
roomNumber: row.roomNumber.trim(),
|
roomNumber: row.roomNumber.trim(),
|
||||||
building: row.building?.trim() || parsed.building || undefined,
|
building: row.building?.trim() || parsed.building || undefined,
|
||||||
@@ -323,6 +325,7 @@ export class RoomsService {
|
|||||||
monthlyRate: row.monthlyRate ?? undefined,
|
monthlyRate: row.monthlyRate ?? undefined,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
await this.createDefaultBeds(room.id, room.capacity);
|
||||||
imported++;
|
imported++;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -353,6 +356,7 @@ export class RoomsService {
|
|||||||
const room = await this.repo.findOne({ where: { id: roomId } });
|
const room = await this.repo.findOne({ where: { id: roomId } });
|
||||||
if (!room) throw new NotFoundException('宿舍不存在');
|
if (!room) throw new NotFoundException('宿舍不存在');
|
||||||
if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加床位');
|
if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加床位');
|
||||||
|
await this.assertCanAddBeds(room, 1);
|
||||||
const existing = await this.bedRepo.findOne({ where: { roomId, bedNumber: dto.bedNumber } });
|
const existing = await this.bedRepo.findOne({ where: { roomId, bedNumber: dto.bedNumber } });
|
||||||
if (existing) throw new BadRequestException('该床位编号已存在');
|
if (existing) throw new BadRequestException('该床位编号已存在');
|
||||||
const bed = this.bedRepo.create({ ...dto, roomId });
|
const bed = this.bedRepo.create({ ...dto, roomId });
|
||||||
@@ -387,6 +391,7 @@ export class RoomsService {
|
|||||||
if (!room) throw new NotFoundException('宿舍不存在');
|
if (!room) throw new NotFoundException('宿舍不存在');
|
||||||
if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加床位');
|
if (room.status === 'archived') throw new BadRequestException('已归档宿舍不能添加床位');
|
||||||
const existing = await this.bedRepo.find({ where: { roomId }, order: { bedNumber: 'ASC' } });
|
const existing = await this.bedRepo.find({ where: { roomId }, order: { bedNumber: 'ASC' } });
|
||||||
|
this.assertCanAddBedsFromCount(room, existing.length, dto.count);
|
||||||
const numbers = existing.map((b) => {
|
const numbers = existing.map((b) => {
|
||||||
const match = b.bedNumber.match(/^\d+/);
|
const match = b.bedNumber.match(/^\d+/);
|
||||||
return match ? parseInt(match[0]) : 0;
|
return match ? parseInt(match[0]) : 0;
|
||||||
@@ -399,6 +404,27 @@ export class RoomsService {
|
|||||||
return this.bedRepo.save(beds);
|
return this.bedRepo.save(beds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createDefaultBeds(roomId: number, capacity: number): Promise<void> {
|
||||||
|
const count = Math.max(capacity ?? 0, 0);
|
||||||
|
if (count === 0) return;
|
||||||
|
const beds = Array.from({ length: count }, (_, index) =>
|
||||||
|
this.bedRepo.create({ roomId, bedNumber: `${index + 1}号床` }),
|
||||||
|
);
|
||||||
|
await this.bedRepo.save(beds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async assertCanAddBeds(room: Room, count: number): Promise<void> {
|
||||||
|
const existingCount = await this.bedRepo.count({ where: { roomId: room.id } });
|
||||||
|
this.assertCanAddBedsFromCount(room, existingCount, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private assertCanAddBedsFromCount(room: Room, existingCount: number, count: number): void {
|
||||||
|
const remaining = Math.max((room.capacity ?? 0) - existingCount, 0);
|
||||||
|
if (count > remaining) {
|
||||||
|
throw new BadRequestException(`床位不能超过额定人数,当前已有 ${existingCount} 张,额定 ${room.capacity} 张,最多还能添加 ${remaining} 张`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 柜子管理 ──
|
// ── 柜子管理 ──
|
||||||
|
|
||||||
async getRoomLockers(roomId: number): Promise<Locker[]> {
|
async getRoomLockers(roomId: number): Promise<Locker[]> {
|
||||||
|
|||||||
Reference in New Issue
Block a user