feat: add bed/locker selection to check-in form and occupancy table

This commit is contained in:
2026-07-09 12:16:51 +08:00
parent f959d2adf2
commit ba94a0e091

View File

@@ -54,6 +54,8 @@ const OccupanciesPage: React.FC = () => {
const [checkOutForm] = Form.useForm();
const [transferForm] = Form.useForm();
const [batchCheckOutForm] = Form.useForm();
const [availableBeds, setAvailableBeds] = useState<any[]>([]);
const [availableLockers, setAvailableLockers] = useState<any[]>([]);
const fetchData = useCallback(async () => {
setLoading(true);
@@ -86,6 +88,25 @@ const OccupanciesPage: React.FC = () => {
setSelectedRowKeys([]);
}, [fetchData]);
const handleRoomChange = async (roomId: number) => {
checkInForm.setFieldValue('bedId', undefined);
checkInForm.setFieldValue('lockerId', undefined);
if (!roomId) {
setAvailableBeds([]);
setAvailableLockers([]);
return;
}
try {
const [beds, lockers] = await Promise.all([
api.get(`/rooms/${roomId}/beds/available`),
api.get(`/rooms/${roomId}/lockers/available`),
]);
setAvailableBeds(beds);
setAvailableLockers(lockers);
if (beds.length === 1) checkInForm.setFieldValue('bedId', beds[0].id);
} catch (e) { console.error(e); }
};
const filteredData = useMemo(() => {
if (!searchText) return data;
const keyword = searchText.toLowerCase();
@@ -108,6 +129,8 @@ const OccupanciesPage: React.FC = () => {
rentalType: values.rentalType,
tenantId: values.tenantId,
notes: values.notes,
bedId: values.bedId,
lockerId: values.lockerId || undefined,
});
message.success('入住登记成功');
setCheckInModal(false);
@@ -195,6 +218,8 @@ const OccupanciesPage: React.FC = () => {
const columns = useMemo(() => [
{ title: '学生', width: 120, render: (_: any, r: any) => r.student?.name || '-' },
{ title: '宿舍', width: 120, render: (_: any, r: any) => r.room?.roomNumber || '-' },
{ title: '床位', width: 80, render: (_: unknown, r: Record<string, unknown>) => (r.bed as Record<string, string> | undefined)?.bedNumber || '-' },
{ title: '柜子', width: 80, render: (_: unknown, r: Record<string, unknown>) => (r.locker as Record<string, string> | undefined)?.lockerNumber || '-' },
{ title: '入住日期', dataIndex: 'checkInDate', width: 110 },
{ title: '计费起始', dataIndex: 'billingStartDate', width: 110 },
{
@@ -494,18 +519,17 @@ const OccupanciesPage: React.FC = () => {
name="roomId"
label="选择宿舍"
rules={[{ required: true, message: '请选择宿舍' }]}
>
<Select
showSearch
optionFilterProp="label"
placeholder="搜索并选择宿舍"
options={rooms.map((r: any) => ({
onChange={handleRoomChange}
options={rooms.map((r) => ({
value: r.id,
label: `${r.roomNumber} (${r.building || ''}) [${r.currentCount}/${r.capacity}]`,
disabled: r.currentCount >= r.capacity,
}))}
/>
</Form.Item>
<Form.Item name="checkInDate" label="入住日期" rules={[{ required: true }]}>
<DatePicker style={{ width: '100%' }} placeholder="选择入住日期" format="YYYY-MM-DD" />
</Form.Item>
@@ -542,6 +566,37 @@ const OccupanciesPage: React.FC = () => {
}))}
/>
</Form.Item>
<Form.Item
name="bedId"
label="床位"
rules={[{ required: true, message: '请选择床位' }]}
>
<Select
placeholder="请先选择房间"
disabled={availableBeds.length === 0}
options={availableBeds.map((b) => ({
value: b.id,
label: b.bedNumber,
}))}
notFoundContent="该房间暂无可用床位"
/>
</Form.Item>
{availableBeds.length > 0 && (
<div style={{ marginTop: -16, marginBottom: 16, color: '#888', fontSize: 12 }}>
{availableBeds.length}
</div>
)}
<Form.Item name="lockerId" label="柜子(可选)">
<Select
allowClear
placeholder="可选分配柜子"
disabled={availableLockers.length === 0}
options={availableLockers.map((l) => ({
value: l.id,
label: l.lockerNumber,
}))}
/>
</Form.Item>
<Form.Item name="notes" label="备注">
<Input.TextArea rows={2} />
</Form.Item>