feat: 为批量归档补充批量恢复
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
UploadOutlined,
|
||||
DownloadOutlined,
|
||||
ExportOutlined,
|
||||
UndoOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import dayjs from 'dayjs';
|
||||
import api from '../../api';
|
||||
@@ -30,6 +31,7 @@ import EditableCell from '../../components/EditableCell';
|
||||
import { downloadBlob } from '../../utils/download';
|
||||
import { message } from '../../ui/app-message';
|
||||
import { usePermission } from '../../hooks/usePermission';
|
||||
import { archiveViewPolicy, expenseStatusForView } from '../archive-view';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
@@ -61,6 +63,8 @@ const ExpensesPage: React.FC = () => {
|
||||
const [selectedPersonalKeys, setSelectedPersonalKeys] = useState<number[]>([]);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [batchLoading, setBatchLoading] = useState(false);
|
||||
const [showArchived, setShowArchived] = useState(false);
|
||||
const expenseViewPolicy = archiveViewPolicy(showArchived ? 'archived' : 'active');
|
||||
|
||||
// Dynamic expense type options from API
|
||||
const [typeOptions, setTypeOptions] = useState<{ value: string; label: string }[]>([]);
|
||||
@@ -124,12 +128,56 @@ const ExpensesPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleBatchRestoreRoom = async () => {
|
||||
if (batchLoading) return;
|
||||
setBatchLoading(true);
|
||||
try {
|
||||
const res = await api.put<{ restored: number; skipped: number }>(
|
||||
'/expenses/room/batch-restore',
|
||||
{ ids: selectedRoomKeys },
|
||||
);
|
||||
message.success(
|
||||
`已恢复 ${res.restored} 条宿舍费用${res.skipped ? `,跳过 ${res.skipped} 条` : ''}`,
|
||||
);
|
||||
setSelectedRoomKeys([]);
|
||||
fetchData();
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '批量恢复失败');
|
||||
} finally {
|
||||
setBatchLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBatchRestorePersonal = async () => {
|
||||
if (batchLoading) return;
|
||||
setBatchLoading(true);
|
||||
try {
|
||||
const res = await api.put<{ restored: number; skipped: number }>(
|
||||
'/expenses/personal/batch-restore',
|
||||
{ ids: selectedPersonalKeys },
|
||||
);
|
||||
message.success(
|
||||
`已恢复 ${res.restored} 条个人费用${res.skipped ? `,跳过 ${res.skipped} 条` : ''}`,
|
||||
);
|
||||
setSelectedPersonalKeys([]);
|
||||
fetchData();
|
||||
} catch (e: any) {
|
||||
message.error(e?.message || '批量恢复失败');
|
||||
} finally {
|
||||
setBatchLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [re, pe, lookups]: any[] = await Promise.all([
|
||||
api.get('/expenses/room'),
|
||||
api.get('/expenses/personal'),
|
||||
api.get('/expenses/room', {
|
||||
params: { status: expenseStatusForView(showArchived ? 'archived' : 'active') },
|
||||
}),
|
||||
api.get('/expenses/personal', {
|
||||
params: { status: expenseStatusForView(showArchived ? 'archived' : 'active') },
|
||||
}),
|
||||
api.get('/expenses/lookups').catch(() => ({ rooms: [], students: [] })),
|
||||
]);
|
||||
setRoomExpenses(re);
|
||||
@@ -140,10 +188,12 @@ const ExpensesPage: React.FC = () => {
|
||||
message.error(e?.message || '加载失败,请稍后重试');
|
||||
}
|
||||
setLoading(false);
|
||||
}, []);
|
||||
}, [showArchived]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
setSelectedRoomKeys([]);
|
||||
setSelectedPersonalKeys([]);
|
||||
}, [fetchData]);
|
||||
|
||||
const filteredRoomExpenses = useMemo(() => {
|
||||
@@ -287,6 +337,7 @@ const ExpensesPage: React.FC = () => {
|
||||
editor="select"
|
||||
options={rooms.map((item) => ({ value: item.id, label: item.roomNumber }))}
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={(next) => saveRoomCell(r, 'roomId', next)}
|
||||
>
|
||||
@@ -304,6 +355,7 @@ const ExpensesPage: React.FC = () => {
|
||||
editor="select"
|
||||
options={typeOptions}
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={(next) => saveRoomCell(r, 'expenseType', next)}
|
||||
>
|
||||
@@ -321,6 +373,7 @@ const ExpensesPage: React.FC = () => {
|
||||
editor="money"
|
||||
min={0.01}
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={(next) => saveRoomCell(r, 'amount', next)}
|
||||
>{`¥${Number(v).toFixed(2)}`}</EditableCell>
|
||||
@@ -334,6 +387,7 @@ const ExpensesPage: React.FC = () => {
|
||||
value={[r.periodStart, r.periodEnd]}
|
||||
editor="date-range"
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={async (next) => {
|
||||
const [periodStart, periodEnd] = next as unknown as [string, string];
|
||||
@@ -353,6 +407,7 @@ const ExpensesPage: React.FC = () => {
|
||||
value={v}
|
||||
editor="textarea"
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
onSave={(next) => saveRoomCell(r, 'description', next)}
|
||||
>
|
||||
{v || '-'}
|
||||
@@ -368,48 +423,60 @@ const ExpensesPage: React.FC = () => {
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
render: (_: any, record: any) => (
|
||||
<Space>
|
||||
<PermissionButton
|
||||
permission="expense:edit"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => {
|
||||
setEditingRoom(record);
|
||||
roomForm.setFieldsValue({
|
||||
roomId: record.roomId,
|
||||
expenseType: record.expenseType,
|
||||
amount: Number(record.amount),
|
||||
period: [dayjs(record.periodStart), dayjs(record.periodEnd)],
|
||||
description: record.description,
|
||||
});
|
||||
setRoomModal(true);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</PermissionButton>
|
||||
<Popconfirm
|
||||
title="确定归档?"
|
||||
onConfirm={async () => {
|
||||
await api.delete(`/expenses/room/${record.id}`);
|
||||
message.success('归档成功');
|
||||
fetchData();
|
||||
}}
|
||||
>
|
||||
render: (_: any, record: any) =>
|
||||
showArchived ? (
|
||||
<Tag color="#999">已归档</Tag>
|
||||
) : (
|
||||
<Space>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
permission="expense:edit"
|
||||
size="small"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => {
|
||||
setEditingRoom(record);
|
||||
roomForm.setFieldsValue({
|
||||
roomId: record.roomId,
|
||||
expenseType: record.expenseType,
|
||||
amount: Number(record.amount),
|
||||
period: [dayjs(record.periodStart), dayjs(record.periodEnd)],
|
||||
description: record.description,
|
||||
});
|
||||
setRoomModal(true);
|
||||
}}
|
||||
>
|
||||
归档
|
||||
编辑
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
<Popconfirm
|
||||
title="确定归档?"
|
||||
onConfirm={async () => {
|
||||
await api.delete(`/expenses/room/${record.id}`);
|
||||
message.success('归档成功');
|
||||
fetchData();
|
||||
}}
|
||||
>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
size="small"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
>
|
||||
归档
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
],
|
||||
[rooms, typeOptions, typeMap, saveRoomCell, roomForm, fetchData],
|
||||
[
|
||||
rooms,
|
||||
typeOptions,
|
||||
typeMap,
|
||||
saveRoomCell,
|
||||
roomForm,
|
||||
fetchData,
|
||||
showArchived,
|
||||
expenseViewPolicy.readonly,
|
||||
],
|
||||
);
|
||||
|
||||
const personalColumns = useMemo(
|
||||
@@ -423,6 +490,7 @@ const ExpensesPage: React.FC = () => {
|
||||
editor="select"
|
||||
options={students.map((item) => ({ value: item.id, label: item.name }))}
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={(next) => savePersonalCell(r, 'studentId', next)}
|
||||
>
|
||||
@@ -440,6 +508,7 @@ const ExpensesPage: React.FC = () => {
|
||||
editor="select"
|
||||
options={personalTypeOptions}
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={(next) => savePersonalCell(r, 'expenseType', next)}
|
||||
>
|
||||
@@ -456,6 +525,7 @@ const ExpensesPage: React.FC = () => {
|
||||
editor="money"
|
||||
min={0.01}
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={(next) => savePersonalCell(r, 'amount', next)}
|
||||
>{`¥${Number(v).toFixed(2)}`}</EditableCell>
|
||||
@@ -470,6 +540,7 @@ const ExpensesPage: React.FC = () => {
|
||||
value={v}
|
||||
editor="date"
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
required
|
||||
onSave={(next) => savePersonalCell(r, 'expenseDate', next)}
|
||||
>
|
||||
@@ -486,6 +557,7 @@ const ExpensesPage: React.FC = () => {
|
||||
value={v}
|
||||
editor="textarea"
|
||||
permission="expense:edit"
|
||||
disabled={expenseViewPolicy.readonly}
|
||||
onSave={(next) => savePersonalCell(r, 'description', next)}
|
||||
>
|
||||
{v || '-'}
|
||||
@@ -495,53 +567,73 @@ const ExpensesPage: React.FC = () => {
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
render: (_: any, record: any) => (
|
||||
<Space>
|
||||
<PermissionButton
|
||||
permission="expense:edit"
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => {
|
||||
setEditingPersonal(record);
|
||||
personalForm.setFieldsValue({
|
||||
studentId: record.studentId,
|
||||
roomId: record.roomId,
|
||||
expenseType: record.expenseType,
|
||||
amount: Number(record.amount),
|
||||
expenseDate: dayjs(record.expenseDate),
|
||||
description: record.description,
|
||||
});
|
||||
setPersonalModal(true);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</PermissionButton>
|
||||
<Popconfirm
|
||||
title="确定归档?"
|
||||
onConfirm={async () => {
|
||||
await api.delete(`/expenses/personal/${record.id}`);
|
||||
message.success('归档成功');
|
||||
fetchData();
|
||||
}}
|
||||
>
|
||||
render: (_: any, record: any) =>
|
||||
showArchived ? (
|
||||
<Tag color="#999">已归档</Tag>
|
||||
) : (
|
||||
<Space>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
permission="expense:edit"
|
||||
size="small"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => {
|
||||
setEditingPersonal(record);
|
||||
personalForm.setFieldsValue({
|
||||
studentId: record.studentId,
|
||||
roomId: record.roomId,
|
||||
expenseType: record.expenseType,
|
||||
amount: Number(record.amount),
|
||||
expenseDate: dayjs(record.expenseDate),
|
||||
description: record.description,
|
||||
});
|
||||
setPersonalModal(true);
|
||||
}}
|
||||
>
|
||||
归档
|
||||
编辑
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
<Popconfirm
|
||||
title="确定归档?"
|
||||
onConfirm={async () => {
|
||||
await api.delete(`/expenses/personal/${record.id}`);
|
||||
message.success('归档成功');
|
||||
fetchData();
|
||||
}}
|
||||
>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
size="small"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
>
|
||||
归档
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
],
|
||||
[students, personalTypeOptions, typeMap, savePersonalCell, personalForm, fetchData],
|
||||
[
|
||||
students,
|
||||
personalTypeOptions,
|
||||
typeMap,
|
||||
savePersonalCell,
|
||||
personalForm,
|
||||
fetchData,
|
||||
showArchived,
|
||||
expenseViewPolicy.readonly,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space style={{ marginBottom: 16 }}>
|
||||
<Button type={!showArchived ? 'primary' : 'default'} onClick={() => setShowArchived(false)}>
|
||||
正常费用
|
||||
</Button>
|
||||
<Button type={showArchived ? 'primary' : 'default'} onClick={() => setShowArchived(true)}>
|
||||
已归档费用
|
||||
</Button>
|
||||
</Space>
|
||||
<Tabs
|
||||
items={[
|
||||
{
|
||||
@@ -576,7 +668,7 @@ const ExpensesPage: React.FC = () => {
|
||||
onChange={(v) => setRoomTypeFilter(v)}
|
||||
options={typeOptions}
|
||||
/>
|
||||
{hasPermission('expense:create') ? (
|
||||
{!showArchived && hasPermission('expense:create') ? (
|
||||
<Upload
|
||||
accept=".xlsx,.xls"
|
||||
showUploadList={false}
|
||||
@@ -605,47 +697,71 @@ const ExpensesPage: React.FC = () => {
|
||||
<Button icon={<UploadOutlined />}>导入水电费Excel</Button>
|
||||
</Upload>
|
||||
) : null}
|
||||
<PermissionButton
|
||||
permission="expense:view"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => {
|
||||
downloadBlob('/expenses/utility/template', '水电费导入模板.xlsx').catch(
|
||||
() => message.error('下载失败'),
|
||||
);
|
||||
}}
|
||||
>
|
||||
下载水电费模板
|
||||
</PermissionButton>
|
||||
{!showArchived ? (
|
||||
<PermissionButton
|
||||
permission="expense:view"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => {
|
||||
downloadBlob('/expenses/utility/template', '水电费导入模板.xlsx').catch(
|
||||
() => message.error('下载失败'),
|
||||
);
|
||||
}}
|
||||
>
|
||||
下载水电费模板
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
</Space>
|
||||
<Space>
|
||||
<Popconfirm
|
||||
title={`确定归档选中的 ${selectedRoomKeys.length} 条费用?`}
|
||||
onConfirm={handleBatchDeleteRoom}
|
||||
okText="归档"
|
||||
cancelText="取消"
|
||||
disabled={selectedRoomKeys.length === 0}
|
||||
>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
{showArchived ? (
|
||||
<Popconfirm
|
||||
title={`确定恢复选中的 ${selectedRoomKeys.length} 条费用?`}
|
||||
onConfirm={handleBatchRestoreRoom}
|
||||
okText="恢复"
|
||||
cancelText="取消"
|
||||
disabled={selectedRoomKeys.length === 0}
|
||||
>
|
||||
批量归档
|
||||
<PermissionButton
|
||||
permission="expense:edit"
|
||||
type="primary"
|
||||
icon={<UndoOutlined />}
|
||||
loading={batchLoading}
|
||||
disabled={selectedRoomKeys.length === 0}
|
||||
>
|
||||
批量恢复
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
) : (
|
||||
<Popconfirm
|
||||
title={`确定归档选中的 ${selectedRoomKeys.length} 条费用?`}
|
||||
onConfirm={handleBatchDeleteRoom}
|
||||
okText="归档"
|
||||
cancelText="取消"
|
||||
disabled={selectedRoomKeys.length === 0}
|
||||
>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
disabled={selectedRoomKeys.length === 0}
|
||||
>
|
||||
批量归档
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
)}
|
||||
{!showArchived ? (
|
||||
<PermissionButton
|
||||
permission="expense:create"
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
setEditingRoom(null);
|
||||
roomForm.resetFields();
|
||||
setRoomModal(true);
|
||||
}}
|
||||
>
|
||||
录入宿舍费用
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
<PermissionButton
|
||||
permission="expense:create"
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
setEditingRoom(null);
|
||||
roomForm.resetFields();
|
||||
setRoomModal(true);
|
||||
}}
|
||||
>
|
||||
录入宿舍费用
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
<Table
|
||||
@@ -701,7 +817,7 @@ const ExpensesPage: React.FC = () => {
|
||||
onChange={(v) => setPersonalTypeFilter(v)}
|
||||
options={personalTypeOptions}
|
||||
/>
|
||||
{hasPermission('expense:create') ? (
|
||||
{!showArchived && hasPermission('expense:create') ? (
|
||||
<Upload
|
||||
accept=".xlsx,.xls"
|
||||
showUploadList={false}
|
||||
@@ -724,69 +840,97 @@ const ExpensesPage: React.FC = () => {
|
||||
<Button icon={<UploadOutlined />}>导入个人附加费</Button>
|
||||
</Upload>
|
||||
) : null}
|
||||
<PermissionButton
|
||||
permission="expense:view"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => {
|
||||
downloadBlob(
|
||||
'/expenses/personal/template',
|
||||
'个人附加费导入模板.xlsx',
|
||||
).catch(() => message.error('下载失败'));
|
||||
}}
|
||||
>
|
||||
下载模板
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
permission="expense:view"
|
||||
icon={<ExportOutlined />}
|
||||
onClick={() => {
|
||||
downloadBlob('/expenses/personal/export', '个人附加费导出.xlsx').catch(() =>
|
||||
message.error('导出失败'),
|
||||
);
|
||||
}}
|
||||
>
|
||||
导出
|
||||
</PermissionButton>
|
||||
{!showArchived ? (
|
||||
<PermissionButton
|
||||
permission="expense:view"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => {
|
||||
downloadBlob(
|
||||
'/expenses/personal/template',
|
||||
'个人附加费导入模板.xlsx',
|
||||
).catch(() => message.error('下载失败'));
|
||||
}}
|
||||
>
|
||||
下载模板
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
{!showArchived ? (
|
||||
<PermissionButton
|
||||
permission="expense:view"
|
||||
icon={<ExportOutlined />}
|
||||
onClick={() => {
|
||||
downloadBlob('/expenses/personal/export', '个人附加费导出.xlsx').catch(
|
||||
() => message.error('导出失败'),
|
||||
);
|
||||
}}
|
||||
>
|
||||
导出
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
</Space>
|
||||
<Space>
|
||||
<Popconfirm
|
||||
title={`确定归档选中的 ${selectedPersonalKeys.length} 条个人费用?`}
|
||||
onConfirm={handleBatchDeletePersonal}
|
||||
okText="归档"
|
||||
cancelText="取消"
|
||||
disabled={selectedPersonalKeys.length === 0}
|
||||
>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
{showArchived ? (
|
||||
<Popconfirm
|
||||
title={`确定恢复选中的 ${selectedPersonalKeys.length} 条个人费用?`}
|
||||
onConfirm={handleBatchRestorePersonal}
|
||||
okText="恢复"
|
||||
cancelText="取消"
|
||||
disabled={selectedPersonalKeys.length === 0}
|
||||
>
|
||||
批量归档
|
||||
<PermissionButton
|
||||
permission="expense:edit"
|
||||
type="primary"
|
||||
icon={<UndoOutlined />}
|
||||
loading={batchLoading}
|
||||
disabled={selectedPersonalKeys.length === 0}
|
||||
>
|
||||
批量恢复
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
) : (
|
||||
<Popconfirm
|
||||
title={`确定归档选中的 ${selectedPersonalKeys.length} 条个人费用?`}
|
||||
onConfirm={handleBatchDeletePersonal}
|
||||
okText="归档"
|
||||
cancelText="取消"
|
||||
disabled={selectedPersonalKeys.length === 0}
|
||||
>
|
||||
<PermissionButton
|
||||
permission="expense:delete"
|
||||
danger
|
||||
icon={<InboxOutlined />}
|
||||
disabled={selectedPersonalKeys.length === 0}
|
||||
>
|
||||
批量归档
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
)}
|
||||
{!showArchived ? (
|
||||
<PermissionButton
|
||||
permission="expense:create"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
utilityForm.resetFields();
|
||||
setUtilityModal(true);
|
||||
}}
|
||||
>
|
||||
添加学生水电费
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
<PermissionButton
|
||||
permission="expense:create"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
utilityForm.resetFields();
|
||||
setUtilityModal(true);
|
||||
}}
|
||||
>
|
||||
添加学生水电费
|
||||
</PermissionButton>
|
||||
<PermissionButton
|
||||
permission="expense:create"
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
setEditingPersonal(null);
|
||||
personalForm.resetFields();
|
||||
setPersonalModal(true);
|
||||
}}
|
||||
>
|
||||
录入个人费用
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
{!showArchived ? (
|
||||
<PermissionButton
|
||||
permission="expense:create"
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
setEditingPersonal(null);
|
||||
personalForm.resetFields();
|
||||
setPersonalModal(true);
|
||||
}}
|
||||
>
|
||||
录入个人费用
|
||||
</PermissionButton>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
<Table
|
||||
|
||||
Reference in New Issue
Block a user