diff --git a/src/pages/Contract/ContractList.tsx b/src/pages/Contract/ContractList.tsx new file mode 100644 index 0000000..f086af3 --- /dev/null +++ b/src/pages/Contract/ContractList.tsx @@ -0,0 +1,359 @@ +import React, {useState, useRef, useCallback} from 'react'; +import { history } from 'umi'; +import {Input, Button, Modal, Form, Select, message, DatePicker} from 'antd'; +import { SearchOutlined , UndoOutlined} from '@ant-design/icons'; +import ProTable, {ActionType, ProColumns} from '@ant-design/pro-table'; +import {saveDateTimeFormatter} from "@/utils/DateUtils"; +import { fetchContracts, deleteContract,updateContract, getContract } from "@/pages/Contract/ContractService"; +import moment from "moment"; +const { Option } = Select; + +interface Contract { + contractType: string; + contractCode: string; + contractName: string; + supplierName: string; + contractAmount: number; + supplierCreditCode: string; + status: string; + createTime: string; +} + +const contractTypeList = [ + { + label: '我已执行合同,录入阳光购系统', + value: '0' + }, + { + label: '我要与供应商进行在线合同磋商', + value: '1' + } +]; + + +const ContractList: React.FC = () => { + const [form] = Form.useForm(); + const [isModalVisible, setIsModalVisible] = useState(false); + const [searchParams, setSearchParams] = useState({ + contractCode: '', + contractName: '', + supplierName: '', + projectName: '', + biddingName: '', + createDate: undefined + }); + const actionRef = useRef(); + const handleEdit = useCallback((selectedRecord: any, opt: string) => { + getContract(selectedRecord.id).then((res) => { + history.push({ + pathname: '/stepOne', + state: { + contractInfo: res.data, + opt: opt + } + }); + }); + },[]); + + // 选择合同类型 + const handleChoose = () => { + // 重置表单到初始值 + form.resetFields(); + setIsModalVisible(true); + }; + + const createContract = async () => { + // 验证表单并获取值 + const values = await form.validateFields(); + //路由跳转 + history.push({ + pathname: '/stepOne', + state: { + contractType: values.contractType + } + }); + // 关闭模态框 + setIsModalVisible(false); + }; + + // 处理搜索 + const handleSearch = useCallback(() => { + if (searchParams.createDate) { + searchParams.createDate = saveDateTimeFormatter(moment(searchParams.createDate)) + setSearchParams({ + ...searchParams + }) + } + actionRef.current?.reload() + }, []); + + const goPerformance = useCallback((selectedRecord: any) => { + getContract(selectedRecord.id).then((res) => { + if(res?.code === 200){ + history.push({ + pathname: '/performance', + state: { + contractInfo: res.data, + opt: 'edit' + } + }); + } + }); + },[]) + + const handleUpdate = useCallback((optRecord: any) => { + updateContract(optRecord).then((r: any) => { + if (r?.code == 200) { + message.success('操作成功'); + } else { + message.error('操作失败'); + } + }).finally(() => actionRef.current?.reload()); + },[]) + + const handleOperation = useCallback((optRecord: any, status: string) => { + optRecord.status = status; + const operation = status == '2' ? '中止' : status == '3' ? '终止' : ''; + Modal.confirm({ + title: '确认' + operation + '该合同?', + onOk: async () => { + await handleUpdate(optRecord); + } + }) + },[]) + + const handleDelete = useCallback((id: string) => { + Modal.confirm({ + title: '确认删除该合同?', + onOk: async () => { + await deleteContract(id).then((r: any) => { + if (r?.code == 200) { + message.success('删除成功'); + } else { + message.error('删除失败'); + } + }) + .finally(() => actionRef.current?.reload()); + }, + }); + },[]) + + // 处理重置 + const handleReset = () => { + setSearchParams({ + contractCode: '', + contractName: '', + supplierName: '', + projectName: '', + biddingName: '', + createDate: undefined + }); + actionRef.current?.reload(); + }; + + // 定义ProTable的列配置 + const columns: ProColumns[] = [ + { + title: '合同/协议编号', + dataIndex: 'contractCode', + key: 'contractCode', + width: 150, + }, + { + title: '合同/协议名称', + dataIndex: 'contractName', + key: 'contractName', + width: 200, + }, + { + title: '供应商名称', + dataIndex: 'supplierName', + key: 'supplierName', + width: 180, + }, + { + title: '合同金额', + dataIndex: 'amount', + key: 'amount', + width: 120, + render: (text: any) => `${text}`, + }, + { + title: '创建时间', + dataIndex: 'createDate', + key: 'createDate', + width: 160, + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + width: 120, + render: (_: any, rec: Contract) => { + const status = rec.status;//合同状态 + if (status === '0') { + return (<>合同草稿已创建) + }else if (status === '1') { + return (<>合同执行中) + }else if (status === '2') { + return (<>合同中止) + }else if (status === '3') { + return (<>合同终止) + }else if (status === '4') { + return (<>合同结束) + } else { + return (<>) + } + } + }, + { + title: '操作', + key: 'operation', + width: 250, + valueType: 'option', + render: (text: any, optRecord: any) => ( + <> + {optRecord.status === '0' && ()} + {(optRecord.status === '0'|| optRecord.status === '2' )&& ()} + {optRecord.status === '1' && ()} + {optRecord.status === '1' && ()} + {optRecord.status === '1' && ()} + {()} + + ) + }, + ]; + + // 定义ProTable的请求函数 + const request = async (params: any, sorter: any) => { + try { + // 构建请求参数,合并搜索参数 + const requestParams = { + ...params, + ...searchParams, + basePageRequest: { + pageNo: params.current || 1, + pageSize: params.pageSize || 10 + }, + }; + const result = await fetchContracts(requestParams); + // 转换数据格式以适应ProTable的要求 + return { + data: result.data.records, + total: result.data.total, + success: true, + }; + } catch (error) { + message.error('获取合同列表失败'); + return { + data: [], + total: 0, + success: false, + }; + } + }; + + return ( +
+ setIsModalVisible(false)} + onOk={createContract} + okText="确定" + > +
+ + + +
+
+ + {/* 标题部分 */} +
+ 合同管理 +
+ + {/* 第一行搜索框 */} +
+ setSearchParams({...searchParams, contractCode: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + setSearchParams({...searchParams, contractName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + setSearchParams({...searchParams, supplierName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> +
+ + +
+
+ + {/* 第二行搜索框 */} +
+ setSearchParams({...searchParams, projectName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + setSearchParams({...searchParams, biddingName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + { + // @ts-ignore + setSearchParams({...searchParams, createDate: date})} + } style={{ width: 200, marginRight: 16 }}/> + +
+ + {/* 使用ProTable替代Table */} + + actionRef={actionRef}//action触发后更新表格 + columns={columns} + request={request} + rowKey="contractCode" + pagination={{ + pageSize: 10, + showSizeChanger: true, + pageSizeOptions: ['10', '20', '50', '100'], + }} + // 移除操作按钮 + options={false} + search={false} + /> +
+ ); +}; + +export default ContractList; diff --git a/src/pages/Contract/ContractService.ts b/src/pages/Contract/ContractService.ts new file mode 100644 index 0000000..aa34f47 --- /dev/null +++ b/src/pages/Contract/ContractService.ts @@ -0,0 +1,146 @@ +import request from "@/utils/request"; + +// 字典项类型定义 +export interface CoscoContract { + // 基本信息 + id: string; + contractType: number; + projectName: string; + biddingName: string; + biddingCode: string; + + // 采购单位信息 + purchaserName: string; + purchaserCode: string; + industryCode: string; + + // 供应商信息 + supplierId: string; + supplierName: string; + supplierCode: string; + signerName: string; + signerContact: string; + + // 合同基本信息 + frameworkAgreementId: string; + contractCode: string; + contractName: string; + categoryCode: string; + signedDatetime: string; // 日期时间通常在前端使用字符串表示 + valuationType: number; + amount: number; // BigDecimal 转为 number + payType: number; + startDatetime: string; + endDatetime: string; + + // 履约信息 + performancePlace: string; + guaranteeType: number; + note: string; + contactAccessory: string; +} + +// 获取字典列表 +// export const fetchDictionaries = async (): Promise => { +// const response = await axios.get('/api/sys-manager-ebtp-project/v1/dictProject/getDictList?parentCode=procurement_type&toParentCode=entrust'); +// return response.data.data; +// }; + +const prefix = '/api/biz-service-ebtp-project/'; + +export async function fetchContracts(params: any) { + return request(prefix + 'v1/contract/getPage', { + data: params, + method: 'POST' + }); +} + +export async function updateContract(params: any) { + return request(prefix + 'v1/contract/update', { + data: params, + method: 'POST' + }); +} + +export async function createContract(params: any) { + return request(prefix + 'v1/contract', { + data: params, + method: 'POST' + }); +} + +export async function getContract(param: any) { + return request(prefix + 'v1/contract/' + param, { + method: 'get' + }); +} + +export async function listNegotiate(params: any) { + return request(prefix + 'v1/negotiate/list', { + data: params, + method: 'POST' + }); +} + +export async function deleteContract(param: any) { + return request(prefix + 'v1/contract/delete/' + param, { + method: 'get', + }); +} + +export async function createPerformance(params: any) { + return request(prefix + 'v1/performance', { + data: params, + method: 'POST' + }); +} + +export async function updatePerformance(params: any) { + return request(prefix + 'v1/performance/update', { + data: params, + method: 'POST' + }); +} + +export async function createAmountItem(params: any) { + return request(prefix + 'v1/amount/item', { + data: params, + method: 'POST' + }); +} + +export async function updateAmountItem(params: any) { + return request(prefix + 'v1/amount/item/update', { + data: params, + method: 'POST' + }); +} + + +export async function createNegotiate(params: any) { + return request(prefix + 'v1/negotiate', { + data: params, + method: 'POST' + }); +} + +export async function updateNegotiate(params: any) { + return request(prefix + 'v1/negotiate/update', { + data: params, + method: 'POST' + }); +} + + +export async function deleteAmountItem(param: any) { + return request(prefix + 'v1/amount/item/delete/' + param, { + method: 'get', + }); +} + +export async function getPerformanceById(params: any) { + return request(prefix + 'v1/performance', { + params: params, + method: 'POST' + }); +} diff --git a/src/pages/Contract/Negotiate.tsx b/src/pages/Contract/Negotiate.tsx new file mode 100644 index 0000000..7b400bc --- /dev/null +++ b/src/pages/Contract/Negotiate.tsx @@ -0,0 +1,250 @@ +import React, {useEffect, useState} from 'react'; +import {Table, Button, Upload, message, Form} from 'antd'; +import {PlusOutlined, DeleteOutlined, SendOutlined, CheckOutlined} from '@ant-design/icons'; +import moment, {Moment} from "moment"; +import {createNegotiate, listNegotiate} from "@/pages/Contract/ContractService"; +import {useLocation} from "umi"; +import {saveDateTimeFormatter} from "@/utils/DateUtils"; +import TextArea from "antd/es/input/TextArea"; +import {history} from "@@/core/history"; + +const createDate = moment() + +const tableData: any[] | (() => any[]) = [ + // { + // key: '1', + // id: '', + // contractFileId: '', + // contractFileName: '2024年度汽车保养服务合同.doc', + // createBy: '集团', + // createDate: createDate, + // note: '我方已上传,请确认', + // }, + // { + // key: '2', + // id: '', + // contractFileId: '', + // contractFileName: '2024年度汽车保养服务合同-20241123.doc', + // createBy: '北京风向文化有限公司', + // createDate: createDate, + // note: '已对合同内容进行修改,请确认。', + // }, +]; +const Negotiate: React.FC = () => { + const location = useLocation(); + const contractInfo = location.state?.contractInfo; + const opt = location.state?.opt; + let readOnly = false; + if (opt === 'detail') { + readOnly = true; + } + // 模拟表格数据 + const [tableDataState, setTableDataState] = useState(tableData); + + // 处理TextArea内容变化 + const handleNoteChange = (e: React.ChangeEvent, key: string) => { + const newTableData = tableDataState.map(item => { + if (item.key === key) { + return {...item, note: e.target.value}; + } + return item; + }); + setTableDataState(newTableData); + }; + + const handleDelete = (key: string) => { + const newTableData = tableDataState.filter((row) => row.key !== key); + newTableData.forEach((item, index) => { + item.key = index.toString(); + }) + setTableDataState(newTableData); + }; + + // rowSelection object indicates the need for row selection + const rowSelection = { + onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => { + console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); + } + }; + + const fetchData = async () => { + try { + await listNegotiate({ + contractId: contractInfo.id + }).then((res) => { + if (res?.code === 200) { + setTableDataState(res.data); + if (res.data && res.data.length != 0) { + for (const i in res.data) { + const record = res.data[i]; + record.readOnly = true; + } + } + } + }); // 传递搜索参数 + + } catch (error) { + message.error('获取磋商信息列表失败'); + } finally { + } + }; + + useEffect(() => { + fetchData().then(res => {} ); + }, []); + + const saveNegotiateItem = (record: any) => { + delete record.key; + record.contractId = contractInfo.id; + record.createDate = saveDateTimeFormatter(moment(record.createDate)) + createNegotiate(record).then((res) => { + if (res?.code === 200) { + message.success("发送成功") + } else { + message.error("发送失败") + } + }) + } + // @ts-ignore + const columns: Table.ColumnType[] = [ + { + title: '序号', + dataIndex: 'key', + key: 'key', + render: (_: any, record: any, index: number) => index + 1, + }, + { + title: '合同文件名称', + dataIndex: 'contractFileName', + key: 'contractFileName', + render: (text: string) => ( + + {text ? {text} : ( + { + console.log('Uploaded file info:', info); + message.success('文件上传成功'); + }} + > + + + )} + + ), + }, + { + title: '上传方', + dataIndex: 'createBy', + key: 'createBy', + }, + { + title: '创建时间', + dataIndex: 'createDate', + key: 'createDate', + render: (value: Moment, record: any) => value ? moment(value).format('yyyy-MM-DD') : null + }, + { + title: '备注', + dataIndex: 'note', + key: 'note', + render: (text: string, record: any) => + ( +