feat: 为批量归档补充批量恢复
This commit is contained in:
@@ -32,6 +32,7 @@ import PermissionButton from '../../components/PermissionButton';
|
||||
import EditableCell from '../../components/EditableCell';
|
||||
import { message } from '../../ui/app-message';
|
||||
import { usePermission } from '../../hooks/usePermission';
|
||||
import { selectArchiveRecords } from '../archive-view';
|
||||
|
||||
const statusMap: Record<string, { text: string; color: string }> = {
|
||||
available: { text: '可入住', color: 'green' },
|
||||
@@ -95,7 +96,6 @@ const RoomsPage: React.FC = () => {
|
||||
const [editing, setEditing] = useState<any>(null);
|
||||
const canSaveRoom = editing ? canEditRooms : canCreateRooms;
|
||||
const [showArchived, setShowArchived] = useState(false);
|
||||
const [archivedCount, setArchivedCount] = useState(0);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [filterBuilding, setFilterBuilding] = useState<string | undefined>(undefined);
|
||||
const [filterStatus, setFilterStatus] = useState<string | undefined>(undefined);
|
||||
@@ -118,9 +118,16 @@ const RoomsPage: React.FC = () => {
|
||||
const [batchLoading, setBatchLoading] = useState(false);
|
||||
|
||||
// Close modals when the required permission is lost
|
||||
useEffect(() => { if (!canSaveRoom && modalOpen) { setModalOpen(false); setEditing(null); form.resetFields(); } }, [canSaveRoom, modalOpen, form]);
|
||||
useEffect(() => {
|
||||
if (!canSaveRoom && modalOpen) {
|
||||
setModalOpen(false);
|
||||
setEditing(null);
|
||||
form.resetFields();
|
||||
}
|
||||
}, [canSaveRoom, modalOpen, form]);
|
||||
|
||||
const handleBatchDelete = async () => {
|
||||
if (batchLoading) return;
|
||||
setBatchLoading(true);
|
||||
try {
|
||||
const res: any = await api.post('/rooms/batch-delete', { ids: selectedRowKeys });
|
||||
@@ -134,14 +141,32 @@ const RoomsPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleBatchRestore = async () => {
|
||||
if (batchLoading) return;
|
||||
setBatchLoading(true);
|
||||
try {
|
||||
const res = await api.put<{ message?: string; restored: number; skipped: number }>(
|
||||
'/rooms/batch-restore',
|
||||
{ ids: selectedRowKeys },
|
||||
);
|
||||
message.success(
|
||||
`已批量恢复 ${res.restored} 间${res.skipped ? `,跳过 ${res.skipped} 间` : ''}`,
|
||||
);
|
||||
setSelectedRowKeys([]);
|
||||
fetchData();
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '批量恢复失败');
|
||||
} finally {
|
||||
setBatchLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params: any = { includeArchived: 'true' };
|
||||
const params: any = { includeArchived: showArchived ? 'true' : undefined };
|
||||
const res: any = await api.get('/rooms/overview', { params });
|
||||
const archived = res.filter((r: any) => r.status === 'archived');
|
||||
setArchivedCount(archived.length);
|
||||
const filtered = showArchived ? res : res.filter((r: any) => r.status !== 'archived');
|
||||
const filtered = selectArchiveRecords(res, showArchived ? 'archived' : 'active');
|
||||
setData(filtered);
|
||||
} catch (e: unknown) {
|
||||
const err = e as { message?: string };
|
||||
@@ -530,11 +555,7 @@ const RoomsPage: React.FC = () => {
|
||||
{r.status === 'archived' ? (
|
||||
canEditRooms ? (
|
||||
<Popconfirm title="确定恢复此宿舍?" onConfirm={() => handleRestore(r.id)}>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<UndoOutlined />}
|
||||
type="link"
|
||||
>
|
||||
<Button size="small" icon={<UndoOutlined />} type="link">
|
||||
恢复
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
@@ -568,10 +589,7 @@ const RoomsPage: React.FC = () => {
|
||||
</PermissionButton>
|
||||
{canDeleteRooms ? (
|
||||
<Popconfirm title="确定归档?" onConfirm={() => handleArchive(r.id)}>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<InboxOutlined />}
|
||||
>
|
||||
<Button size="small" icon={<InboxOutlined />}>
|
||||
归档
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
@@ -638,15 +656,34 @@ const RoomsPage: React.FC = () => {
|
||||
/>
|
||||
<Button
|
||||
type={showArchived ? 'primary' : 'default'}
|
||||
onClick={() => setShowArchived(!showArchived)}
|
||||
onClick={() => {
|
||||
setShowArchived(!showArchived);
|
||||
setFilterStatus(undefined);
|
||||
setSelectedRowKeys([]);
|
||||
}}
|
||||
>
|
||||
{showArchived
|
||||
? '隐藏已归档'
|
||||
: `显示已归档${archivedCount > 0 ? ` (${archivedCount})` : ''}`}
|
||||
{showArchived ? '返回正常数据' : '查看已归档'}
|
||||
</Button>
|
||||
</Space>
|
||||
<Space wrap className="responsive-toolbar__group">
|
||||
{canDeleteRooms ? (
|
||||
{showArchived && canEditRooms ? (
|
||||
<Popconfirm
|
||||
title={`确定批量恢复选中的 ${selectedRowKeys.length} 间宿舍?`}
|
||||
onConfirm={handleBatchRestore}
|
||||
okText="恢复"
|
||||
cancelText="取消"
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<UndoOutlined />}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
loading={batchLoading}
|
||||
>
|
||||
批量恢复
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
) : !showArchived && canDeleteRooms ? (
|
||||
<Popconfirm
|
||||
title={`确定批量归档选中的 ${selectedRowKeys.length} 间宿舍?(有在住人员的会跳过)`}
|
||||
onConfirm={handleBatchDelete}
|
||||
@@ -664,19 +701,21 @@ const RoomsPage: React.FC = () => {
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
) : null}
|
||||
<PermissionButton
|
||||
permission="room:create"
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
setEditing(null);
|
||||
form.resetFields();
|
||||
setModalOpen(true);
|
||||
}}
|
||||
>
|
||||
添加宿舍
|
||||
</PermissionButton>
|
||||
{hasPermission('room:create') ? (
|
||||
{!showArchived ? (
|
||||
<PermissionButton
|
||||
permission="room:create"
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
setEditing(null);
|
||||
form.resetFields();
|
||||
setModalOpen(true);
|
||||
}}
|
||||
>
|
||||
添加宿舍
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
{!showArchived && hasPermission('room:create') ? (
|
||||
<Upload
|
||||
accept=".xlsx,.xls"
|
||||
showUploadList={false}
|
||||
@@ -734,7 +773,6 @@ const RoomsPage: React.FC = () => {
|
||||
rowSelection={{
|
||||
selectedRowKeys,
|
||||
onChange: (keys) => setSelectedRowKeys(keys as number[]),
|
||||
getCheckboxProps: (record: any) => ({ disabled: record.status === 'archived' }),
|
||||
}}
|
||||
/>
|
||||
<style>{`.archived-row { opacity: 0.6; background: #fafafa !important; }`}</style>
|
||||
|
||||
Reference in New Issue
Block a user