import React from 'react'; import { Button, Empty, Popconfirm, Space, Table, Tag, Tooltip, Upload, } from 'antd'; import { CheckOutlined, FileTextOutlined, StopOutlined, UploadOutlined, } from '@ant-design/icons'; import dayjs from 'dayjs'; import PermissionButton from '../../components/PermissionButton'; import EditableCell from '../../components/EditableCell'; import { message } from '../../ui/app-message'; const RENTAL_FIELDS = { classroomId: 'classroomId', lesseeOrganizationId: 'lesseeOrganizationId', startDate: 'startDate', endDate: 'endDate', dailyRate: 'dailyRate', totalAmount: 'totalAmount', } as const; export interface RentalTableProps { data: any[]; loading: boolean; classrooms: any[]; organizations: any[]; canPurgeRental: boolean; hasPermission: (permission: string) => boolean; onSaveCell: (record: any, field: string, value: unknown) => Promise | void; onEdit: (record: any) => void; onAction: (id: number, action: 'cancel' | 'end') => void; onArchive: (id: number) => void; onPurge: (id: number, name: string) => void; onDownloadContract: (id: number, filename?: string) => void; onDeleteContract: (id: number) => void; onUploadContract: (id: number, formData: FormData) => Promise; } export const RentalTable: React.FC = ({ data, loading, classrooms, organizations, canPurgeRental, hasPermission, onSaveCell, onEdit, onAction, onArchive, onPurge, onDownloadContract, onDeleteContract, onUploadContract, }) => { const EditableRentalCell = ({ value, field, record, editor, min, required, options, children, }: { value: unknown; field: string; record: R; editor?: React.ComponentProps['editor']; min?: number; required?: boolean; options?: Array<{ value: string | number; label: string }>; children?: React.ReactNode; }) => ( { await onSaveCell(record, field, next); }} > {children ?? String(value ?? '-')} ); const columns = [ { title: '教室', width: 120, dataIndex: 'classroom', render: (c: any, r: any) => ( item.status !== 'archived') .map((item) => ({ value: item.id, label: item.building ? `${item.building} · ${item.name}` : item.name, }))} required > {c ? ( {c.building ? `${c.building} · ` : ''} {c.name} ) : ( '-' )} ), }, { title: '承租机构', width: 100, dataIndex: 'lesseeOrganization', render: (t: any, r: any) => ( item.status !== 'archived') .map((item) => ({ value: item.id, label: item.name }))} required > {t ? ( {t.name} ) : ( '-' )} ), }, { title: '开始日期', dataIndex: 'startDate', width: 110, render: (v: string, r: any) => ( {v} ), }, { title: '结束日期', dataIndex: 'endDate', width: 110, render: (v: string, r: any) => ( {v} ), }, { title: '时长', width: 80, render: (_: any, r: any) => { const d = dayjs(r.endDate).diff(dayjs(r.startDate), 'day') + 1; return `${d}天`; }, }, { title: '日租金', dataIndex: 'dailyRate', width: 100, render: (v: any, r: any) => ( {v ? `¥${v}` : '-'} ), }, { title: '总额', dataIndex: 'totalAmount', width: 100, render: (v: any, r: any) => ( {v ? `¥${v}` : '-'} ), }, { title: '状态', dataIndex: 'effectiveStatus', width: 90, render: (status: string) => { const config: Record = { active: { text: '进行中', color: 'green' }, ended: { text: '已结束', color: 'default' }, cancelled: { text: '已取消', color: 'red' }, }; return {config[status]?.text || status}; }, }, { title: '合同', width: 120, dataIndex: 'contractPath', render: (v: string, r: any) => v ? ( {hasPermission('rental:edit') ? ( onDeleteContract(r.id)}> ) : ( '-' ), }, { title: '操作', width: 150, render: (_: any, record: any) => ( {record.effectiveStatus === 'active' && ( <> onEdit(record)}> 编辑 onAction(record.id, 'cancel')}> } > 取消 {!dayjs(record.startDate).isAfter(dayjs(), 'day') && ( onAction(record.id, 'end')}> }> 结束 )} )} {record.effectiveStatus !== 'active' && ( onArchive(record.id)} > 归档 )} {record.status === 'cancelled' && canPurgeRental ? ( ) : null} ), }, ]; return ( }} pagination={{ defaultPageSize: 15, showSizeChanger: true, pageSizeOptions: [15, 30, 50, 100], showTotal: (total) => `共 ${total} 条`, }} scroll={{ x: 1200 }} /> ); };