166 lines
5.2 KiB
TypeScript
166 lines
5.2 KiB
TypeScript
import React from 'react';
|
|
import { Button, Popconfirm, Space, Table, Tag } from 'antd';
|
|
import { DeleteOutlined, InboxOutlined } from '@ant-design/icons';
|
|
import dayjs from 'dayjs';
|
|
import PermissionButton from '../../components/PermissionButton';
|
|
import { QueryEmpty } from '../../components/QueryState';
|
|
import { message } from '../../ui/app-message';
|
|
import { statusMap } from './DepositModals';
|
|
import type { DepositRecord } from './DepositModals';
|
|
|
|
export interface DepositTableProps {
|
|
data: any[];
|
|
loading: boolean;
|
|
canPurgeDeposit: boolean;
|
|
canCreateDeposit?: boolean;
|
|
onCreateDeposit?: () => void;
|
|
refundForm: ReturnType<typeof import('antd').Form.useForm>[0];
|
|
onDetail: (record: DepositRecord) => void;
|
|
onRefund: (record: DepositRecord) => void;
|
|
onArchive: (id: number) => Promise<unknown> | unknown;
|
|
onPurge: (id: number) => Promise<unknown> | unknown;
|
|
}
|
|
|
|
export const DepositTable: React.FC<DepositTableProps> = ({
|
|
data,
|
|
loading,
|
|
canPurgeDeposit,
|
|
canCreateDeposit,
|
|
onCreateDeposit,
|
|
refundForm,
|
|
onDetail,
|
|
onRefund,
|
|
onArchive,
|
|
onPurge,
|
|
}) => {
|
|
const columns = [
|
|
{ title: '学生', width: 120, render: (_: unknown, r: any) => r.student?.name || '-' },
|
|
{
|
|
title: '当前可用押金',
|
|
dataIndex: 'amount',
|
|
width: 130,
|
|
render: (v: number) => `¥${Number(v || 0).toFixed(2)}`,
|
|
},
|
|
{
|
|
title: '房间',
|
|
width: 120,
|
|
render: (_: unknown, r: any) =>
|
|
r.roomNumber ? `${r.building ? `${r.building}-` : ''}${r.roomNumber}` : '-',
|
|
},
|
|
{ title: '房型', dataIndex: 'roomType', width: 100, render: (v: string) => v || '-' },
|
|
{ title: '最近收取日期', dataIndex: 'paidDate', width: 120, render: (v: string) => v || '-' },
|
|
{
|
|
title: '状态',
|
|
dataIndex: 'status',
|
|
render: (s: string) =>
|
|
s === 'unpaid' ? (
|
|
<Tag color="default">未缴</Tag>
|
|
) : (
|
|
<Tag color={statusMap[s]?.color}>{statusMap[s]?.text || s}</Tag>
|
|
),
|
|
},
|
|
{ title: '退还日期', dataIndex: 'refundDate', width: 110, render: (v: unknown) => v || '-' },
|
|
{ title: '备注', dataIndex: 'notes', width: 120, render: (v: unknown) => v || '-' },
|
|
{
|
|
title: '操作',
|
|
fixed: 'right' as const,
|
|
width: 240,
|
|
render: (_: unknown, record: any) => {
|
|
const hasDeposit = typeof record.id === 'number';
|
|
return (
|
|
<Space>
|
|
{hasDeposit && (
|
|
<PermissionButton permission="deposit:view" size="small" onClick={() => onDetail(record)}>
|
|
详情
|
|
</PermissionButton>
|
|
)}
|
|
{record.status === 'paid' && hasDeposit && (
|
|
<PermissionButton
|
|
permission="deposit:refund"
|
|
size="small"
|
|
type="primary"
|
|
onClick={() => {
|
|
onRefund(record);
|
|
refundForm.setFieldsValue({ refundDate: dayjs() });
|
|
}}
|
|
>
|
|
退还
|
|
</PermissionButton>
|
|
)}
|
|
{hasDeposit && (
|
|
<Popconfirm
|
|
title="确定归档?"
|
|
onConfirm={async () => {
|
|
try {
|
|
await onArchive(record.id);
|
|
message.success('归档成功');
|
|
} catch {
|
|
// 错误提示由 useApiMutation 统一处理
|
|
}
|
|
}}
|
|
>
|
|
<PermissionButton
|
|
permission="deposit:delete"
|
|
size="small"
|
|
danger
|
|
icon={<InboxOutlined />}
|
|
>
|
|
归档
|
|
</PermissionButton>
|
|
</Popconfirm>
|
|
)}
|
|
{record.status === 'archived' && hasDeposit && canPurgeDeposit ? (
|
|
<Popconfirm
|
|
title="确定永久删除该押金记录?"
|
|
description="删除后不可恢复,分期记录将一并清除。"
|
|
okText="永久删除"
|
|
okButtonProps={{ danger: true }}
|
|
onConfirm={async () => {
|
|
try {
|
|
await onPurge(record.id);
|
|
message.success('已永久删除(不可恢复)');
|
|
} catch {
|
|
// 错误提示由 useApiMutation 统一处理
|
|
}
|
|
}}
|
|
>
|
|
<Button size="small" danger icon={<DeleteOutlined />}>
|
|
删除
|
|
</Button>
|
|
</Popconfirm>
|
|
) : null}
|
|
</Space>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
return (
|
|
<Table
|
|
columns={columns}
|
|
dataSource={data}
|
|
rowKey="id"
|
|
loading={loading}
|
|
scroll={{ x: 1200 }}
|
|
pagination={{
|
|
defaultPageSize: 15,
|
|
showSizeChanger: true,
|
|
pageSizeOptions: [15, 30, 50, 100],
|
|
showTotal: (total) => `共 ${total} 条`,
|
|
}}
|
|
locale={{
|
|
emptyText: (
|
|
<QueryEmpty
|
|
description="暂无数据"
|
|
action={
|
|
canCreateDeposit && onCreateDeposit
|
|
? { label: '收取押金', onClick: onCreateDeposit }
|
|
: undefined
|
|
}
|
|
/>
|
|
),
|
|
}}
|
|
/>
|
|
);
|
|
};
|