Files
gongxue-base/apps/admin/src/pages/Deposits/DepositTable.tsx

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
}
/>
),
}}
/>
);
};