feat: 重构各业务模块管理页面与服务
This commit is contained in:
382
apps/admin/src/pages/Rooms/RoomDrawer.tsx
Normal file
382
apps/admin/src/pages/Rooms/RoomDrawer.tsx
Normal file
@@ -0,0 +1,382 @@
|
||||
// aislop-ignore-file: duplicate-block -- 床位/柜子表格声明结构相似且字段不同,渲染逻辑已共享 EditableRoomCell
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Drawer,
|
||||
InputNumber,
|
||||
Popconfirm,
|
||||
Space,
|
||||
Table,
|
||||
Tabs,
|
||||
Tag,
|
||||
} from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import PermissionButton from '../../components/PermissionButton';
|
||||
import {
|
||||
BED_STATUS_MAP,
|
||||
BED_STATUS_OPTIONS,
|
||||
EditableRoomCell,
|
||||
statusMap,
|
||||
type BedItem,
|
||||
type LockerItem,
|
||||
} from './RoomColumns';
|
||||
|
||||
export interface RoomDrawerProps {
|
||||
open: boolean;
|
||||
room: any;
|
||||
beds: BedItem[];
|
||||
lockers: LockerItem[];
|
||||
canEditRooms: boolean;
|
||||
remainingBedSlots: number;
|
||||
defaultBatchBedCount: number;
|
||||
onClose: () => void;
|
||||
onAddBed: () => void;
|
||||
onBatchBeds: (count: number) => void;
|
||||
onEditBed: (record: BedItem) => void;
|
||||
onDeleteBed: (id: number) => void;
|
||||
onSaveBedCell: (record: BedItem, field: string, value: unknown) => void;
|
||||
onAddLocker: () => void;
|
||||
onBatchLockers: (count: number) => void;
|
||||
onEditLocker: (record: LockerItem) => void;
|
||||
onDeleteLocker: (id: number) => void;
|
||||
onSaveLockerCell: (record: LockerItem, field: string, value: unknown) => void;
|
||||
}
|
||||
|
||||
export const RoomDrawer: React.FC<RoomDrawerProps> = ({
|
||||
open,
|
||||
room,
|
||||
beds,
|
||||
lockers,
|
||||
canEditRooms,
|
||||
remainingBedSlots,
|
||||
defaultBatchBedCount,
|
||||
onClose,
|
||||
onAddBed,
|
||||
onBatchBeds,
|
||||
onEditBed,
|
||||
onDeleteBed,
|
||||
onSaveBedCell,
|
||||
onAddLocker,
|
||||
onBatchLockers,
|
||||
onEditLocker,
|
||||
onDeleteLocker,
|
||||
onSaveLockerCell,
|
||||
}) => {
|
||||
const roomItemActions = (kind: 'bed' | 'locker') => (r: any) => {
|
||||
const isBed = kind === 'bed';
|
||||
const handleDelete = isBed ? onDeleteBed : onDeleteLocker;
|
||||
const handleEdit = isBed ? onEditBed : onEditLocker;
|
||||
return (
|
||||
<Space size="small">
|
||||
<PermissionButton
|
||||
permission="room:edit"
|
||||
size="small"
|
||||
type="link"
|
||||
disabled={room?.status === 'archived'}
|
||||
onClick={() => handleEdit(r)}
|
||||
>
|
||||
编辑
|
||||
</PermissionButton>
|
||||
{r.status !== 'occupied' && canEditRooms && (
|
||||
<Popconfirm title="确定归档?" onConfirm={() => handleDelete(r.id)}>
|
||||
<Button size="small" type="link" danger disabled={room?.status === 'archived'}>
|
||||
归档
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={`${room?.roomNumber} 房间详情`}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
size={640}
|
||||
destroyOnClose
|
||||
>
|
||||
<Tabs
|
||||
defaultActiveKey="info"
|
||||
items={[
|
||||
{
|
||||
key: 'info',
|
||||
label: '基本信息',
|
||||
children:
|
||||
room && (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<div>
|
||||
<strong>房间号:</strong>
|
||||
{room.roomNumber}
|
||||
</div>
|
||||
<div>
|
||||
<strong>楼栋:</strong>
|
||||
{room.building || '-'}
|
||||
</div>
|
||||
<div>
|
||||
<strong>楼层:</strong>
|
||||
{room.floor ?? '-'}
|
||||
</div>
|
||||
<div>
|
||||
<strong>类型:</strong>
|
||||
{room.roomType || '-'}
|
||||
</div>
|
||||
<div>
|
||||
<strong>额定人数:</strong>
|
||||
{room.capacity}
|
||||
</div>
|
||||
<div>
|
||||
<strong>租赁类别:</strong>
|
||||
{room.rentalCategory === 'long' ? '长租' : '短租'}
|
||||
</div>
|
||||
<div>
|
||||
<strong>月租金:</strong>
|
||||
{room.monthlyRate ? `¥${room.monthlyRate}` : '-'}
|
||||
</div>
|
||||
<div>
|
||||
<strong>状态:</strong>
|
||||
<Tag color={statusMap[room.status]?.color}>
|
||||
{statusMap[room.status]?.text}
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'beds',
|
||||
label: `床位管理 (${beds.length})`,
|
||||
children: (
|
||||
<div>
|
||||
{canEditRooms ? (
|
||||
<div style={{ marginBottom: 12, display: 'flex', gap: 8 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
disabled={room?.status === 'archived' || remainingBedSlots === 0}
|
||||
onClick={onAddBed}
|
||||
>
|
||||
添加床位
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title={remainingBedSlots > 0 ? '批量生成床位' : '床位已达到额定人数'}
|
||||
description={
|
||||
remainingBedSlots > 0 ? (
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={remainingBedSlots}
|
||||
defaultValue={defaultBatchBedCount}
|
||||
id="batch-bed-count"
|
||||
style={{ width: 80 }}
|
||||
/>
|
||||
) : (
|
||||
'如需增加床位,请先调整宿舍额定人数'
|
||||
)
|
||||
}
|
||||
onConfirm={() => {
|
||||
const input = document.getElementById(
|
||||
'batch-bed-count',
|
||||
) as HTMLInputElement;
|
||||
onBatchBeds(
|
||||
input
|
||||
? parseInt(input.value) || defaultBatchBedCount
|
||||
: defaultBatchBedCount,
|
||||
);
|
||||
}}
|
||||
okText="生成"
|
||||
disabled={room?.status === 'archived' || remainingBedSlots === 0}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
disabled={room?.status === 'archived' || remainingBedSlots === 0}
|
||||
>
|
||||
批量生成
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
) : null}
|
||||
<Table
|
||||
dataSource={beds}
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
size="small"
|
||||
columns={[
|
||||
{
|
||||
title: '编号',
|
||||
dataIndex: 'bedNumber',
|
||||
width: 80,
|
||||
render: (v: string, r: BedItem) => (
|
||||
<EditableRoomCell
|
||||
value={v}
|
||||
field="bedNumber"
|
||||
record={r}
|
||||
required
|
||||
archived={room?.status === 'archived'}
|
||||
onSave={onSaveBedCell}
|
||||
>
|
||||
{v}
|
||||
</EditableRoomCell>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 80,
|
||||
render: (s: string, r: BedItem) => (
|
||||
<EditableRoomCell
|
||||
value={s}
|
||||
field="status"
|
||||
record={r}
|
||||
editor="select"
|
||||
options={BED_STATUS_OPTIONS}
|
||||
archived={room?.status === 'archived'}
|
||||
onSave={onSaveBedCell}
|
||||
>
|
||||
<Tag color={BED_STATUS_MAP[s]?.color}>
|
||||
{BED_STATUS_MAP[s]?.text || s}
|
||||
</Tag>
|
||||
</EditableRoomCell>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'notes',
|
||||
render: (v: string, r: BedItem) => (
|
||||
<EditableRoomCell
|
||||
value={v}
|
||||
field="notes"
|
||||
record={r}
|
||||
editor="textarea"
|
||||
archived={room?.status === 'archived'}
|
||||
onSave={onSaveBedCell}
|
||||
>
|
||||
{v || '-'}
|
||||
</EditableRoomCell>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
render: roomItemActions('bed'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'lockers',
|
||||
label: `柜子管理 (${lockers.length})`,
|
||||
children: (
|
||||
<div>
|
||||
{canEditRooms ? (
|
||||
<div style={{ marginBottom: 12, display: 'flex', gap: 8 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
disabled={room?.status === 'archived'}
|
||||
onClick={onAddLocker}
|
||||
>
|
||||
添加柜子
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="批量生成柜子"
|
||||
description={
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={20}
|
||||
defaultValue={4}
|
||||
id="batch-locker-count"
|
||||
style={{ width: 80 }}
|
||||
/>
|
||||
}
|
||||
onConfirm={() => {
|
||||
const input = document.getElementById(
|
||||
'batch-locker-count',
|
||||
) as HTMLInputElement;
|
||||
onBatchLockers(input ? parseInt(input.value) || 4 : 4);
|
||||
}}
|
||||
okText="生成"
|
||||
disabled={room?.status === 'archived'}
|
||||
>
|
||||
<Button size="small" disabled={room?.status === 'archived'}>
|
||||
批量生成
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
) : null}
|
||||
<Table
|
||||
dataSource={lockers}
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
size="small"
|
||||
columns={[
|
||||
{
|
||||
title: '编号',
|
||||
dataIndex: 'lockerNumber',
|
||||
width: 80,
|
||||
render: (v: string, r: LockerItem) => (
|
||||
<EditableRoomCell
|
||||
value={v}
|
||||
field="lockerNumber"
|
||||
record={r}
|
||||
required
|
||||
archived={room?.status === 'archived'}
|
||||
onSave={onSaveLockerCell}
|
||||
>
|
||||
{v}
|
||||
</EditableRoomCell>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 80,
|
||||
render: (s: string, r: LockerItem) => (
|
||||
<EditableRoomCell
|
||||
value={s}
|
||||
field="status"
|
||||
record={r}
|
||||
editor="select"
|
||||
options={BED_STATUS_OPTIONS}
|
||||
archived={room?.status === 'archived'}
|
||||
onSave={onSaveLockerCell}
|
||||
>
|
||||
<Tag color={BED_STATUS_MAP[s]?.color}>
|
||||
{BED_STATUS_MAP[s]?.text || s}
|
||||
</Tag>
|
||||
</EditableRoomCell>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'notes',
|
||||
render: (v: string, r: LockerItem) => (
|
||||
<EditableRoomCell
|
||||
value={v}
|
||||
field="notes"
|
||||
record={r}
|
||||
editor="textarea"
|
||||
archived={room?.status === 'archived'}
|
||||
onSave={onSaveLockerCell}
|
||||
>
|
||||
{v || '-'}
|
||||
</EditableRoomCell>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
render: roomItemActions('locker'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user