refactor: resolve remaining field audit issues

This commit is contained in:
2026-07-13 15:12:36 +08:00
parent 0533c30ece
commit aa1ed7db56
34 changed files with 953 additions and 263 deletions

View File

@@ -15,7 +15,7 @@ import {
Tooltip,
Empty,
} from 'antd';
import { PlusOutlined, UploadOutlined, DeleteOutlined, FileTextOutlined } from '@ant-design/icons';
import { PlusOutlined, UploadOutlined, DeleteOutlined, FileTextOutlined, StopOutlined, CheckOutlined } from '@ant-design/icons';
import dayjs, { Dayjs } from 'dayjs';
import api from '../../api';
import { downloadBlob } from '../../utils/download';
@@ -39,6 +39,7 @@ const ClassroomRentalsPage: React.FC = () => {
const [editing, setEditing] = useState<any>(null);
const [form] = Form.useForm();
const [filterMonth, setFilterMonth] = useState<Dayjs | null>(null);
const [filterStatus, setFilterStatus] = useState<string | undefined>();
const [searchText, setSearchText] = useState('');
const [saving, setSaving] = useState(false);
const [unavailableDates, setUnavailableDates] = useState<Set<string>>(new Set());
@@ -48,20 +49,22 @@ const ClassroomRentalsPage: React.FC = () => {
const selectedClassroomId = Form.useWatch('classroomId', form);
const filteredData = useMemo(() => {
if (!searchText) return data;
const s = searchText.toLowerCase();
return data.filter((r: any) => {
if (filterStatus && r.effectiveStatus !== filterStatus) return false;
if (!searchText) return true;
const s = searchText.toLowerCase();
const matchClassroom = r.classroom?.name?.toLowerCase().includes(s);
const matchOrganization = r.lesseeOrganization?.name?.toLowerCase().includes(s);
return matchClassroom || matchOrganization;
});
}, [data, searchText]);
}, [data, searchText, filterStatus]);
const fetchData = async () => {
setLoading(true);
try {
const params: any = {};
if (filterMonth) params.month = filterMonth.format('YYYY-MM');
params.includeEnded = true;
const res: any = await api.get('/classroom-rentals', { params });
setData(res);
} catch (e: any) {
@@ -215,6 +218,16 @@ const ClassroomRentalsPage: React.FC = () => {
}
};
const handleRentalAction = async (id: number, action: 'cancel' | 'end') => {
try {
await api.put(`/classroom-rentals/${id}/${action}`);
message.success(action === 'cancel' ? '租赁已取消' : '租赁已结束');
fetchData();
} catch (e: any) {
message.error(e?.message || '操作失败');
}
};
const handleDownloadContract = async (id: number, filename?: string) => {
try {
await downloadBlob(`/classroom-rentals/${id}/contract`, filename || `contract-${id}.pdf`);
@@ -308,6 +321,19 @@ const ClassroomRentalsPage: React.FC = () => {
width: 100,
render: (v: any) => (v ? `¥${v}` : '-'),
},
{
title: '状态',
dataIndex: 'effectiveStatus',
width: 90,
render: (status: string) => {
const config: Record<string, { text: string; color: string }> = {
active: { text: '进行中', color: 'green' },
ended: { text: '已结束', color: 'default' },
cancelled: { text: '已取消', color: 'red' },
};
return <Tag color={config[status]?.color}>{config[status]?.text || status}</Tag>;
},
},
{
title: '合同',
width: 120,
@@ -364,21 +390,30 @@ const ClassroomRentalsPage: React.FC = () => {
width: 150,
render: (_: any, record: any) => (
<Space>
<PermissionButton
permission="rental:edit"
size="small"
onClick={() => openEdit(record)}
>
</PermissionButton>
<Popconfirm
title="确定删除该租赁订单?合同文件将一并删除。"
onConfirm={() => handleDelete(record.id)}
>
<PermissionButton permission="rental:delete" size="small" danger>
</PermissionButton>
</Popconfirm>
{record.effectiveStatus === 'active' && (
<>
<PermissionButton permission="rental:edit" size="small" onClick={() => openEdit(record)}>
</PermissionButton>
<Popconfirm title="确定取消该租赁?" onConfirm={() => handleRentalAction(record.id, 'cancel')}>
<PermissionButton permission="rental:edit" size="small" danger icon={<StopOutlined />}>
</PermissionButton>
</Popconfirm>
{!dayjs(record.startDate).isAfter(dayjs(), 'day') && (
<Popconfirm title="确定今天结束该租赁?" onConfirm={() => handleRentalAction(record.id, 'end')}>
<PermissionButton permission="rental:edit" size="small" icon={<CheckOutlined />}>
</PermissionButton>
</Popconfirm>
)}
</>
)}
{record.effectiveStatus !== 'active' && (
<Popconfirm title="确定删除该租赁订单?合同文件将一并删除。" onConfirm={() => handleDelete(record.id)}>
<PermissionButton permission="rental:delete" size="small" danger></PermissionButton>
</Popconfirm>
)}
</Space>
),
},
@@ -415,6 +450,18 @@ const ClassroomRentalsPage: React.FC = () => {
allowClear
format="YYYY-MM"
/>
<Select
placeholder="状态"
allowClear
style={{ width: 110 }}
value={filterStatus}
onChange={setFilterStatus}
options={[
{ value: 'active', label: '进行中' },
{ value: 'ended', label: '已结束' },
{ value: 'cancelled', label: '已取消' },
]}
/>
</Space>
<PermissionButton
permission="rental:create"
@@ -459,7 +506,7 @@ const ClassroomRentalsPage: React.FC = () => {
optionFilterProp="label"
placeholder="选择教室"
onChange={handleClassroomChange}
options={classrooms.map((c) => ({
options={classrooms.filter((c) => c.status === 'available').map((c) => ({
value: c.id,
label: `${c.building ? c.building + ' · ' : ''}${c.name}${c.roomType}`,
}))}