forked from wangziqi/gongxue-base
- Add Empty component to antd imports - Replace console.error with message.error in catch blocks - Add batchLoading state + guard on batch operations - Add locale emptyText to Tables
662 lines
22 KiB
TypeScript
662 lines
22 KiB
TypeScript
import React, { useEffect, useState, useMemo } from 'react';
|
|
import {
|
|
Table,
|
|
Modal,
|
|
Form,
|
|
Select,
|
|
DatePicker,
|
|
InputNumber,
|
|
Input,
|
|
Space,
|
|
message,
|
|
Tag,
|
|
Popconfirm,
|
|
Tabs,
|
|
List,
|
|
Card,
|
|
Empty,
|
|
} from 'antd';
|
|
import { PlusOutlined, DeleteOutlined, DollarOutlined, CheckOutlined } from '@ant-design/icons';
|
|
import dayjs from 'dayjs';
|
|
import api from '../../api';
|
|
import { maskPhone, maskIdNumber } from '../../utils/sensitive';
|
|
import PermissionButton from '../../components/PermissionButton';
|
|
|
|
const statusMap: Record<string, { text: string; color: string }> = {
|
|
paid: { text: '已缴', color: 'green' },
|
|
refunded: { text: '已全退', color: 'blue' },
|
|
partial_refund: { text: '部分退还', color: 'orange' },
|
|
deducted: { text: '已全扣', color: 'red' },
|
|
};
|
|
|
|
const refundStatusMap: Record<string, { text: string; color: string }> = {
|
|
pending: { text: '待班主任审批', color: 'orange' },
|
|
head_teacher_approved: { text: '待财务审批', color: 'blue' },
|
|
finance_approved: { text: '已退款', color: 'green' },
|
|
refunded: { text: '已退款', color: 'green' },
|
|
};
|
|
|
|
const installmentStatusMap: Record<string, { text: string; color: string }> = {
|
|
pending: { text: '待缴', color: 'orange' },
|
|
paid: { text: '已缴', color: 'green' },
|
|
};
|
|
|
|
interface PendingRefund {
|
|
id: number;
|
|
student?: { name?: string };
|
|
amount?: number;
|
|
paidDate?: string;
|
|
refundStatus?: string;
|
|
refundRequestedAt?: string;
|
|
}
|
|
|
|
const DepositsPage: React.FC = () => {
|
|
const [data, setData] = useState<any[]>([]);
|
|
const [students, setStudents] = useState<any[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [createModal, setCreateModal] = useState(false);
|
|
const [refundModal, setRefundModal] = useState<any>(null);
|
|
const [detailModal, setDetailModal] = useState<any>(null);
|
|
const [installmentModal, setInstallmentModal] = useState<number | null>(null);
|
|
const [pendingRefunds, setPendingRefunds] = useState<PendingRefund[]>([]);
|
|
const [pendingLoading, setPendingLoading] = useState(false);
|
|
const [rejectModal, setRejectModal] = useState<any>(null);
|
|
const [rejectReason, setRejectReason] = useState('');
|
|
const [createForm] = Form.useForm();
|
|
const [refundForm] = Form.useForm();
|
|
const [installmentForm] = Form.useForm();
|
|
const [searchText, setSearchText] = useState('');
|
|
const [filterStatus, setFilterStatus] = useState<string | undefined>(undefined);
|
|
const [activeTab, setActiveTab] = useState<string>('all');
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
const fetchData = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const [d, s]: any[] = await Promise.all([api.get('/deposits'), api.get('/students')]);
|
|
setData(d);
|
|
setStudents(s);
|
|
} catch (e: any) {
|
|
message.error(e?.message || '加载失败,请稍后重试');
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
const fetchPendingRefunds = async () => {
|
|
setPendingLoading(true);
|
|
try {
|
|
const res = await api.get<PendingRefund[]>('/deposits/pending-refunds');
|
|
setPendingRefunds(res || []);
|
|
} catch (e: any) {
|
|
message.error(e?.message || '加载失败,请稍后重试');
|
|
}
|
|
setPendingLoading(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, []);
|
|
|
|
const filteredData = useMemo(() => {
|
|
return data.filter((d: any) => {
|
|
if (searchText) {
|
|
const s = searchText.toLowerCase();
|
|
if (!d.student?.name?.toLowerCase().includes(s)) return false;
|
|
}
|
|
if (filterStatus && d.status !== filterStatus) return false;
|
|
return true;
|
|
});
|
|
}, [data, searchText, filterStatus]);
|
|
|
|
const studentOptions = useMemo(
|
|
() =>
|
|
students
|
|
.filter((s: any) => s.status === 'active')
|
|
.map((s: any) => ({
|
|
value: s.id,
|
|
label: `${s.name} (${s.idNumber ? maskIdNumber(s.idNumber) : (s.phone ? maskPhone(s.phone) : '')})`,
|
|
})),
|
|
[students],
|
|
);
|
|
|
|
const handleCreate = async () => {
|
|
setSaving(true);
|
|
const values = await createForm.validateFields();
|
|
try {
|
|
await api.post('/deposits', {
|
|
studentId: values.studentId,
|
|
amount: values.amount,
|
|
paidDate: values.paidDate.format('YYYY-MM-DD'),
|
|
notes: values.notes,
|
|
});
|
|
message.success('押金记录已创建');
|
|
setCreateModal(false);
|
|
createForm.resetFields();
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '操作失败');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const handleRefund = async () => {
|
|
setSaving(true);
|
|
const values = await refundForm.validateFields();
|
|
try {
|
|
await api.put(`/deposits/${refundModal.id}/refund`, {
|
|
refundDate: values.refundDate.format('YYYY-MM-DD'),
|
|
deductionAmount: values.deductionAmount || 0,
|
|
deductionReason: values.deductionReason,
|
|
notes: values.notes,
|
|
});
|
|
message.success('退还操作完成');
|
|
setRefundModal(null);
|
|
refundForm.resetFields();
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '操作失败');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const handleRequestRefund = async (record: any) => {
|
|
try {
|
|
await api.post(`/deposits/${record.id}/request-refund`);
|
|
message.success('退款申请已提交');
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '操作失败');
|
|
}
|
|
};
|
|
|
|
const handleApproveRefund = async (record: any) => {
|
|
try {
|
|
await api.put(`/deposits/${record.id}/approve-refund`);
|
|
message.success('审批通过');
|
|
fetchPendingRefunds();
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '操作失败');
|
|
}
|
|
};
|
|
|
|
|
|
const handleRejectRefund = async () => {
|
|
if (!rejectModal) return;
|
|
setSaving(true);
|
|
try {
|
|
await api.put(`/deposits/${rejectModal.id}/reject-refund`, { reason: rejectReason || '未说明原因' });
|
|
message.success('已驳回退款申请');
|
|
setRejectModal(null);
|
|
setRejectReason('');
|
|
fetchPendingRefunds();
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '驳回失败');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
const handleAddInstallment = async () => {
|
|
if (installmentModal == null) return;
|
|
const values = await installmentForm.validateFields();
|
|
try {
|
|
await api.post(`/deposits/${installmentModal}/installments`, {
|
|
amount: values.amount,
|
|
dueDate: values.dueDate.format('YYYY-MM-DD'),
|
|
});
|
|
message.success('分期已添加');
|
|
setInstallmentModal(null);
|
|
installmentForm.resetFields();
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '操作失败');
|
|
}
|
|
};
|
|
|
|
const handlePayInstallment = async (installmentId: number) => {
|
|
try {
|
|
await api.put(`/deposits/installments/${installmentId}`, {
|
|
paidDate: dayjs().format('YYYY-MM-DD'),
|
|
status: 'paid',
|
|
});
|
|
message.success('分期已标记为已缴');
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '操作失败');
|
|
}
|
|
};
|
|
|
|
const handleDeleteInstallment = async (installmentId: number) => {
|
|
try {
|
|
await api.delete(`/deposits/installments/${installmentId}`);
|
|
message.success('分期已删除');
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '操作失败');
|
|
}
|
|
};
|
|
|
|
const columns = useMemo(() => [
|
|
{ title: '学生', width: 120, render: (_: any, r: any) => r.student?.name || '-' },
|
|
{ title: '押金金额', dataIndex: 'amount', width: 110, render: (v: number) => `¥${Number(v).toFixed(2)}` },
|
|
{ title: '缴纳日期', dataIndex: 'paidDate', width: 110 },
|
|
{
|
|
title: '状态',
|
|
dataIndex: 'status',
|
|
render: (s: string) => <Tag color={statusMap[s]?.color}>{statusMap[s]?.text || s}</Tag>,
|
|
},
|
|
{
|
|
title: '退款审批',
|
|
dataIndex: 'refundStatus',
|
|
render: (s: string) =>
|
|
s ? <Tag color={refundStatusMap[s]?.color}>{refundStatusMap[s]?.text || s}</Tag> : '-',
|
|
},
|
|
{
|
|
title: '退还金额',
|
|
dataIndex: 'refundAmount',
|
|
render: (v: any) => (v != null ? `¥${Number(v).toFixed(2)}` : '-'),
|
|
},
|
|
{
|
|
title: '扣除金额',
|
|
dataIndex: 'deductionAmount',
|
|
render: (v: any) => (v > 0 ? `¥${Number(v).toFixed(2)}` : '-'),
|
|
},
|
|
{ title: '扣除原因', dataIndex: 'deductionReason', width: 120, render: (v: any) => v || '-' },
|
|
{ title: '退还日期', dataIndex: 'refundDate', width: 110, render: (v: any) => v || '-' },
|
|
{ title: '备注', dataIndex: 'notes', width: 120, render: (v: any) => v || '-' },
|
|
{
|
|
title: '操作',
|
|
width: 240,
|
|
render: (_: any, record: any) => (
|
|
<Space>
|
|
<PermissionButton
|
|
permission="deposit:view"
|
|
size="small"
|
|
onClick={() => {
|
|
setDetailModal(record);
|
|
}}
|
|
>
|
|
详情
|
|
</PermissionButton>
|
|
{record.status === 'paid' && !record.refundStatus && (
|
|
<>
|
|
<PermissionButton
|
|
permission="deposit:edit"
|
|
size="small"
|
|
type="primary"
|
|
onClick={() => {
|
|
setRefundModal(record);
|
|
refundForm.setFieldsValue({ refundDate: dayjs(), deductionAmount: 0 });
|
|
}}
|
|
>
|
|
退还
|
|
</PermissionButton>
|
|
<PermissionButton
|
|
permission="deposit:edit"
|
|
size="small"
|
|
onClick={() => handleRequestRefund(record)}
|
|
>
|
|
申请退款
|
|
</PermissionButton>
|
|
</>
|
|
)}
|
|
<Popconfirm
|
|
title="确定删除?"
|
|
onConfirm={async () => {
|
|
try {
|
|
await api.delete(`/deposits/${record.id}`);
|
|
message.success('删除成功');
|
|
fetchData();
|
|
} catch (e: any) {
|
|
message.error(e?.message || '删除失败');
|
|
}
|
|
}}
|
|
>
|
|
<PermissionButton
|
|
permission="deposit:delete"
|
|
size="small"
|
|
danger
|
|
icon={<DeleteOutlined />}
|
|
>
|
|
删除
|
|
</PermissionButton>
|
|
</Popconfirm>
|
|
</Space>
|
|
),
|
|
},
|
|
], [handleRequestRefund, fetchData]);
|
|
|
|
const pendingColumns = [
|
|
{ title: '学生', width: 120, render: (_: any, r: any) => r.student?.name || '-' },
|
|
{ title: '押金金额', dataIndex: 'amount', width: 110, render: (v: number) => `¥${Number(v).toFixed(2)}` },
|
|
{ title: '缴纳日期', dataIndex: 'paidDate', width: 110 },
|
|
{
|
|
title: '审批状态', width: 120,
|
|
dataIndex: 'refundStatus',
|
|
render: (s: string) => <Tag color={refundStatusMap[s]?.color}>{refundStatusMap[s]?.text || s}</Tag>,
|
|
},
|
|
{
|
|
title: '申请时间', width: 160,
|
|
dataIndex: 'refundRequestedAt',
|
|
render: (v: any) => (v ? dayjs(v).format('YYYY-MM-DD HH:mm') : '-'),
|
|
},
|
|
{
|
|
title: '操作', width: 200,
|
|
render: (_: unknown, record: PendingRefund) => (
|
|
<Space>
|
|
<PermissionButton
|
|
permission="deposit:approve"
|
|
size="small"
|
|
type="primary"
|
|
icon={<CheckOutlined />}
|
|
onClick={() => handleApproveRefund(record)}
|
|
>
|
|
审批通过
|
|
</PermissionButton>
|
|
<PermissionButton
|
|
permission="deposit:approve"
|
|
size="small"
|
|
danger
|
|
onClick={() => {
|
|
setRejectModal(record);
|
|
setRejectReason('');
|
|
}}
|
|
>
|
|
驳回
|
|
</PermissionButton>
|
|
</Space>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div>
|
|
<Tabs
|
|
activeKey={activeTab}
|
|
onChange={(key) => {
|
|
setActiveTab(key);
|
|
if (key === 'pending') fetchPendingRefunds();
|
|
}}
|
|
items={[
|
|
{
|
|
key: 'all',
|
|
label: '押金列表',
|
|
children: (
|
|
<>
|
|
<div
|
|
style={{
|
|
marginBottom: 16,
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
flexWrap: 'wrap',
|
|
gap: 8,
|
|
}}
|
|
>
|
|
<Space wrap>
|
|
<Input.Search
|
|
placeholder="搜索学生姓名"
|
|
allowClear
|
|
style={{ width: 180 }}
|
|
onSearch={(v) => setSearchText(v)}
|
|
onChange={(e) => {
|
|
if (!e.target.value) setSearchText('');
|
|
}}
|
|
/>
|
|
<Select
|
|
placeholder="状态筛选"
|
|
allowClear
|
|
style={{ width: 120 }}
|
|
value={filterStatus}
|
|
onChange={(v) => setFilterStatus(v)}
|
|
options={[
|
|
{ value: 'paid', label: '已缴' },
|
|
{ value: 'refunded', label: '已全退' },
|
|
{ value: 'partial_refund', label: '部分退还' },
|
|
{ value: 'deducted', label: '已全扣' },
|
|
]}
|
|
/>
|
|
</Space>
|
|
<PermissionButton
|
|
permission="deposit:create"
|
|
type="primary"
|
|
icon={<PlusOutlined />}
|
|
onClick={() => {
|
|
createForm.resetFields();
|
|
createForm.setFieldsValue({ amount: 500, paidDate: dayjs() });
|
|
setCreateModal(true);
|
|
}}
|
|
>
|
|
收取押金
|
|
</PermissionButton>
|
|
</div>
|
|
<Table
|
|
columns={columns}
|
|
dataSource={filteredData}
|
|
rowKey="id"
|
|
loading={loading}
|
|
scroll={{ x: 1200 }}
|
|
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 条` }}
|
|
locale={{ emptyText: <Empty description="暂无数据" /> }}
|
|
/>
|
|
</>
|
|
),
|
|
},
|
|
{
|
|
key: 'pending',
|
|
label: '待审批退款',
|
|
children: (
|
|
<Table
|
|
columns={pendingColumns}
|
|
dataSource={pendingRefunds}
|
|
rowKey="id"
|
|
loading={pendingLoading}
|
|
scroll={{ x: 1200 }}
|
|
pagination={{ pageSize: 15, showTotal: (total) => `共 ${total} 条` }}
|
|
/>
|
|
),
|
|
},
|
|
]}
|
|
/>
|
|
|
|
{/* Create Modal */}
|
|
<Modal
|
|
title="收取押金"
|
|
open={createModal}
|
|
onOk={handleCreate}
|
|
onCancel={() => setCreateModal(false)}
|
|
okText="确认"
|
|
confirmLoading={saving}
|
|
>
|
|
<Form form={createForm} layout="vertical">
|
|
<Form.Item
|
|
name="studentId"
|
|
label="学生"
|
|
rules={[{ required: true, message: '请选择学生' }]}
|
|
>
|
|
<Select
|
|
showSearch
|
|
optionFilterProp="label"
|
|
placeholder="搜索并选择学生"
|
|
options={studentOptions}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item name="amount" label="押金金额(元)" rules={[{ required: true }]}>
|
|
<InputNumber min={0} precision={2} style={{ width: '100%' }} />
|
|
</Form.Item>
|
|
<Form.Item name="paidDate" label="缴纳日期" rules={[{ required: true }]}>
|
|
<DatePicker style={{ width: '100%' }} placeholder="选择缴纳日期" format="YYYY-MM-DD" />
|
|
</Form.Item>
|
|
<Form.Item name="notes" label="备注">
|
|
<Input.TextArea rows={2} />
|
|
</Form.Item>
|
|
</Form>
|
|
</Modal>
|
|
|
|
{/* Refund Modal */}
|
|
<Modal
|
|
title={`退还押金 - ${refundModal?.student?.name}`}
|
|
open={!!refundModal}
|
|
onOk={handleRefund}
|
|
onCancel={() => setRefundModal(null)}
|
|
okText="确认退还"
|
|
confirmLoading={saving}
|
|
>
|
|
<Form form={refundForm} layout="vertical">
|
|
<div style={{ marginBottom: 16, padding: 12, background: '#f5f5f5', borderRadius: 8 }}>
|
|
押金金额: <strong>¥{Number(refundModal?.amount || 0).toFixed(2)}</strong>
|
|
</div>
|
|
<Form.Item name="refundDate" label="退还日期" rules={[{ required: true }]}>
|
|
<DatePicker style={{ width: '100%' }} placeholder="选择退还日期" format="YYYY-MM-DD" />
|
|
</Form.Item>
|
|
<Form.Item name="deductionAmount" label="扣除金额(元)" extra="如无扣除填0">
|
|
<InputNumber
|
|
min={0}
|
|
max={Number(refundModal?.amount || 500)}
|
|
precision={2}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item name="deductionReason" label="扣除原因">
|
|
<Input placeholder="如:房间损坏赔偿" />
|
|
</Form.Item>
|
|
<Form.Item name="notes" label="备注">
|
|
<Input.TextArea rows={2} />
|
|
</Form.Item>
|
|
</Form>
|
|
</Modal>
|
|
|
|
{/* Detail Modal */}
|
|
<Modal
|
|
title={`押金详情 - ${detailModal?.student?.name}`}
|
|
open={!!detailModal}
|
|
onCancel={() => setDetailModal(null)}
|
|
footer={null}
|
|
width={640}
|
|
>
|
|
{detailModal && (
|
|
<div>
|
|
<Card size="small" style={{ marginBottom: 16 }}>
|
|
<p><strong>押金金额:</strong> ¥{Number(detailModal.amount).toFixed(2)}</p>
|
|
<p><strong>缴纳日期:</strong> {detailModal.paidDate}</p>
|
|
<p>
|
|
<strong>状态:</strong>{' '}
|
|
<Tag color={statusMap[detailModal.status]?.color}>
|
|
{statusMap[detailModal.status]?.text || detailModal.status}
|
|
</Tag>
|
|
</p>
|
|
{detailModal.refundStatus && (
|
|
<p>
|
|
<strong>退款审批:</strong>{' '}
|
|
<Tag color={refundStatusMap[detailModal.refundStatus]?.color}>
|
|
{refundStatusMap[detailModal.refundStatus]?.text || detailModal.refundStatus}
|
|
</Tag>
|
|
</p>
|
|
)}
|
|
{detailModal.notes && <p><strong>备注:</strong> {detailModal.notes}</p>}
|
|
</Card>
|
|
|
|
{/* Installments Section */}
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
|
<h4 style={{ margin: 0 }}>分期记录</h4>
|
|
<PermissionButton
|
|
permission="deposit:edit"
|
|
size="small"
|
|
type="primary"
|
|
icon={<PlusOutlined />}
|
|
onClick={() => {
|
|
setInstallmentModal(detailModal.id);
|
|
installmentForm.resetFields();
|
|
}}
|
|
>
|
|
添加分期
|
|
</PermissionButton>
|
|
</div>
|
|
{detailModal.installments?.length > 0 ? (
|
|
<List
|
|
dataSource={detailModal.installments}
|
|
renderItem={(item: any) => (
|
|
<List.Item
|
|
actions={[
|
|
item.status === 'pending' && (
|
|
<PermissionButton
|
|
key="pay"
|
|
permission="deposit:edit"
|
|
size="small"
|
|
type="primary"
|
|
icon={<DollarOutlined />}
|
|
onClick={() => handlePayInstallment(item.id)}
|
|
>
|
|
标记已缴
|
|
</PermissionButton>
|
|
),
|
|
<Popconfirm
|
|
title="确定删除?"
|
|
onConfirm={() => handleDeleteInstallment(item.id)}
|
|
>
|
|
<PermissionButton key="del" permission="deposit:delete" size="small" danger icon={<DeleteOutlined />}>
|
|
删除
|
|
</PermissionButton>
|
|
</Popconfirm>
|
|
].filter(Boolean)}
|
|
>
|
|
<List.Item.Meta
|
|
title={`¥${Number(item.amount).toFixed(2)}`}
|
|
description={`到期: ${item.dueDate}${item.paidDate ? ` | 缴纳: ${item.paidDate}` : ''}`}
|
|
/>
|
|
<Tag color={installmentStatusMap[item.status]?.color}>
|
|
{installmentStatusMap[item.status]?.text || item.status}
|
|
</Tag>
|
|
</List.Item>
|
|
)}
|
|
/>
|
|
) : (
|
|
<p style={{ color: '#999' }}>暂无分期记录</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
</Modal>
|
|
|
|
{/* Add Installment Modal */}
|
|
<Modal
|
|
title="添加分期"
|
|
open={installmentModal != null}
|
|
onOk={handleAddInstallment}
|
|
onCancel={() => setInstallmentModal(null)}
|
|
okText="确认"
|
|
>
|
|
<Form form={installmentForm} layout="vertical">
|
|
<Form.Item name="amount" label="分期金额(元)" rules={[{ required: true }]}>
|
|
<InputNumber min={0} precision={2} style={{ width: '100%' }} />
|
|
</Form.Item>
|
|
<Form.Item name="dueDate" label="到期日期" rules={[{ required: true }]}>
|
|
<DatePicker style={{ width: '100%' }} placeholder="选择到期日期" format="YYYY-MM-DD" />
|
|
</Form.Item>
|
|
</Form>
|
|
</Modal>
|
|
|
|
{/* Reject Refund Modal */}
|
|
<Modal
|
|
title={`驳回退款申请 - ${rejectModal?.student?.name || ''}`}
|
|
open={!!rejectModal}
|
|
onOk={handleRejectRefund}
|
|
onCancel={() => { setRejectModal(null); setRejectReason(''); }}
|
|
okText="确认驳回"
|
|
okButtonProps={{ danger: true }}
|
|
confirmLoading={saving}
|
|
>
|
|
<Input.TextArea
|
|
aria-label="驳回原因"
|
|
value={rejectReason}
|
|
onChange={(e) => setRejectReason(e.target.value)}
|
|
rows={3}
|
|
/>
|
|
</Modal>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DepositsPage;
|