|
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|
|
import { Table, Button, Space, Tag, Input, DatePicker, Select, Form, message, Modal, Typography, Divider, Card } from 'antd';
|
|
import { Table, Button, Space, Tag, Input, DatePicker, Select, Form, message, Modal, Typography, Divider, Card } from 'antd';
|
|
|
import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, ReloadOutlined, SaveOutlined, CloseOutlined } from '@ant-design/icons';
|
|
import { PlusOutlined, EditOutlined, DeleteOutlined, SearchOutlined, ReloadOutlined, SaveOutlined, CloseOutlined } from '@ant-design/icons';
|
|
|
import type { TableProps, FormProps } from 'antd';
|
|
import type { TableProps, FormProps } from 'antd';
|
|
|
-import { useRequest } from 'ahooks';
|
|
|
|
|
|
|
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
|
import { App } from 'antd';
|
|
import { App } from 'antd';
|
|
|
import type { InferResponseType, InferRequestType } from 'hono/client';
|
|
import type { InferResponseType, InferRequestType } from 'hono/client';
|
|
@@ -34,6 +34,7 @@ const { RangePicker } = DatePicker;
|
|
|
|
|
|
|
|
const ContractRenews: React.FC = () => {
|
|
const ContractRenews: React.FC = () => {
|
|
|
const { message: antMessage } = App.useApp();
|
|
const { message: antMessage } = App.useApp();
|
|
|
|
|
+ const queryClient = useQueryClient();
|
|
|
const [form] = Form.useForm();
|
|
const [form] = Form.useForm();
|
|
|
const [searchForm] = Form.useForm();
|
|
const [searchForm] = Form.useForm();
|
|
|
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
|
|
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
|
|
@@ -44,49 +45,52 @@ const ContractRenews: React.FC = () => {
|
|
|
const [total, setTotal] = useState<number>(0);
|
|
const [total, setTotal] = useState<number>(0);
|
|
|
|
|
|
|
|
// 获取合同列表
|
|
// 获取合同列表
|
|
|
- const { run: fetchContracts } = useRequest(async () => {
|
|
|
|
|
- const response = await contractClient.$get({
|
|
|
|
|
- query: { page: 1, pageSize: 1000 }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- if (!response.ok) {
|
|
|
|
|
- throw new Error('获取合同列表失败');
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const data = await response.json();
|
|
|
|
|
- return data.data;
|
|
|
|
|
- }, {
|
|
|
|
|
|
|
+ const { data: contractsData } = useQuery({
|
|
|
|
|
+ queryKey: ['contracts', 'all'],
|
|
|
|
|
+ queryFn: async () => {
|
|
|
|
|
+ const response = await contractClient.$get({
|
|
|
|
|
+ query: { page: 1, pageSize: 1000 }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (!response.ok) {
|
|
|
|
|
+ throw new Error('获取合同列表失败');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ return data.data;
|
|
|
|
|
+ },
|
|
|
onSuccess: (data) => {
|
|
onSuccess: (data) => {
|
|
|
setContracts(data);
|
|
setContracts(data);
|
|
|
},
|
|
},
|
|
|
onError: (error) => {
|
|
onError: (error) => {
|
|
|
- antMessage.error(`获取合同列表失败: ${error.message}`);
|
|
|
|
|
|
|
+ antMessage.error(`获取合同列表失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
|
},
|
|
},
|
|
|
- manual: false
|
|
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // 获取续签列表
|
|
|
|
|
- const { loading, run: fetchRenews } = useRequest(async (params: any) => {
|
|
|
|
|
- const response = await contractRenewClient.$get({
|
|
|
|
|
- query: params
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 获取续签列表
|
|
|
|
|
+ const fetchRenewsData = async (params: any) => {
|
|
|
|
|
+ const response = await contractRenewClient.$get({
|
|
|
|
|
+ query: params
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (!response.ok) {
|
|
|
|
|
+ throw new Error('获取续签记录失败');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return response.json();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const { data: renewsData, isLoading: loading, refetch: fetchRenews } = useQuery({
|
|
|
|
|
+ queryKey: ['contractRenews', currentPage, pageSize],
|
|
|
|
|
+ queryFn: () => fetchRenewsData({ page: currentPage, pageSize }),
|
|
|
|
|
+ onSuccess: (data: ContractRenewListResponse) => {
|
|
|
|
|
+ setTotal(data.pagination.total);
|
|
|
|
|
+ },
|
|
|
|
|
+ onError: (error) => {
|
|
|
|
|
+ antMessage.error(`获取续签记录失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
|
|
|
+ },
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
- if (!response.ok) {
|
|
|
|
|
- throw new Error('获取续签记录失败');
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return response.json();
|
|
|
|
|
- }, {
|
|
|
|
|
- onSuccess: (data: ContractRenewListResponse) => {
|
|
|
|
|
- setTotal(data.pagination.total);
|
|
|
|
|
- return data;
|
|
|
|
|
- },
|
|
|
|
|
- onError: (error) => {
|
|
|
|
|
- antMessage.error(`获取续签记录失败: ${error.message}`);
|
|
|
|
|
- },
|
|
|
|
|
- manual: true
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
// 搜索功能
|
|
// 搜索功能
|
|
|
const handleSearch = async () => {
|
|
const handleSearch = async () => {
|
|
|
try {
|
|
try {
|
|
@@ -110,7 +114,7 @@ const ContractRenews: React.FC = () => {
|
|
|
params.endDate = values.dateRange[1].format('YYYY-MM-DD');
|
|
params.endDate = values.dateRange[1].format('YYYY-MM-DD');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fetchRenews(params);
|
|
|
|
|
|
|
+ fetchRenews({ ...params });
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
antMessage.error('搜索参数验证失败');
|
|
antMessage.error('搜索参数验证失败');
|
|
|
}
|
|
}
|
|
@@ -127,10 +131,8 @@ const ContractRenews: React.FC = () => {
|
|
|
setCurrentPage(pagination.current || 1);
|
|
setCurrentPage(pagination.current || 1);
|
|
|
setPageSize(pagination.pageSize || 10);
|
|
setPageSize(pagination.pageSize || 10);
|
|
|
|
|
|
|
|
- fetchRenews({
|
|
|
|
|
- page: pagination.current || 1,
|
|
|
|
|
- pageSize: pagination.pageSize || 10,
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ setCurrentPage(pagination.current || 1);
|
|
|
|
|
+ setPageSize(pagination.pageSize || 10);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 打开新增/编辑模态框
|
|
// 打开新增/编辑模态框
|
|
@@ -196,7 +198,7 @@ const ContractRenews: React.FC = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
setIsModalVisible(false);
|
|
setIsModalVisible(false);
|
|
|
- fetchRenews({ page: currentPage, pageSize }); // 重新加载数据
|
|
|
|
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['contractRenews'] }); // 重新加载数据
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
antMessage.error(`操作失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
antMessage.error(`操作失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
|
}
|
|
}
|
|
@@ -220,7 +222,7 @@ const ContractRenews: React.FC = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
antMessage.success('续签记录删除成功');
|
|
antMessage.success('续签记录删除成功');
|
|
|
- fetchRenews({ page: currentPage, pageSize }); // 重新加载数据
|
|
|
|
|
|
|
+ queryClient.invalidateQueries({ queryKey: ['contractRenews'] }); // 重新加载数据
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
antMessage.error(`删除失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
antMessage.error(`删除失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
|
|
}
|
|
}
|
|
@@ -230,8 +232,7 @@ const ContractRenews: React.FC = () => {
|
|
|
|
|
|
|
|
// 初始化加载数据
|
|
// 初始化加载数据
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- fetchContracts();
|
|
|
|
|
- fetchRenews({ page: 1, pageSize });
|
|
|
|
|
|
|
+ // 初始加载由useQuery自动触发
|
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
// 表格列定义
|
|
// 表格列定义
|
|
@@ -248,6 +249,73 @@ const ContractRenews: React.FC = () => {
|
|
|
dataIndex: ['contract', 'contractNo'],
|
|
dataIndex: ['contract', 'contractNo'],
|
|
|
key: 'contractNo',
|
|
key: 'contractNo',
|
|
|
width: 150,
|
|
width: 150,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '客户名称',
|
|
|
|
|
+ dataIndex: ['contract', 'customer', 'name'],
|
|
|
|
|
+ key: 'customerName',
|
|
|
|
|
+ width: 180,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '续签开始日期',
|
|
|
|
|
+ dataIndex: 'renewStartDate',
|
|
|
|
|
+ key: 'renewStartDate',
|
|
|
|
|
+ width: 120,
|
|
|
|
|
+ render: (date: string) => date ? dayjs(date).format('YYYY-MM-DD') : '-',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '续签结束日期',
|
|
|
|
|
+ dataIndex: 'renewEndDate',
|
|
|
|
|
+ key: 'renewEndDate',
|
|
|
|
|
+ width: 120,
|
|
|
|
|
+ render: (date: string) => date ? dayjs(date).format('YYYY-MM-DD') : '-',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '续签金额',
|
|
|
|
|
+ dataIndex: 'renewAmount',
|
|
|
|
|
+ key: 'renewAmount',
|
|
|
|
|
+ width: 100,
|
|
|
|
|
+ render: (amount: number) => `¥${amount.toFixed(2)}`,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '状态',
|
|
|
|
|
+ dataIndex: 'status',
|
|
|
|
|
+ key: 'status',
|
|
|
|
|
+ width: 100,
|
|
|
|
|
+ render: (status: number) => {
|
|
|
|
|
+ const statusInfo = RenewStatusEnum[status];
|
|
|
|
|
+ return statusInfo ? (
|
|
|
|
|
+ <Tag color={statusInfo.color} key={status}>
|
|
|
|
|
+ {statusInfo.label}
|
|
|
|
|
+ </Tag>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <Tag color="default">未知</Tag>
|
|
|
|
|
+ );
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '操作',
|
|
|
|
|
+ key: 'action',
|
|
|
|
|
+ width: 150,
|
|
|
|
|
+ render: (_: any, record: ContractRenewItem) => (
|
|
|
|
|
+ <Space size="middle">
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ icon={<EditOutlined />}
|
|
|
|
|
+ onClick={() => showModal(record)}
|
|
|
|
|
+ >
|
|
|
|
|
+ 编辑
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ danger
|
|
|
|
|
+ icon={<DeleteOutlined />}
|
|
|
|
|
+ onClick={() => handleDelete(record.id)}
|
|
|
|
|
+ >
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </Space>
|
|
|
|
|
+ ),
|
|
|
sorter: true,
|
|
sorter: true,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|