diff --git a/src/components/CompanyInfo/_mock.ts b/src/components/CompanyInfo/_mock.ts new file mode 100644 index 0000000..a91db54 --- /dev/null +++ b/src/components/CompanyInfo/_mock.ts @@ -0,0 +1,343 @@ +import { Request, Response } from 'express'; + +// const data = [ +// { +// title: 'Name', +// dataIndex: '评审项', +// key: '评审项', +// }, +// { +// title: 'Other', +// children: [ +// { +// title: 'Age', +// key: '人员A', +// }, +// { +// title: 'Address', +// key: '人员B', +// }, +// ] +// }, +// ] +const dataInvoiceInfo = [ + { + id: '1', + taxpayerType: '一般纳税人', + taxpayerCode: '91345678901234567X', + head: '北京某科技有限公司', + address: '北京市朝阳区XX路99号', + phone: '010-12345678', + bank: '中国银行北京分行', + account: '6228888888888888', + updateTime: '2025-06-17 10:20:00', + voided: false, + qualificationCertificate: 'https://example.com/cert1.pdf', + }, + { + id: '2', + taxpayerType: '小规模纳税人', + taxpayerCode: '91345678901234566Y', + head: '上海某信息技术有限公司', + address: '上海市浦东新区XX大厦8楼', + phone: '021-87654321', + bank: '工商银行上海分行', + account: '6229999999999999', + updateTime: '2025-06-16 15:30:00', + voided: true, + qualificationCertificate: '', + }, +] +const mockQualificationData = [ + { + id: '1', + certificateType: '建筑业企业资质证书', + name: '建筑工程施工总承包一级', + code: 'ZJ-A123456', + typeLevel: '一级', + authority: '住房和城乡建设部', + dateTime: '2023-03-01', + termOfValidity: '2028-03-01', + updateTime: '2025-06-17 10:30:00', + }, + { + id: '2', + certificateType: '安全生产许可证', + name: '施工企业安全生产许可证', + code: 'AQ-789012', + typeLevel: 'A级', + authority: '应急管理部', + dateTime: '2022-06-15', + termOfValidity: '2025-06-15', + updateTime: '2025-06-17 11:45:00', + }, +] +export const mockData = { + base: { + "id": "123456", + "supplierType": "dvs", + "licenceAccessory": "https://example.com/license.pdf", + "licenceDate": "2025-12-31", + "enterpriseType": "company", + "name": "深圳供应商有限公司", + "nameEn": "Shenzhen Supplier Co., Ltd.", + "socialCreditCode": "91440300MA5F3XXXXQ", + "range": "电子元器件、金属材料销售", + "regAddress": "广东省深圳市南山区科技园", + "workAddress": "广东省深圳市南山区软件产业基地", + "parentCompanyInvestor": "深圳控股集团有限公司", + "legalPerson": "李四", + "idCard": "440301199001015678", + "capital": 5000, + "contactsName": "王五", + "contactsPhone": "13800138000", + "contactsType": "法人代表", + "contactsEmail": "contact@supplier.com", + "telephone": "0755-12345678", + "nation": "新加坡", + "vat": "SG12345678VAT", + "taxpayerId": "SG-TAX-998877", + "currency": "SGD", + "personName": "张三", + "personPhone": "13812345678", + "personBank": "中国银行深圳分行", + "personAccount": "6222020200123456789", + "remark": "该供应商已完成初步审核", + "accessStatus": 1, + "blacklistStatus": 0, + "greylistStatus": 1, + "fillinStatus": 0, + "fillinBy": "", + "sapCode": "SAP998877", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }, + qualifications: [{ + "id": "cert-001", + "supplierId": "supplier-123456", + "certificateType": "安全生产许可证", + "name": "建筑施工总承包一级资质", + "code": "ZJ20230605001", + "typeLevel": "一级", + "authority": "住房和城乡建设部", + "dateTime": "2023-06-05", + "termOfValidity": "2026-06-05", + "accessory": "https://example.com/certificate.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2023-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2024-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }], + invoice: { + "id": "invoice-001", + "supplierId": "supplier-123456", + "taxpayerType": "general", + "taxpayerCode": "91440300MA5F3XXXXQ", + "phone": "0755-12345678", + "account": "6222020200123456789", + "head": "深圳供应商有限公司", + "address": "深圳市南山区科技园开票楼101号", + "bank": "中国银行深圳分行", + "qualificationCertificate": "https://example.com/tax-cert.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }, + bank: [{ + "id": "bank-001", + "supplierId": "supplier-123456", + "interbankNumber": "123456789012", + "bank": "中国银行深圳分行", + "swiftCode": "BKCHCNBJ45A", + "accountName": "Shenzhen Supplier Co., Ltd.", + "account": "6222020200123456789", + "currency": "CNY", + "nation": "中国", + "province": "广东省", + "city": "深圳市", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }], + survey: { + "supplierName": "深圳供应商有限公司", + "name": "李四", + "position": "采购经理", + "phone": "13800138000", + "email": "lisi@supplier.com", + "dateTime": "2025-06-17", + }, + questionReply: [ + { + "surveyQuestion": "法律法规:\n我们确保经营和提供的产品服务遵守国家及 各业务所在地的所有使用法律、法规", + "replyValue": "是", + },{ + "surveyQuestion": "健康和安全:\n我们为员工提供符合法律法规的安全且健康 的工作场所。我们建立安全管理体系,并向 员工传达工作场所或生活设施的健康和安全 标准,致力于减少工作对员工造成的伤害和 疾病。", + "replyValue": "符合", + },{ + "surveyQuestion": "环境:\n我们能够以环境友好的方式经营。我们遵守 适用的环境法律、法规和标准;并建立有效 的环境管理体系。\n我们遵守贵集团对相关产品或服务的部分附 加环境要求,这些要求和规定体现在设计与 产品规范的合同文档中。", + "replyValue": "符合", + },{ + "surveyQuestion": "监督和记录:\n我们保留记录遵守相关法律和此行为准则的必要文件,并根据要求为贵集团提供对文件的查看权。我们会允许贵集团在适当的时候,以验证行为准则执行为目的的现场勘查", + "replyValue": "符合", + } + ], + attachments: { + "attachmentsType": "commitment", + "fileName": "anti-bribery-commitment.pdf", + "fileType": "pdf", + "fileSize": "204800", + "filePath": "/data/files/anti-bribery-commitment.pdf", + "fileUrl": "http://example.com/files/anti-bribery-commitment.pdf", + } +}; +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + // 供应商信息 + 'GET /api/system/coscoSupplier': (req: Request, res: Response) => { + res.json({ + code: 200, + data: mockData, + msg: '操作成功' + }); + }, + // + 'GET /api/system/qualifications': (req: Request, res: Response) => { + res.json({ code: 200, + data: mockQualificationData, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/invoice': (req: Request, res: Response) => { + res.json({ code: 200, + data: dataInvoiceInfo, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/bank': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + id: '1', + interbankNumber: '123456789', + bank: '中国银行', + accountName: '张三', + account: '6228480000000000000', + currency: '人民币', + nation: '中国', + province: '广东省', + city: '广州市', + updateTime: '2024-06-18', + }, + { + id: '2', + interbankNumber: '987654321', + bank: '工商银行', + accountName: '李四', + account: '6228480000000000001', + currency: '美元', + nation: '中国', + province: '江苏省', + city: '南京市', + updateTime: '2024-06-17', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/tianyancha': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + key: '1', + base: '京', + name: '北京科技有限公司', + legalPersonName: '张三', + legalPersonType: '1', + regNumber: '110108123456789', + industry: '信息技术', + companyOrgType: '有限责任公司', + regLocation: '北京市海淀区中关村', + estiblishTime: '2010-06-15', + fromTime: '2010-06-16', + toTime: '2025-06-15', + businessScope: '软件开发、技术咨询', + approvedTime: '2010-06-10', + regStatus: '存续', + regCapital: '5000万元', + regInstitute: '北京市工商局', + orgNumber: '1234567890', + creditCode: '91110108MA01A12345', + property3: 'Beijing Tech Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1001', + taxNumber: '110108123456789', + email: 'contact@bjtech.com', + website: 'http://www.bjtech.com', + phoneNumber: '010-12345678', + lastUpdateTime: '2025-06-15 10:00:00', + }, + { + key: '2', + base: '沪', + name: '上海电子商务有限公司', + legalPersonName: '李四', + legalPersonType: '1', + regNumber: '310101987654321', + industry: '电子商务', + companyOrgType: '股份有限公司', + regLocation: '上海市浦东新区', + estiblishTime: '2015-03-20', + fromTime: '2015-03-21', + toTime: '2030-03-20', + businessScope: '电子商务平台运营、广告设计', + approvedTime: '2015-03-15', + regStatus: '存续', + regCapital: '1亿元', + regInstitute: '上海市工商局', + orgNumber: '0987654321', + creditCode: '91310101MA1AB23456', + property3: 'Shanghai E-commerce Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1002', + taxNumber: '310101987654321', + email: 'info@shcommerce.com', + website: 'http://www.shcommerce.com', + phoneNumber: '021-87654321', + lastUpdateTime: '2025-06-15 09:30:00', + }, + ], + total: 2, + msg: '操作成功' }); + }, + + 'GET /api/500': (req: Request, res: Response) => { + res.status(500).send({ + timestamp: 1513932555104, + status: 500, + error: 'error', + message: 'error', + path: '/base/category/list', + }); + }, + + +}; diff --git a/src/components/CompanyInfo/component/BankInfoTab.tsx b/src/components/CompanyInfo/component/BankInfoTab.tsx new file mode 100644 index 0000000..e9f7410 --- /dev/null +++ b/src/components/CompanyInfo/component/BankInfoTab.tsx @@ -0,0 +1,120 @@ +import React, { useEffect, useState } from 'react'; +import { Table } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { bank } from '../services'; +import { useIntl } from 'umi'; + +interface BankInfo { + id: string; + interbankNumber: string; + bank: string; + accountName: string; + account: string; + currency: string; + nation: string; + province: string; + city: string; + updateTime: string; +} +// 表格头部 +const columns: ColumnsType = [ + { + title: 'page.workbench.bank.index', + dataIndex: 'index', + key: 'index', + width: 80, + align: 'center', + render: (_: any, __: any, index: number) => index + 1 + }, + { + title: 'page.workbench.bank.interbankNumber', + dataIndex: 'interbankNumber', + key: 'interbankNumber', + }, + { + title: 'page.workbench.bank.bank', + dataIndex: 'bank', + key: 'bank', + }, + { + title: 'page.workbench.bank.accountName', + dataIndex: 'accountName', + key: 'accountName', + }, + { + title: 'page.workbench.bank.account', + dataIndex: 'account', + key: 'account', + }, + { + title: 'page.workbench.bank.currency', + dataIndex: 'currency', + key: 'currency', + }, + { + title: 'page.workbench.bank.nation', + dataIndex: 'nation', + key: 'nation', + }, + { + title: 'page.workbench.bank.province', + dataIndex: 'province', + key: 'province', + }, + { + title: 'page.workbench.bank.city', + dataIndex: 'city', + key: 'city', + }, + { + title: 'page.workbench.bank.updateTime', + dataIndex: 'updateTime', + key: 'updateTime', + }, +]; +const BankInfoTab: React.FC = () => { + //双语 + const intl = useIntl(); + //列表渲染数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //列表方法 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, total } = await bank({page, pageSize}); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + getList(); + }, []); + + return ( +
+ ({ + ...column, + title: intl.formatMessage({ id: column.title as string }) + }))} + dataSource={data} + pagination={pagination} + loading={loading} + onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)} + /> + + ); +}; + +export default BankInfoTab; \ No newline at end of file diff --git a/src/components/CompanyInfo/component/BaseInfoTab.tsx b/src/components/CompanyInfo/component/BaseInfoTab.tsx new file mode 100644 index 0000000..9cf8058 --- /dev/null +++ b/src/components/CompanyInfo/component/BaseInfoTab.tsx @@ -0,0 +1,106 @@ +import React, { useEffect, useState } from 'react'; +import { Descriptions } from 'antd'; +import { coscoSupplier } from '../services'; +import { useIntl } from 'umi'; + +const BaseInfoTab: React.FC = () => { + const intl = useIntl(); + const [registerInfo, setRegisterInfo] = useState({ base: {} }); + + const fetchData = async () => { + const res = await coscoSupplier({}); + if (res.code === 200) { + setRegisterInfo(res.data); + } + }; + + useEffect(() => { + //供应商信息 + fetchData() + }, []); + + if (!registerInfo?.base) return
{intl.formatMessage({ id: 'component.globalModal.loading' })}...
; + + return ( +
+ + {registerInfo.base.supplierType === 'dvs' && ( + + {intl.formatMessage({ id: 'component.globalModal.domesticEnterprise' })} + + )} + + {registerInfo.base.name} + + + {registerInfo.base.nameEn} + + {registerInfo.base.supplierType !== 'dvs' && ( + <> + + {registerInfo.base.nation} + + + {registerInfo.base.vat} + + + {registerInfo.base.taxpayerId} + + + {registerInfo.base.currency} + + + )} + + {registerInfo.base.socialCreditCode} + + + {registerInfo.base.range} + + + {registerInfo.base.regAddress} + + + {registerInfo.base.workAddress} + + + {registerInfo.base.parentCompanyInvestor} + + + {registerInfo.base.legalPerson} + + + {registerInfo.base.idCard} + + + {registerInfo.base.capital} + + + {registerInfo.base.enterpriseType} + + + {registerInfo.base.contactsName} + + + {registerInfo.base.contactsPhone} + + + {registerInfo.base.contactsType} + + + {registerInfo.base.contactsEmail} + + + {registerInfo.base.telephone} + + +
+ ); +}; + +export default BaseInfoTab; diff --git a/src/components/CompanyInfo/component/ContactsInfoTab.tsx b/src/components/CompanyInfo/component/ContactsInfoTab.tsx new file mode 100644 index 0000000..5566cbe --- /dev/null +++ b/src/components/CompanyInfo/component/ContactsInfoTab.tsx @@ -0,0 +1,117 @@ +import React, { useEffect, useState } from 'react'; +import { Table } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { coscoSupplier } from '../services'; +import { useIntl } from 'umi'; + +// 联系人信息接口 +interface Contact { + id: string; + name: string; + department: string; + position: string; + mobile: string; + phone: string; + email: string; + updateTime: string; +} + +const ContactsInfoTab: React.FC = () => { + const intl = useIntl(); + const [data, setData] = useState([]); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 10, + total: 0, + }); + const [loading, setLoading] = useState(false); + + const fetchContacts = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const res = await coscoSupplier(page); + if (res.code === 200) { + setData(res.contacts || []); + setPagination({ + current: page, + pageSize, + total: res.totalContacts || (res.contacts?.length || 0), + }); + } + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchContacts(); + }, []); + + const handleTableChange = (pagination: TablePaginationConfig) => { + fetchContacts(pagination.current!, pagination.pageSize!); + }; + + const columns: ColumnsType = [ + { + title: intl.formatMessage({ id: 'page.workbench.contacts.index' }), + dataIndex: 'index', + key: 'index', + width: 60, + align: 'center', + render: (_: any, __: any, index: number) => + (pagination.current! - 1) * pagination.pageSize! + index + 1, + }, + { + title: intl.formatMessage({ id: 'page.workbench.contacts.name' }), + dataIndex: 'name', + key: 'name', + }, + { + title: intl.formatMessage({ id: 'page.workbench.contacts.department' }), + dataIndex: 'department', + key: 'department', + }, + { + title: intl.formatMessage({ id: 'page.workbench.contacts.position' }), + dataIndex: 'position', + key: 'position', + }, + { + title: intl.formatMessage({ id: 'page.workbench.contacts.mobile' }), + dataIndex: 'mobile', + key: 'mobile', + }, + { + title: intl.formatMessage({ id: 'page.workbench.contacts.phone' }), + dataIndex: 'phone', + key: 'phone', + }, + { + title: intl.formatMessage({ id: 'page.workbench.contacts.email' }), + dataIndex: 'email', + key: 'email', + }, + { + title: intl.formatMessage({ id: 'page.workbench.contacts.updateTime' }), + dataIndex: 'updateTime', + key: 'updateTime', + }, + ]; + + return ( +
+
+ + ); +}; + +export default ContactsInfoTab; \ No newline at end of file diff --git a/src/components/CompanyInfo/component/InvoiceTab.tsx b/src/components/CompanyInfo/component/InvoiceTab.tsx new file mode 100644 index 0000000..776abaf --- /dev/null +++ b/src/components/CompanyInfo/component/InvoiceTab.tsx @@ -0,0 +1,83 @@ +import React, { useEffect, useState } from 'react'; +import { Table } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { invoice } from '../services'; +import { useIntl } from 'umi'; + +interface InvoiceInfo { + id: string; + taxpayerType: string; + taxpayerCode: string; + head: string; + address: string; + phone: string; + bank: string; + account: string; + updateTime: string; + certificateUrl: string; +} + +const columns: ColumnsType = [ + { title: 'page.workbench.invoice.index', dataIndex: 'index', width: 80, key: 'index', render: (_: any, __: any, index: number) => index + 1 }, + { title: 'page.workbench.invoice.taxpayerType', dataIndex: 'taxpayerType' }, + { title: 'page.workbench.invoice.taxpayerCode', dataIndex: 'taxpayerCode' }, + { title: 'page.workbench.invoice.head', dataIndex: 'head' }, + { title: 'page.workbench.invoice.address', dataIndex: 'address' }, + { title: 'page.workbench.invoice.phone', dataIndex: 'phone' }, + { title: 'page.workbench.invoice.bank', dataIndex: 'bank' }, + { title: 'page.workbench.invoice.account', dataIndex: 'account' }, + { title: 'page.workbench.invoice.updateTime', dataIndex: 'updateTime' }, + { + title: 'page.workbench.invoice.qualificationCertificate', + width:'180px', + dataIndex: 'qualificationCertificate', + render: (val: string) => (val ? 查看附件 : '-'), + }, +]; + +const InvoiceTab: React.FC = () => { + //语言切换 + const intl = useIntl(); + //列表渲染数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //列表方法 + const getList = async (page = 1, pageSize = 10) => { + setLoading(true); + try { + const { code, data, total } = await invoice({page, pageSize}); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + getList(); + }, []); + + return ( +
+
({ + ...column, + title: intl.formatMessage({ id: column.title as string }) + }))} + dataSource={data} + pagination={pagination} + loading={loading} + onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)} + /> + + ); +}; + +export default InvoiceTab; \ No newline at end of file diff --git a/src/components/CompanyInfo/component/OtherAttachmentsTab.tsx b/src/components/CompanyInfo/component/OtherAttachmentsTab.tsx new file mode 100644 index 0000000..f589d30 --- /dev/null +++ b/src/components/CompanyInfo/component/OtherAttachmentsTab.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useState } from 'react'; +import { Table } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import { coscoSupplier } from '../services'; +import { useIntl } from 'umi'; + +interface AttachmentItem { + id: string; + fileName: string; + fileType: string; + uploadTime: string; + uploader: string; + fileUrl: string; +} + +const OtherAttachmentsTab: React.FC = () => { + const intl = useIntl(); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + setLoading(true); + coscoSupplier(1).then((res: any) => { + setLoading(false); + if (res.code === 200) { + setData(res.otherAttachments || []); + } + }); + }, []); + + const columns: ColumnsType = [ + { + title: intl.formatMessage({ id: 'page.workbench.attachments.index' }), + render: (_: any, __: any, index: number) => index + 1, + width: 60, + }, + { + title: intl.formatMessage({ id: 'page.workbench.attachments.fileName' }), + dataIndex: 'fileName', + }, + { + title: intl.formatMessage({ id: 'page.workbench.attachments.fileType' }), + dataIndex: 'fileType', + }, + { + title: intl.formatMessage({ id: 'page.workbench.attachments.uploadTime' }), + dataIndex: 'uploadTime', + }, + { + title: intl.formatMessage({ id: 'page.workbench.attachments.uploader' }), + dataIndex: 'uploader', + }, + { + title: intl.formatMessage({ id: 'page.workbench.attachments.action' }), + dataIndex: 'fileUrl', + render: (val: string) => (val ? 下载 : '-'), + }, + ]; + + return ( +
+
+ + ); +}; + +export default OtherAttachmentsTab; \ No newline at end of file diff --git a/src/components/CompanyInfo/component/QualificationTab.tsx b/src/components/CompanyInfo/component/QualificationTab.tsx new file mode 100644 index 0000000..c5fddc4 --- /dev/null +++ b/src/components/CompanyInfo/component/QualificationTab.tsx @@ -0,0 +1,75 @@ +import React, { useEffect, useState } from 'react'; +import { Table } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { qualifications } from '../services'; +import { useIntl } from 'umi'; + +interface Qualification { + id: string; + certificateType: string; + name: string; + code: string; + typeLevel: string; + authority: string; + dateTime: string; + termOfValidity: string; + updateTime: string; +} +// 列表头部信息 +const columns: ColumnsType = [ + { title: 'page.workbench.certificateType', dataIndex: 'certificateType' }, + { title: 'page.workbench.certificateName', dataIndex: 'name' }, + { title: 'page.workbench.certificateCode', dataIndex: 'code' }, + { title: 'page.workbench.typeLevel', dataIndex: 'typeLevel' }, + { title: 'page.workbench.authority', dataIndex: 'authority' }, + { title: 'page.workbench.dateTime', dataIndex: 'dateTime' }, + { title: 'page.workbench.termOfValidity', dataIndex: 'termOfValidity' }, + { title: 'page.workbench.updateTime', dataIndex: 'updateTime' }, +]; + +const QualificationTab: React.FC = () => { + //语言切换 + const intl = useIntl(); + //列表渲染数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //列表方法 + const getList = async (page = 1, pageSize = 10) => { + setLoading(true); + try { + const { code, data, total } = await qualifications({page, pageSize}); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + getList(); + }, []); + + return ( +
+
({ + ...column, + title: intl.formatMessage({ id: column.title as string }) + }))} + dataSource={data} + pagination={pagination} + loading={loading} + onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)} + /> + + ); +}; + +export default QualificationTab; \ No newline at end of file diff --git a/src/components/CompanyInfo/index.tsx b/src/components/CompanyInfo/index.tsx new file mode 100644 index 0000000..24ac192 --- /dev/null +++ b/src/components/CompanyInfo/index.tsx @@ -0,0 +1,46 @@ +import React, { useState } from 'react'; +import { Tabs } from 'antd'; +import { useIntl } from 'umi'; +import BaseInfoTab from './component/BaseInfoTab'; +import QualificationTab from './component/QualificationTab'; +import InvoiceTab from './component/InvoiceTab'; +import OtherAttachmentsTab from './component/OtherAttachmentsTab'; +import ContactsInfoTab from './component/ContactsInfoTab'; +import BankInfoTab from './component/BankInfoTab'; + +import Supplier from '@/pages/supplier/register/supplier'; + + +const { TabPane } = Tabs; + +const CompanyInfo: React.FC = () => { + const intl = useIntl(); + // 切换tab + const [subTab, setSubTab] = useState('base'); + + return ( + + + {/* */} + + + + + + + + + + + + + + + + + + + ); +}; + +export default CompanyInfo; \ No newline at end of file diff --git a/src/components/CompanyInfo/services.ts b/src/components/CompanyInfo/services.ts new file mode 100644 index 0000000..c0313b1 --- /dev/null +++ b/src/components/CompanyInfo/services.ts @@ -0,0 +1,38 @@ +import request from '@/utils/request'; + + +export async function coscoSupplier(params:any) { + console.log(params,'params'); + + return request('/api/system/coscoSupplier', { + method: 'GET', + params + }); +} + +export async function library(params:any) { + return request('/api/system/library', { + method: 'GET', + params + }); +} + +export async function qualifications(params:any) { + return request('/api/system/qualifications', { + method: 'GET', + params + }); +} +export async function invoice(params:any) { + return request('/api/system/invoice', { + method: 'GET', + params + }); +} +export async function bank(params:any) { + return request('/api/system/bank', { + method: 'GET', + params + }); +} + \ No newline at end of file diff --git a/src/components/GlobalModal/_mock.ts b/src/components/GlobalModal/_mock.ts new file mode 100644 index 0000000..2d67325 --- /dev/null +++ b/src/components/GlobalModal/_mock.ts @@ -0,0 +1,277 @@ +import { Request, Response } from 'express'; + +// const data = [ +// { +// title: 'Name', +// dataIndex: '评审项', +// key: '评审项', +// }, +// { +// title: 'Other', +// children: [ +// { +// title: 'Age', +// key: '人员A', +// }, +// { +// title: 'Address', +// key: '人员B', +// }, +// ] +// }, +// ] + + +export const mockData = { + base: { + "id": "123456", + "supplierType": "dvs", + "licenceAccessory": "https://example.com/license.pdf", + "licenceDate": "2025-12-31", + "enterpriseType": "company", + "name": "深圳供应商有限公司", + "nameEn": "Shenzhen Supplier Co., Ltd.", + "socialCreditCode": "91440300MA5F3XXXXQ", + "range": "电子元器件、金属材料销售", + "regAddress": "广东省深圳市南山区科技园", + "workAddress": "广东省深圳市南山区软件产业基地", + "parentCompanyInvestor": "深圳控股集团有限公司", + "legalPerson": "李四", + "idCard": "440301199001015678", + "capital": 5000, + "contactsName": "王五", + "contactsPhone": "13800138000", + "contactsType": "法人代表", + "contactsEmail": "contact@supplier.com", + "telephone": "0755-12345678", + "nation": "新加坡", + "vat": "SG12345678VAT", + "taxpayerId": "SG-TAX-998877", + "currency": "SGD", + "personName": "张三", + "personPhone": "13812345678", + "personBank": "中国银行深圳分行", + "personAccount": "6222020200123456789", + "remark": "该供应商已完成初步审核", + "accessStatus": 1, + "blacklistStatus": 0, + "greylistStatus": 1, + "fillinStatus": 0, + "fillinBy": "", + "sapCode": "SAP998877", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }, + qualifications: [{ + "id": "cert-001", + "supplierId": "supplier-123456", + "certificateType": "安全生产许可证", + "name": "建筑施工总承包一级资质", + "code": "ZJ20230605001", + "typeLevel": "一级", + "authority": "住房和城乡建设部", + "dateTime": "2023-06-05", + "termOfValidity": "2026-06-05", + "accessory": "https://example.com/certificate.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2023-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2024-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }], + invoice: { + "id": "invoice-001", + "supplierId": "supplier-123456", + "taxpayerType": "general", + "taxpayerCode": "91440300MA5F3XXXXQ", + "phone": "0755-12345678", + "account": "6222020200123456789", + "head": "深圳供应商有限公司", + "address": "深圳市南山区科技园开票楼101号", + "bank": "中国银行深圳分行", + "qualificationCertificate": "https://example.com/tax-cert.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }, + bank: [{ + "id": "bank-001", + "supplierId": "supplier-123456", + "interbankNumber": "123456789012", + "bank": "中国银行深圳分行", + "swiftCode": "BKCHCNBJ45A", + "accountName": "Shenzhen Supplier Co., Ltd.", + "account": "6222020200123456789", + "currency": "CNY", + "nation": "中国", + "province": "广东省", + "city": "深圳市", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }], + survey: { + "supplierName": "深圳供应商有限公司", + "name": "李四", + "position": "采购经理", + "phone": "13800138000", + "email": "lisi@supplier.com", + "dateTime": "2025-06-17", + }, + questionReply: [ + { + "surveyQuestion": "法律法规:\n我们确保经营和提供的产品服务遵守国家及 各业务所在地的所有使用法律、法规", + "replyValue": "是", + },{ + "surveyQuestion": "健康和安全:\n我们为员工提供符合法律法规的安全且健康 的工作场所。我们建立安全管理体系,并向 员工传达工作场所或生活设施的健康和安全 标准,致力于减少工作对员工造成的伤害和 疾病。", + "replyValue": "符合", + },{ + "surveyQuestion": "环境:\n我们能够以环境友好的方式经营。我们遵守 适用的环境法律、法规和标准;并建立有效 的环境管理体系。\n我们遵守贵集团对相关产品或服务的部分附 加环境要求,这些要求和规定体现在设计与 产品规范的合同文档中。", + "replyValue": "符合", + },{ + "surveyQuestion": "监督和记录:\n我们保留记录遵守相关法律和此行为准则的必要文件,并根据要求为贵集团提供对文件的查看权。我们会允许贵集团在适当的时候,以验证行为准则执行为目的的现场勘查", + "replyValue": "符合", + } + ], + attachments: { + "attachmentsType": "commitment", + "fileName": "anti-bribery-commitment.pdf", + "fileType": "pdf", + "fileSize": "204800", + "filePath": "/data/files/anti-bribery-commitment.pdf", + "fileUrl": "http://example.com/files/anti-bribery-commitment.pdf", + } +}; +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + // 供应商信息 + 'GET /api/system/coscoSupplier': (req: Request, res: Response) => { + res.json({ code: 200, data: mockData, msg: '操作成功' }); + }, + + //品类库 + 'GET /api/system/library': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + "name": "电子元器件类", + "termOfValidity": "2026-12-31T00:00:00", + "deptId": "dept_001", + "area": "华东区域", + "approveStatus": 1, + "workFlowId": "wf_20250601", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2025-06-17T09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-17T10:30:00", + "lastUpdateTime": "2025-06-17T10:30:00" + }, + { + "name": "建筑材料类", + "termOfValidity": "2025-09-30T00:00:00", + "deptId": "dept_002", + "area": "华南区域", + "approveStatus": 0, + "workFlowId": "wf_20250602", + "delFlag": "normal", + "createBy": "system", + "createTime": "2025-06-10T08:20:00", + "updateBy": "reviewer", + "updateTime": "2025-06-15T15:45:00", + "lastUpdateTime": "2025-06-15T15:45:00" + } + ], + total: 2, + msg: '操作成功' }); + }, + // + 'GET /api/system/tianyancha': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + key: '1', + base: '京', + name: '北京科技有限公司', + legalPersonName: '张三', + legalPersonType: '1', + regNumber: '110108123456789', + industry: '信息技术', + companyOrgType: '有限责任公司', + regLocation: '北京市海淀区中关村', + estiblishTime: '2010-06-15', + fromTime: '2010-06-16', + toTime: '2025-06-15', + businessScope: '软件开发、技术咨询', + approvedTime: '2010-06-10', + regStatus: '存续', + regCapital: '5000万元', + regInstitute: '北京市工商局', + orgNumber: '1234567890', + creditCode: '91110108MA01A12345', + property3: 'Beijing Tech Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1001', + taxNumber: '110108123456789', + email: 'contact@bjtech.com', + website: 'http://www.bjtech.com', + phoneNumber: '010-12345678', + lastUpdateTime: '2025-06-15 10:00:00', + }, + { + key: '2', + base: '沪', + name: '上海电子商务有限公司', + legalPersonName: '李四', + legalPersonType: '1', + regNumber: '310101987654321', + industry: '电子商务', + companyOrgType: '股份有限公司', + regLocation: '上海市浦东新区', + estiblishTime: '2015-03-20', + fromTime: '2015-03-21', + toTime: '2030-03-20', + businessScope: '电子商务平台运营、广告设计', + approvedTime: '2015-03-15', + regStatus: '存续', + regCapital: '1亿元', + regInstitute: '上海市工商局', + orgNumber: '0987654321', + creditCode: '91310101MA1AB23456', + property3: 'Shanghai E-commerce Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1002', + taxNumber: '310101987654321', + email: 'info@shcommerce.com', + website: 'http://www.shcommerce.com', + phoneNumber: '021-87654321', + lastUpdateTime: '2025-06-15 09:30:00', + }, + ], + total: 2, + msg: '操作成功' }); + }, + + 'GET /api/500': (req: Request, res: Response) => { + res.status(500).send({ + timestamp: 1513932555104, + status: 500, + error: 'error', + message: 'error', + path: '/base/category/list', + }); + }, + + +}; diff --git a/src/components/GlobalModal/components/AccessCategoryTable.tsx b/src/components/GlobalModal/components/AccessCategoryTable.tsx new file mode 100644 index 0000000..b6fb011 --- /dev/null +++ b/src/components/GlobalModal/components/AccessCategoryTable.tsx @@ -0,0 +1,122 @@ +import React, { useEffect, useState } from 'react'; +import { Table, Tabs, message } from 'antd'; +import { useIntl } from 'umi'; +import { getAccessCategoryList, getCategoryLibraryList } from '../services'; +const { TabPane } = Tabs; + +const AccessCategoryTable = ({id}:{id:any}) => { + const intl = useIntl(); + const columns = [ + { + title: intl.formatMessage({id: 'component.globalModal.CategoryLibraryName'}), + dataIndex: 'name', + key: 'name', + }, + { + title: intl.formatMessage({id: 'component.globalModal.periodOfValidity'}), + dataIndex: 'termOfValidity', + key: 'termOfValidity', + }, + { + title: intl.formatMessage({id: 'component.globalModal.ResponsibleDepartmentID'}), + dataIndex: 'deptId', + key: 'deptId', + }, + { + title: intl.formatMessage({id: 'component.globalModal.RegionalSelection'}), + dataIndex: 'area', + key: 'area', + }, + ] + + //Tabs切换key + const [activeKey, setActiveKey] = useState<'access' | 'category'>('access'); + //准入品类 + const [accessData, setAccessData] = useState([]); + //分页 + const [accessPagination, setAccessPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //加载 + const [accessLoading, setAccessLoading] = useState(false); + //品类库 + const [categoryData, setCategoryData] = useState([]); + //分页 + const [categoryPagination, setCategoryPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //加载 + const [categoryLoading, setCategoryLoading] = useState(false); + // 准入品类 + const fetchAccessData = async (current: number, pageSize: number) => { + setAccessLoading(true); + try { + const { code, data, total } = await getAccessCategoryList({ pageNum: current, pageSize }); + if (code === 200) { + setAccessData(data); + setAccessPagination({ current, pageSize, total }); + } + } catch { + message.error(intl.formatMessage({id:'component.globalModal.FailedToObtainAdmissionCategory'})); + + } finally { + setAccessLoading(false); + } + }; + //品类库 + const fetchCategoryData = async (current: number, pageSize: number) => { + setCategoryLoading(true); + try { + const { code, data, total } = await getCategoryLibraryList({ pageNum: current, pageSize }); + if (code === 200) { + setCategoryData(data); + setCategoryPagination({ current, pageSize, total }); + } + } catch { + message.error(intl.formatMessage({id:'component.globalModal.FailedToObtainCategoryLibrary'})); + } finally { + setCategoryLoading(false); + } + }; + //初始化 + useEffect(() => { + if (activeKey === 'access') { + fetchAccessData(accessPagination.current, accessPagination.pageSize); + } else { + fetchCategoryData(categoryPagination.current, categoryPagination.pageSize); + } + }, [activeKey, id]) + return ( + setActiveKey(key as 'access' | 'category')} > + +
fetchAccessData(page, pageSize), + }} + /> + + + +
fetchCategoryData(page, pageSize), + }} + /> + + + ); +}; + +export default AccessCategoryTable; diff --git a/src/components/GlobalModal/components/RiskList.tsx b/src/components/GlobalModal/components/RiskList.tsx new file mode 100644 index 0000000..29c0e1f --- /dev/null +++ b/src/components/GlobalModal/components/RiskList.tsx @@ -0,0 +1,66 @@ +import React, { useEffect, useState } from 'react'; +import { Table, message } from 'antd'; +import { getRiskList } from '../services' +import { useIntl } from 'umi'; + +const RiskList = ({id}:{id:any}) => { + const intl = useIntl(); + const columns = [ + { title: intl.formatMessage({id: 'component.globalModal.Risktype'}), dataIndex: 'type', key: 'type' }, + { title: intl.formatMessage({id: 'component.globalModal.level'}), dataIndex: 'level', key: 'level' }, + { title: intl.formatMessage({id: 'component.globalModal.desc'}), dataIndex: 'desc', key: 'desc' }, + ] + //合规风险 + const [dataList, setDataList] = useState([]) + //加载 + const [loading, setLoading] = useState(false); + //分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); + + //数据渲染 + const getList = async (page = 1, pageSize = 10) => { + setLoading(true); + try { + const { code, data, total } = await getRiskList({ pageNum: page, pageSize }); + if (code === 200) { + setDataList(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(intl.formatMessage({id: 'component.globalModal.fetchRiskFail'})); + } + } catch (error) { + message.error(intl.formatMessage({id: 'component.globalModal.apiError'})); + } finally { + setLoading(false); + } + }; + //分页 + const handleTableChange = (paginationInfo: any) => { + getList(paginationInfo.current, paginationInfo.pageSize); + }; + //初始化 + useEffect(() => { + getList(); + }, [id]); + + return ( + <> +
{intl.formatMessage({id: 'component.globalModal.riskList'})}
} + dataSource={dataList} + loading={loading} + columns={columns} + pagination={{ + current: pagination.current, + pageSize: pagination.pageSize, + total: pagination.total, + showSizeChanger: true, + onChange: (page, pageSize = 10) => getList(page, pageSize), + }} + onChange={handleTableChange} + /> + + ); +}; + +export default RiskList; diff --git a/src/components/GlobalModal/components/SupplierRegisterInfo.tsx b/src/components/GlobalModal/components/SupplierRegisterInfo.tsx new file mode 100644 index 0000000..165be32 --- /dev/null +++ b/src/components/GlobalModal/components/SupplierRegisterInfo.tsx @@ -0,0 +1,262 @@ +import React from 'react'; +import { Descriptions, Table } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import { useIntl } from 'umi'; +interface qualifications { + id: string; // 主键ID(如后续用于 rowKey) + certificateType: string; // 资质证书类型 + name: string; // 资质名称 + code: string; // 资质证书编号 + typeLevel: string; // 资质类别和等级 + authority: string; // 发证机构 + dateTime: string; // 发证日期(如用 Date 类型可改成 Date) + termOfValidity: string; // 资质有效期至 + accessory?: string; // 附件文件url,可能为空 +} +interface BankInfo { + interbankNumber: string; + bank: string; + accountName: string; + account: string; + currency: string; + nation: string; + province: string; + city: string; +} +const SupplierRegisterInfo = ({ registerInfo }: { registerInfo: any }) => { +const intl = useIntl(); +const qualificationsColumns:ColumnsType = [ + { title: intl.formatMessage({id: 'component.globalModal.serialNumber'}), dataIndex: 'index', key: 'index', width: 60, align: 'center', render: (_: any, __: any, index: number) => index + 1 }, + { title: intl.formatMessage({id: 'component.globalModal.certificateType'}), dataIndex: 'certificateType', key: 'certificateType' }, + { title: intl.formatMessage({id: 'component.globalModal.certificateName'}), dataIndex: 'name', key: 'name' }, + { title: intl.formatMessage({id: 'component.globalModal.certificateNumber'}), dataIndex: 'code', key: 'code' }, + { title: intl.formatMessage({id: 'component.globalModal.certificateCategoryLevel'}), dataIndex: 'typeLevel', key: 'typeLevel' }, + { title: intl.formatMessage({id: 'component.globalModal.issuingAuthority'}), dataIndex: 'authority', key: 'authority' }, + { title: intl.formatMessage({id: 'component.globalModal.issueDate'}), dataIndex: 'dateTime', key: 'dateTime' }, + { title: intl.formatMessage({id: 'component.globalModal.validUntil'}), dataIndex: 'termOfValidity', key: 'termOfValidity' }, + { title: intl.formatMessage({id: 'component.globalModal.attachment'}), dataIndex: 'accessory', key: 'accessory', render: (text: any) => text ? 查看附件 : '-' }, +]; +const bankColumns:ColumnsType = [ + { title: intl.formatMessage({id: 'component.globalModal.serialNumber'}), dataIndex: 'index', key: 'index', width: 60, align: 'center', render: (_: any, __: any, index: number) => index + 1 }, + { title: intl.formatMessage({id: 'component.globalModal.bankCode'}), dataIndex: 'interbankNumber', key: 'interbankNumber' }, + { title: intl.formatMessage({id: 'component.globalModal.openingBank'}), dataIndex: 'bank', key: 'bank' }, + { title: intl.formatMessage({id: 'component.globalModal.accountName'}), dataIndex: 'accountName', key: 'accountName' }, + { title: intl.formatMessage({id: 'component.globalModal.accountNumber'}), dataIndex: 'account', key: 'account' }, + { title: intl.formatMessage({id: 'component.globalModal.currency'}), dataIndex: 'currency', key: 'currency' }, + { title: intl.formatMessage({id: 'component.globalModal.country'}), dataIndex: 'nation', key: 'nation' }, + { title: intl.formatMessage({id: 'component.globalModal.province'}), dataIndex: 'province', key: 'province' }, + { title: intl.formatMessage({id: 'component.globalModal.city'}), dataIndex: 'city', key: 'city' }, +]; + if (!registerInfo) return
{intl.formatMessage({id: 'component.globalModal.loading'})}...
; + + return ( + <> + + {registerInfo.base.supplierType === 'dvs' && ( + + {intl.formatMessage({ id: 'component.globalModal.domesticEnterprise' })} + + )} + + {registerInfo.base.name} + + + {registerInfo.base.nameEn} + + {registerInfo.base.supplierType !== 'dvs' && ( + <> + + {registerInfo.base.nation} + + + {registerInfo.base.vat} + + + {registerInfo.base.taxpayerId} + + + {registerInfo.base.currency} + + + )} + + {registerInfo.base.socialCreditCode} + + + {registerInfo.base.range} + + + {registerInfo.base.regAddress} + + + {registerInfo.base.workAddress} + + + {registerInfo.base.parentCompanyInvestor} + + + {registerInfo.base.legalPerson} + + + {registerInfo.base.idCard} + + + {registerInfo.base.capital} + + + {registerInfo.base.enterpriseType} + + + {registerInfo.base.contactsName} + + + {registerInfo.base.contactsPhone} + + + {registerInfo.base.contactsType} + + + {registerInfo.base.contactsEmail} + + + {registerInfo.base.telephone} + + + +
{intl.formatMessage({id: 'component.globalModal.qualificationInfo'})}
} + /> + + {registerInfo.base.supplierType === 'dvs' && ( + + + {registerInfo.invoice.taxpayerType} + + + {registerInfo.invoice.head} + + + {registerInfo.invoice.taxpayerCode} + + + {registerInfo.invoice.address} + + + {registerInfo.invoice.phone} + + + {registerInfo.invoice.bank} + + + {registerInfo.invoice.account} + + + + {intl.formatMessage({ id: 'component.globalModal.viewAttachment' })} + + + + )} + +
{intl.formatMessage({id: 'component.globalModal.bankAccount'})}
} + /> + +
{intl.formatMessage({id: 'component.globalModal.ethicsQuestionnaireTitle'})}
+ + + + {registerInfo.survey.supplierName} + + + {registerInfo.survey.name} + + + {registerInfo.survey.position} + + + {registerInfo.survey.phone} + + + {registerInfo.survey.email} + + + {registerInfo.survey.dateTime} + + + +
+
index + 1 }, + { title: intl.formatMessage({id: 'component.globalModal.question'}), dataIndex: 'surveyQuestion', key: 'surveyQuestion' }, + { title: intl.formatMessage({id: 'component.globalModal.answer'}), dataIndex: 'replyValue', key: 'replyValue', width: 120 }, + ]} + pagination={false} + title={() =>
{intl.formatMessage({id: 'component.globalModal.questionnaire'})}
} + /> + + + + + {intl.formatMessage({id: 'component.globalModal.viewAttachment'})} + + + + + + {intl.formatMessage({id: 'component.globalModal.viewAttachment'})} + + + + ); +}; + +export default SupplierRegisterInfo; diff --git a/src/components/GlobalModal/components/TianyanchaInfo.tsx b/src/components/GlobalModal/components/TianyanchaInfo.tsx new file mode 100644 index 0000000..ce9f8c0 --- /dev/null +++ b/src/components/GlobalModal/components/TianyanchaInfo.tsx @@ -0,0 +1,138 @@ +import React, { useState, useEffect } from 'react'; +import { Table, Button, message } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import type { IntlShape } from 'react-intl'; +import { tianyanchaIc } from '../services'; +import { useIntl } from 'umi'; +interface TianyanchaInfo { + base: string; // 省份简称 + name: string; // 企业名称 + legalPersonName?: string; // 法人 + legalPersonType?: string; // 法人类型,1 人,2 公司 + regNumber?: string; // 注册号 + industry?: string; // 行业 + companyOrgType?: string; // 企业类型 + regLocation?: string; // 注册地址 + estiblishTime?: string; // 成立时间 + fromTime?: string; // 经营开始时间 + toTime?: string; // 经营结束时间 + businessScope?: string; // 经营范围 + approvedTime?: string; // 核准时间 + regStatus?: string; // 企业状态 + regCapital?: string; // 注册资本 + regInstitute?: string; // 登记机关 + orgNumber?: string; // 组织机构代码 + creditCode?: string; // 统一社会信用代码 + property3?: string; // 英文名 + updatetime?: string; // 更新时间 + companyId?: string; // 表对应id + taxNumber?: string; // 纳税人识别号 + email?: string; // 邮箱 + website?: string; // 网址 + phoneNumber?: string; // 电话号 + lastUpdateTime?: string; // 最后更新时间,建议用 ISO 字符串 +} + + +const TianyanchaInfo = ({ id }:{id:any}) => { + const intl = useIntl(); + +// 渲染列表头,注意用 intl.formatMessage 做标题国际化 +const getColumns = (intl: IntlShape): ColumnsType => [ + { title: intl.formatMessage({ id: 'component.tianyancha.base' }), dataIndex: 'base', key: 'base' }, + { title: intl.formatMessage({ id: 'component.tianyancha.name' }), dataIndex: 'name', key: 'name' }, + { title: intl.formatMessage({ id: 'component.tianyancha.legalPersonName' }), dataIndex: 'legalPersonName', key: 'legalPersonName' }, + { title: intl.formatMessage({ id: 'component.tianyancha.legalPersonType' }), dataIndex: 'legalPersonType', key: 'legalPersonType' }, + { title: intl.formatMessage({ id: 'component.tianyancha.regNumber' }), dataIndex: 'regNumber', key: 'regNumber' }, + { title: intl.formatMessage({ id: 'component.tianyancha.industry' }), dataIndex: 'industry', key: 'industry' }, + { title: intl.formatMessage({ id: 'component.tianyancha.companyOrgType' }), dataIndex: 'companyOrgType', key: 'companyOrgType' }, + { title: intl.formatMessage({ id: 'component.tianyancha.regLocation' }), dataIndex: 'regLocation', key: 'regLocation' }, + { title: intl.formatMessage({ id: 'component.tianyancha.estiblishTime' }), dataIndex: 'estiblishTime', key: 'estiblishTime' }, + { title: intl.formatMessage({ id: 'component.tianyancha.fromTime' }), dataIndex: 'fromTime', key: 'fromTime' }, + { title: intl.formatMessage({ id: 'component.tianyancha.toTime' }), dataIndex: 'toTime', key: 'toTime' }, + { title: intl.formatMessage({ id: 'component.tianyancha.businessScope' }), dataIndex: 'businessScope', key: 'businessScope' }, + { title: intl.formatMessage({ id: 'component.tianyancha.approvedTime' }), dataIndex: 'approvedTime', key: 'approvedTime' }, + { title: intl.formatMessage({ id: 'component.tianyancha.regStatus' }), dataIndex: 'regStatus', key: 'regStatus' }, + { title: intl.formatMessage({ id: 'component.tianyancha.regCapital' }), dataIndex: 'regCapital', key: 'regCapital' }, + { title: intl.formatMessage({ id: 'component.tianyancha.regInstitute' }), dataIndex: 'regInstitute', key: 'regInstitute' }, + { title: intl.formatMessage({ id: 'component.tianyancha.orgNumber' }), dataIndex: 'orgNumber', key: 'orgNumber' }, + { title: intl.formatMessage({ id: 'component.tianyancha.creditCode' }), dataIndex: 'creditCode', key: 'creditCode' }, + { title: intl.formatMessage({ id: 'component.tianyancha.property3' }), dataIndex: 'property3', key: 'property3' }, + { title: intl.formatMessage({ id: 'component.tianyancha.updatetime' }), dataIndex: 'updatetime', key: 'updatetime' }, + { title: intl.formatMessage({ id: 'component.tianyancha.companyId' }), dataIndex: 'companyId', key: 'companyId' }, + { title: intl.formatMessage({ id: 'component.tianyancha.taxNumber' }), dataIndex: 'taxNumber', key: 'taxNumber' }, + { title: intl.formatMessage({ id: 'component.tianyancha.email' }), dataIndex: 'email', key: 'email' }, + { title: intl.formatMessage({ id: 'component.tianyancha.website' }), dataIndex: 'website', key: 'website' }, + { title: intl.formatMessage({ id: 'component.tianyancha.phoneNumber' }), dataIndex: 'phoneNumber', key: 'phoneNumber' }, + { title: intl.formatMessage({ id: 'component.tianyancha.lastUpdateTime' }), dataIndex: 'lastUpdateTime', key: 'lastUpdateTime' }, +]; + + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + const [loading, setLoading] = useState(false); + const [dataList, setDataList] = useState([]); + const [updateTime, setUpdateTime] = useState(new Date().toLocaleDateString()); + + const getList = async (page: number, pageSize: number) => { + setLoading(true); + try { + const { code, data, total } = await tianyanchaIc({ pageNum: page, pageSize }); + if (code === 200) { + setDataList(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(intl.formatMessage({ id: 'component.tianyancha.fetchFailed' })); + } + } catch (err) { + message.error(intl.formatMessage({ id: 'component.tianyancha.fetchFailed' })); + } finally { + setLoading(false); + } + }; + + const onUpdateTime = () => { + setUpdateTime(new Date().toLocaleDateString()); + getList(pagination.current, pagination.pageSize); + message.success(intl.formatMessage({ id: 'component.tianyancha.updateSuccess' })); + }; + + const handleTableChange = (pageInfo: any) => { + getList(pageInfo.current, pageInfo.pageSize); + }; + + useEffect(() => { + setUpdateTime(new Date().toLocaleDateString()); + getList(pagination.current, pagination.pageSize); + }, [id]); + + return ( + <> +
( +
+ + {intl.formatMessage({ id: 'component.tianyancha.title' })} + + ({intl.formatMessage({ id: 'component.tianyancha.updateTime' })}:{updateTime}){' '} + +
+ )} + rowKey="companyId" + dataSource={dataList} + columns={getColumns(intl)} + pagination={{ + current: pagination.current, + pageSize: pagination.pageSize, + total: pagination.total, + showSizeChanger: true, + pageSizeOptions: ['5', '10', '20'], + }} + loading={loading} + onChange={handleTableChange} + /> + + ); +}; + +export default TianyanchaInfo; diff --git a/src/components/GlobalModal/index.tsx b/src/components/GlobalModal/index.tsx new file mode 100644 index 0000000..392451c --- /dev/null +++ b/src/components/GlobalModal/index.tsx @@ -0,0 +1,97 @@ +import React, { useEffect, useState } from 'react'; +import { connect, useIntl } from 'umi'; +import { Modal, Button, Space, Tag } from 'antd'; +import { coscoSupplier } from './services' +import SupplierRegisterInfo from './components/SupplierRegisterInfo'; +import AccessCategoryTable from './components/AccessCategoryTable'; +import TianyanchaInfo from './components/TianyanchaInfo'; +import RiskList from './components/RiskList'; +// 弹出主体 +const GlobalModal = ({ visible, id, dispatch }: any) => { + const intl = useIntl(); + + // 页面显示具体tab类型 + const [modalType, setModalType] = useState<'register' | 'category' | 'tianyancha' | 'risk'>('register'); + //供应商信息数据 + const [registerInfo, setRegisterInfo] = useState(null); + //黑名单显示状态 + const isBlacklisted = true; + //灰名单显示状态 + const isGreylisted = true; + //获取供应商信息 + const fetchRegisterInfo = () => { + //供应商信息 + coscoSupplier({id}).then((res) => { + let { code, data } = res; + if (code == 200) { + setRegisterInfo(data); + } + }) + }; + //切换 供应商信息 准入品类/品类库 天眼查工商信息 合规风险列表 页面标签 + const handleSwitch = (type: typeof modalType) => { + setModalType(type); + }; + // 渲染页面 供应商信息 准入品类/品类库 天眼查工商信息 合规风险列表 页面标签 + const renderContent = () => { + if (modalType === 'register') { + //供应商页面 + return ; + } + if (modalType === 'category') { + //准入品类/品类库 + return + } + if (modalType === 'tianyancha') { + //天眼查信息 + return ; + } + if (modalType === 'risk') { + //合规风险列表 + return ; + } + return null; + }; + // 初始化 + useEffect(() => { + if (visible) { + setModalType('register'); + fetchRegisterInfo(); + } + }, [visible, id]); + return ( + dispatch({ type: 'globalModal/hide' })} + footer={null} + title={ +
+ {intl.formatMessage({id: 'component.globalModal.title' })} + + {isBlacklisted && {intl.formatMessage({id: 'component.globalModal.blacklist' })}} + {isGreylisted && {intl.formatMessage({id: 'component.globalModal.GreyList' })}} + +
+ } + > +
+ + + + + + +
+ {renderContent()} +
+
+
+ ); +}; + +export default connect(({ globalModal }: any) => ({ + visible: globalModal.visible, + id: globalModal.id, +}))(GlobalModal); diff --git a/src/components/GlobalModal/services.ts b/src/components/GlobalModal/services.ts new file mode 100644 index 0000000..bae5da6 --- /dev/null +++ b/src/components/GlobalModal/services.ts @@ -0,0 +1,46 @@ +import request from '@/utils/request'; + + +export async function coscoSupplier(params:any) { + console.log(params,'params'); + + return request('/api/system/coscoSupplier', { + method: 'GET', + params + }); +} + +export async function library(params:any) { + return request('/api/system/library', { + method: 'GET', + params + }); +} + +export async function getAccessCategoryList(params:any) { + return request('/api/system/library', { + method: 'GET', + params + }); +} + +export async function getCategoryLibraryList(params:any) { + return request('/api/system/library', { + method: 'GET', + params + }); +} + +export async function getRiskList(params:any) { + return request('/api/system/library', { + method: 'GET', + params + }); +} + +export async function tianyanchaIc(params:any) { + return request('/api/system/tianyancha', { + method: 'GET', + params + }); +} diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 429c0d4..6e5813d 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -1,9 +1,13 @@ import login from './en-US/login'; import register from './en-US/register'; import menu from './en-US/menu'; +import workbench from './en-US/workbench'; +import globalModal from './en-US/globalModal'; export default { ...login, ...register, ...menu, + ...workbench, + ...globalModal, }; diff --git a/src/locales/en-US/globalModal.ts b/src/locales/en-US/globalModal.ts new file mode 100644 index 0000000..dd8f469 --- /dev/null +++ b/src/locales/en-US/globalModal.ts @@ -0,0 +1,116 @@ +export default { + 'component.globalModal.title': 'Supplier evaluation criteria information review', + 'component.globalModal.blacklist': 'blacklist', + 'component.globalModal.GreyList': 'Grey List', + 'component.globalModal.register': 'Supplier registration information', + 'component.globalModal.category': 'Category List', + 'component.globalModal.tianyancha': 'Tianyancha', + 'component.globalModal.ComplianceRisk': 'Compliance Risk', + 'component.globalModal.CategoryLibraryName': 'Category library name', + 'component.globalModal.periodOfValidity': 'period of validity', + 'component.globalModal.ResponsibleDepartmentID': 'Responsible department ID', + 'component.globalModal.RegionalSelection': 'Regional selection', + 'component.globalModal.AdmissionCategory': 'Admission category', + 'component.globalModal.CategoryLibrary': 'Category library', + 'component.globalModal.FailedToObtainAdmissionCategory': 'Failed to obtain admission category', + 'component.globalModal.FailedToObtainCategoryLibrary': 'Failed to obtain category library', + 'component.globalModal.Risktype': 'Risk type', + "component.globalModal.level": "Level", + "component.globalModal.desc": "Description", + "component.globalModal.fetchRiskFail": "Failed to fetch compliance risk data", + "component.globalModal.apiError": "API request error", + "component.globalModal.riskList": "Compliance Risk List", + 'component.globalModal.serialNumber': 'Serial Number', + 'component.globalModal.certificateType': 'Certificate Type', + 'component.globalModal.certificateName': 'Certificate Name', + 'component.globalModal.certificateNumber': 'Certificate Number', + 'component.globalModal.certificateCategoryLevel': 'Category and Level', + 'component.globalModal.issuingAuthority': 'Issuing Authority', + 'component.globalModal.issueDate': 'Issue Date', + 'component.globalModal.validUntil': 'Valid Until', + 'component.globalModal.attachment': 'Attachment File', + 'component.globalModal.loading': 'Loading', + 'component.globalModal.supplierRegisterInfo': 'Supplier Registration Information', + 'component.globalModal.supplierIdentityType': 'Supplier Identity Type', + 'component.globalModal.domesticEnterprise': 'Domestic Enterprise/Organization', + 'component.globalModal.enterpriseName': 'Enterprise Name', + 'component.globalModal.enterpriseEnglishName': 'Enterprise English Name', + 'component.globalModal.foreignCountryRegion': 'Country/Region of Foreign Enterprise', + 'component.globalModal.foreignVAT': 'Foreign Enterprise VAT Number', + 'component.globalModal.foreignTaxpayerId': 'Foreign Taxpayer ID', + 'component.globalModal.foreignCurrency': 'Foreign Enterprise Currency', + 'component.globalModal.creditCode': 'Unified Social Credit Code', + 'component.globalModal.businessScope': 'Business Scope', + 'component.globalModal.registerAddress': 'Registered Address', + 'component.globalModal.officeAddress': 'Office Address', + 'component.globalModal.parentCompanyInfo': 'Parent Company/Investor Information', + 'component.globalModal.legalPerson': 'Legal Representative/Principal', + 'component.globalModal.idCardNumber': 'ID Card Number', + 'component.globalModal.registeredCapital': 'Registered/Paid-in Capital (10K RMB)', + 'component.globalModal.supplierType': 'Supplier Type', + 'component.globalModal.contactName': 'Contact Name', + 'component.globalModal.contactMobile': 'Contact Mobile', + 'component.globalModal.contactIdType': 'Contact Identity Type', + 'component.globalModal.contactEmail': 'Contact Email', + 'component.globalModal.contactPhone': 'Landline', + 'component.globalModal.qualificationInfo': 'Qualification Information', + 'component.globalModal.invoiceInfo': 'Invoice Information', + 'component.globalModal.taxpayerType': 'Taxpayer Type', + 'component.globalModal.invoiceTitle': 'Invoice Title', + 'component.globalModal.taxpayerCode': 'Taxpayer Identification Number', + 'component.globalModal.invoiceAddress': 'Invoice Address', + 'component.globalModal.invoicePhone': 'Invoice Phone', + 'component.globalModal.invoiceBank': 'Bank of Invoice Account', + 'component.globalModal.invoiceAccount': 'Bank Account Number', + 'component.globalModal.qualificationCertificate': 'VAT Qualification Certificate', + 'component.globalModal.viewAttachment': 'View Attachment', + 'component.globalModal.fillerInfo': 'Filler Information', + 'component.globalModal.supplierName': 'Supplier Name', + 'component.globalModal.fillerName': 'Name', + 'component.globalModal.fillerPosition': 'Position', + 'component.globalModal.fillerPhone': 'Phone', + 'component.globalModal.fillerEmail': 'Email', + 'component.globalModal.fillerDate': 'Date', + 'component.globalModal.antiBriberyTitle': 'Supplier Anti-Bribery Commitment', + 'component.globalModal.antiBriberyLabel': 'Please stamp with company seal and upload', + 'component.globalModal.otherAttachmentTitle': 'Other Attachments', + 'component.globalModal.otherAttachmentLabel': 'Other attachments (optional upload)', + 'component.globalModal.bankAccount': 'Bank Account', + 'component.globalModal.ethicsQuestionnaireTitle': 'Ethical Compliance Self-Assessment Questionnaire', + 'component.globalModal.questionnaire': 'Questionnaire', + 'component.globalModal.index': 'No.', + 'component.globalModal.question': 'Question', + 'component.globalModal.answer': 'Answer', + 'component.tianyancha.title': 'Tianyancha Business Info', + 'component.tianyancha.updateTime': 'Update Time', + 'component.tianyancha.updateBtn': 'Update', + 'component.tianyancha.fetchFailed': 'Failed to fetch business info', + 'component.tianyancha.updateSuccess': 'Business info updated', + + 'component.tianyancha.base': 'Province Abbr.', + 'component.tianyancha.name': 'Company Name', + 'component.tianyancha.legalPersonName': 'Legal Person', + 'component.tianyancha.legalPersonType': 'Legal Person Type', + 'component.tianyancha.regNumber': 'Registration Number', + 'component.tianyancha.industry': 'Industry', + 'component.tianyancha.companyOrgType': 'Company Type', + 'component.tianyancha.regLocation': 'Registered Address', + 'component.tianyancha.estiblishTime': 'Establishment Date', + 'component.tianyancha.fromTime': 'Business Start Date', + 'component.tianyancha.toTime': 'Business End Date', + 'component.tianyancha.businessScope': 'Business Scope', + 'component.tianyancha.approvedTime': 'Approval Date', + 'component.tianyancha.regStatus': 'Company Status', + 'component.tianyancha.regCapital': 'Registered Capital', + 'component.tianyancha.regInstitute': 'Registration Authority', + 'component.tianyancha.orgNumber': 'Organization Code', + 'component.tianyancha.creditCode': 'Unified Social Credit Code', + 'component.tianyancha.property3': 'English Name', + 'component.tianyancha.updatetime': 'Update Time', + 'component.tianyancha.companyId': 'Table ID', + 'component.tianyancha.taxNumber': 'Taxpayer ID', + 'component.tianyancha.email': 'Email', + 'component.tianyancha.website': 'Website', + 'component.tianyancha.phoneNumber': 'Phone Number', + 'component.tianyancha.lastUpdateTime': 'Last Update Time', + }; \ No newline at end of file diff --git a/src/locales/en-US/menu.ts b/src/locales/en-US/menu.ts index b713e42..93059b1 100644 --- a/src/locales/en-US/menu.ts +++ b/src/locales/en-US/menu.ts @@ -22,4 +22,40 @@ export default { 'menu.年审任务管理': 'Annual Review Task Management', 'menu.年度查询': 'Annual Query', 'menu.年审结果': 'Annual Review Results', + + //供应商 + 'menu.admit': 'admit', + 'menu.dict': 'dict', + 'menu.backend': 'Supplier backend', + 'menu.backend.workbenches': 'workbenches', + 'menu.backend.cooperateEnterprise': 'Enterprise Category List', + 'menu.backend.changeProgressInquiry': 'Change progress inquiry', + 'menu.backend.supplierNews': 'Supplier News', + 'menu.informationRetrieval': 'Supplier Information Retrieval', + 'menu.informationRetrieval.registrationQuery': 'Registered supplier inquiry', + 'menu.informationRetrieval.groupQualifiedSupplierQuery': 'Group Qualified Supplier Query', + 'menu.informationRetrieval.mySupplierInquiry': 'My supplier inquiry', + 'menu.admission': '供应商准入', + 'menu.admission.admissionManagement': '供应商准入管理', + 'menu.admission.admissionReviewManagement': '供应商准入评审管理', + 'menu.admission.SupplierEntryReview': '供应商准入审核管理', + 'menu.admission.SupplierCategoryEntry': '供应商品类准入管理', + 'menu.admission.SupplierCategoryEntryReview': '供应商品类准入审核管理', + 'menu.informationManagement': '供应商信息管理', + 'menu.informationManagement.SupplierRegisterAgent': '供应商注册代录', + 'menu.informationManagement.SupplierChangeManage': '供应商变更管理', + 'menu.informationManagement.SupplierChangeReviewManage': '供应商变更审核管理', + 'menu.category': 'Supplier Category Library', + 'menu.category.categoryManage': 'Category Management', + 'menu.category.categoryLibraryManage': 'Category Library Management', + 'menu.category.categoryLibraryReview': 'Category Library Review', + 'menu.category.supplierEntryManage': 'Supplier Entry Management', + 'menu.category.supplierEntryReview': 'Supplier Entry Review', + 'menu.supplierBlacklist': 'Supplier Black/Grey List Management', + 'menu.supplierBlacklist.blacklistManage': 'Supplier Black/Grey List', + 'menu.supplierBlacklist.blacklistAudit': 'Supplier Black/Grey List Audit', + 'menu.supplierExit': 'Supplier Exit Management', + 'menu.supplierExit.supplierExitManage': 'Supplier Exit', + 'menu.supplierExit.supplierExitAudit': 'Supplier Exit Audit', + 'menu.supplierMessage': 'Message Notification', }; diff --git a/src/locales/en-US/workbench.ts b/src/locales/en-US/workbench.ts new file mode 100644 index 0000000..679f251 --- /dev/null +++ b/src/locales/en-US/workbench.ts @@ -0,0 +1,73 @@ +export default { + 'page.workbench.title': 'Workbench', + 'page.workbench.company': 'Company Info', + 'page.workbench.personal': 'Personal Info', + 'page.workbench.password': 'Change Password', + + 'page.workbench.base': 'Basic Information', + 'page.workbench.qualification': 'Qualification Information', + 'page.workbench.invoice': 'Invoice Information', + 'page.workbench.bank': 'Bank Account Information', + 'page.workbench.attachments': 'Other Attachments', + 'page.workbench.contacts': 'Contact Information', + 'page.workbench.certificateType': 'Certificate Type', + 'page.workbench.certificateName': 'Certificate Name', + 'page.workbench.certificateCode': 'Certificate Code', + 'page.workbench.typeLevel': 'Type and Level', + 'page.workbench.authority': 'Issuing Authority', + 'page.workbench.dateTime': 'Date of Issue', + 'page.workbench.termOfValidity': 'Validity Period', + 'page.workbench.updateTime': 'Update Time', + 'page.workbench.invoice.index': 'Index', + 'page.workbench.invoice.taxpayerType': 'Taxpayer Type', + 'page.workbench.invoice.taxpayerCode': 'Taxpayer Identification Number', + 'page.workbench.invoice.head': 'Invoice Head', + 'page.workbench.invoice.address': 'Invoice Address', + 'page.workbench.invoice.phone': 'Invoice Phone', + 'page.workbench.invoice.bank': 'Invoice Bank', + 'page.workbench.invoice.account': 'Invoice Bank Account', + 'page.workbench.invoice.updateTime': 'Update Time', + 'page.workbench.invoice.qualificationCertificate': 'General Taxpayer Qualification Certificate', + + 'page.workbench.bank.index': 'Index', + 'page.workbench.bank.interbankNumber': 'Bank Code', + 'page.workbench.bank.bank': 'Bank Name', + 'page.workbench.bank.accountName': 'Account Name', + 'page.workbench.bank.account': 'Account Number', + 'page.workbench.bank.currency': 'Currency', + 'page.workbench.bank.nation': 'Country', + 'page.workbench.bank.province': 'Province', + 'page.workbench.bank.city': 'City', + 'page.workbench.bank.updateTime': 'Update Time', + + 'page.workbench.attachments.index': 'Index', + 'page.workbench.attachments.fileName': 'File Name', + 'page.workbench.attachments.fileType': 'File Type', + 'page.workbench.attachments.uploadTime': 'Upload Time', + 'page.workbench.attachments.uploader': 'Uploader', + 'page.workbench.attachments.action': 'Action', + + 'page.workbench.contacts.index': 'Index', + 'page.workbench.contacts.name': 'Name', + 'page.workbench.contacts.department': 'Department', + 'page.workbench.contacts.position': 'Position', + 'page.workbench.contacts.mobile': 'Mobile', + 'page.workbench.contacts.phone': 'Office Phone', + 'page.workbench.contacts.email': 'Email', + 'page.workbench.contacts.updateTime': 'Update Time', + 'page.workbench.personal.name': 'Name', + 'page.workbench.personal.gender': 'Gender', + 'page.workbench.personal.phone': 'Phone', + 'page.workbench.personal.email': 'Email', + 'page.workbench.personal.department': 'Department', + 'page.workbench.personal.position': 'Position', + 'page.workbench.personal.entryDate': 'Entry Date', + 'page.changePassword.label.newPassword': 'New Password', + 'page.changePassword.label.confirmPassword': 'Confirm New Password', + 'page.changePassword.message.enterNewPassword': 'Please enter new password', + 'page.changePassword.message.enterConfirmPassword': 'Please enter confirm password', + 'page.changePassword.error.passwordMismatch': 'The two new passwords do not match', + 'page.changePassword.success': 'Password changed successfully', + 'page.changePassword.button.submit': 'Submit', + }; + \ No newline at end of file diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 9382ed8..71f74e6 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -1,9 +1,12 @@ import login from './zh-CN/login'; import register from './zh-CN/register'; import menu from './zh-CN/menu'; - +import workbench from './zh-CN/workbench'; +import globalModal from './zh-CN/globalModal'; export default { ...login, ...register, ...menu, + ...workbench, + ...globalModal, }; diff --git a/src/locales/zh-CN/globalModal.ts b/src/locales/zh-CN/globalModal.ts new file mode 100644 index 0000000..81708c8 --- /dev/null +++ b/src/locales/zh-CN/globalModal.ts @@ -0,0 +1,125 @@ +export default { + 'component.globalModal.title': '供应商评估依据信息查阅', + 'component.globalModal.blacklist': '黑名单', + 'component.globalModal.GreyList': '灰名单', + 'component.globalModal.register': '供应商注册信息', + 'component.globalModal.category': '品类名录', + 'component.globalModal.tianyancha': '天眼查', + 'component.globalModal.ComplianceRisk': '合规风险', + 'component.globalModal.CategoryLibraryName': '品类库名称', + 'component.globalModal.periodOfValidity': '有效期', + 'component.globalModal.ResponsibleDepartmentID': '负责部门ID', + 'component.globalModal.RegionalSelection': '区域选择', + 'component.globalModal.AdmissionCategory': '准入品类', + 'component.globalModal.CategoryLibrary': '品类库', + 'component.globalModal.FailedToObtainAdmissionCategory': '获取准入品类失败', + 'component.globalModal.FailedToObtainCategoryLibrary': '获取品类库失败', + 'component.globalModal.Risktype': '风险类型', + "component.globalModal.level": "等级", + "component.globalModal.desc": "描述", + "component.globalModal.fetchRiskFail": "获取合规风险数据失败", + "component.globalModal.apiError": "接口请求错误", + "component.globalModal.riskList": "合规风险列表", + 'component.globalModal.serialNumber': '序号', + 'component.globalModal.certificateType': '资质证书类型', + 'component.globalModal.certificateName': '资质名称', + 'component.globalModal.certificateNumber': '资质证书编号', + 'component.globalModal.certificateCategoryLevel': '资质类别和等级', + 'component.globalModal.issuingAuthority': '发证机构', + 'component.globalModal.issueDate': '发证日期', + 'component.globalModal.validUntil': '资质有效期至', + 'component.globalModal.attachment': '附件文件', + 'component.globalModal.bankCode': '联行号', + 'component.globalModal.openingBank': '开户银行', + 'component.globalModal.accountName': '账户名称', + 'component.globalModal.accountNumber': '账号', + 'component.globalModal.currency': '币种', + 'component.globalModal.country': '国家', + 'component.globalModal.province': '省份', + 'component.globalModal.city': '地市', + 'component.globalModal.loading': '加载中', + 'component.globalModal.supplierRegisterInfo': '供应商注册信息', + 'component.globalModal.supplierIdentityType': '供应商身份类型', + 'component.globalModal.domesticEnterprise': '境内企业/机构', + 'component.globalModal.enterpriseName': '企业名称', + 'component.globalModal.enterpriseEnglishName': '企业英文名称', + 'component.globalModal.foreignCountryRegion': '境外企业国家地区', + 'component.globalModal.foreignVAT': '境外企业增值税号VAT', + 'component.globalModal.foreignTaxpayerId': '境外纳税人ID号', + 'component.globalModal.foreignCurrency': '境外企业币种', + 'component.globalModal.creditCode': '统一社会信用代码', + 'component.globalModal.businessScope': '经营范围', + 'component.globalModal.registerAddress': '注册地址', + 'component.globalModal.officeAddress': '办公地址', + 'component.globalModal.parentCompanyInfo': '母公司/出资人信息', + 'component.globalModal.legalPerson': '企业法定代表人/负责人', + 'component.globalModal.idCardNumber': '身份证号', + 'component.globalModal.registeredCapital': '注册资本/实收资本(万元)', + 'component.globalModal.supplierType': '供应商类型', + 'component.globalModal.contactName': '联系人姓名', + 'component.globalModal.contactMobile': '联系人手机', + 'component.globalModal.contactIdType': '联系人身份类别', + 'component.globalModal.contactEmail': '联系人邮箱', + 'component.globalModal.contactPhone': '固定电话', + 'component.globalModal.qualificationInfo': '资质信息', + 'component.globalModal.invoiceInfo': '开票信息', + 'component.globalModal.taxpayerType': '纳税人类型', + 'component.globalModal.invoiceTitle': '开票抬头', + 'component.globalModal.taxpayerCode': '纳税人识别号', + 'component.globalModal.invoiceAddress': '开票地址', + 'component.globalModal.invoicePhone': '开票电话', + 'component.globalModal.invoiceBank': '开票户行', + 'component.globalModal.invoiceAccount': '开票户行账号', + 'component.globalModal.qualificationCertificate': '一般纳税人资格证明文件', + 'component.globalModal.viewAttachment': '查看附件', + 'component.globalModal.fillerInfo': '填写人信息', + 'component.globalModal.supplierName': '供应商名称', + 'component.globalModal.fillerName': '姓名', + 'component.globalModal.fillerPosition': '职位', + 'component.globalModal.fillerPhone': '电话号', + 'component.globalModal.fillerEmail': '邮箱', + 'component.globalModal.fillerDate': '日期', + 'component.globalModal.antiBriberyTitle': '供应商反商业贿赂承诺书', + 'component.globalModal.antiBriberyLabel': '请加盖公司公章后上传', + 'component.globalModal.otherAttachmentTitle': '其他附件', + 'component.globalModal.otherAttachmentLabel': '其他附件(非必须上传)', + 'component.globalModal.bankAccount': '银行账户', + 'component.globalModal.ethicsQuestionnaireTitle': '社会准则符合性自查问卷', + 'component.globalModal.questionnaire': '问卷', + 'component.globalModal.index': '序号', + 'component.globalModal.question': '问题', + 'component.globalModal.answer': '回复', + + 'component.tianyancha.title': '天眼查工商信息', + 'component.tianyancha.updateTime': '更新时间', + 'component.tianyancha.updateBtn': '更新', + 'component.tianyancha.fetchFailed': '获取工商信息失败', + 'component.tianyancha.updateSuccess': '工商信息已更新', + + 'component.tianyancha.base': '省份简称', + 'component.tianyancha.name': '企业名称', + 'component.tianyancha.legalPersonName': '法人', + 'component.tianyancha.legalPersonType': '法人类型', + 'component.tianyancha.regNumber': '注册号', + 'component.tianyancha.industry': '行业', + 'component.tianyancha.companyOrgType': '企业类型', + 'component.tianyancha.regLocation': '注册地址', + 'component.tianyancha.estiblishTime': '成立时间', + 'component.tianyancha.fromTime': '经营开始时间', + 'component.tianyancha.toTime': '经营结束时间', + 'component.tianyancha.businessScope': '经营范围', + 'component.tianyancha.approvedTime': '核准时间', + 'component.tianyancha.regStatus': '企业状态', + 'component.tianyancha.regCapital': '注册资本', + 'component.tianyancha.regInstitute': '登记机关', + 'component.tianyancha.orgNumber': '组织机构代码', + 'component.tianyancha.creditCode': '统一社会信用代码', + 'component.tianyancha.property3': '英文名', + 'component.tianyancha.updatetime': '更新时间', + 'component.tianyancha.companyId': '表对应ID', + 'component.tianyancha.taxNumber': '纳税人识别号', + 'component.tianyancha.email': '邮箱', + 'component.tianyancha.website': '网址', + 'component.tianyancha.phoneNumber': '电话号码', + 'component.tianyancha.lastUpdateTime': '最后更新时间', + }; \ No newline at end of file diff --git a/src/locales/zh-CN/menu.ts b/src/locales/zh-CN/menu.ts index f637731..f0f45d3 100644 --- a/src/locales/zh-CN/menu.ts +++ b/src/locales/zh-CN/menu.ts @@ -22,4 +22,41 @@ export default { 'menu.年审任务管理': '年审任务管理', 'menu.年度查询': '年度查询', 'menu.年审结果': '年审结果', + + + //供应商 + 'menu.admit': '供应商准入审批', + 'menu.dict': '字典', + 'menu.backend': '供应商后台', + 'menu.backend.workbenches': '工作台', + 'menu.backend.cooperateEnterprise': '企业品类名录', + 'menu.backend.changeProgressInquiry': '变更进度查询', + 'menu.backend.supplierNews': '供应商消息', + 'menu.informationRetrieval': '供应商信息检索', + 'menu.informationRetrieval.registrationQuery': '注册供应商查询', + 'menu.informationRetrieval.groupQualifiedSupplierQuery': '集团合格供应商查询', + 'menu.informationRetrieval.mySupplierInquiry': '我的供应商查询', + 'menu.admission': '供应商准入', + 'menu.admission.admissionManagement': '供应商准入管理', + 'menu.admission.admissionReviewManagement': '供应商准入评审管理', + 'menu.admission.SupplierEntryReview': '供应商准入审核管理', + 'menu.admission.SupplierCategoryEntry': '供应商品类准入管理', + 'menu.admission.SupplierCategoryEntryReview': '供应商品类准入审核管理', + 'menu.informationManagement': '供应商信息管理', + 'menu.informationManagement.SupplierRegisterAgent': '供应商注册代录', + 'menu.informationManagement.SupplierChangeManage': '供应商变更管理', + 'menu.informationManagement.SupplierChangeReviewManage': '供应商变更审核管理', + 'menu.category': '供应商品类库', + 'menu.category.categoryManage': '品类管理', + 'menu.category.categoryLibraryManage': '品类库管理', + 'menu.category.categoryLibraryReview': '品类库建库审核管理', + 'menu.category.supplierEntryManage': '供应商入库管理', + 'menu.category.supplierEntryReview': '供应商入库审核管理', + 'menu.supplierBlacklist': '供应商黑灰名单管理', + 'menu.supplierBlacklist.blacklistManage': '供应商黑灰名单管理', + 'menu.supplierBlacklist.blacklistAudit': '供应商黑灰名单审核管理', + 'menu.supplierExit': '供应商退出管理', + 'menu.supplierExit.supplierExitManage': '供应商退出管理', + 'menu.supplierExit.supplierExitAudit': '供应商退出审核管理', + 'menu.supplierMessage': '消息通知', }; diff --git a/src/locales/zh-CN/workbench.ts b/src/locales/zh-CN/workbench.ts new file mode 100644 index 0000000..f09e897 --- /dev/null +++ b/src/locales/zh-CN/workbench.ts @@ -0,0 +1,72 @@ +export default { + 'page.workbench.title': '工作台', + 'page.workbench.company': '公司信息', + 'page.workbench.personal': '个人信息', + 'page.workbench.password': '修改密码', + + 'page.workbench.base': '基本信息', + 'page.workbench.qualification': '资质信息', + 'page.workbench.invoice': '开票信息', + 'page.workbench.bank': '银行账户信息', + 'page.workbench.attachments': '其他附件', + 'page.workbench.contacts': '联系人信息', + 'page.workbench.certificateType': '资质证书类型', + 'page.workbench.certificateName': '资质名称', + 'page.workbench.certificateCode': '资质证书编号', + 'page.workbench.typeLevel': '资质类别和等级', + 'page.workbench.authority': '发证机构', + 'page.workbench.dateTime': '发证日期', + 'page.workbench.termOfValidity': '资质有效期至', + 'page.workbench.updateTime': '更新时间', + 'page.workbench.invoice.index': '序号', + 'page.workbench.invoice.taxpayerType': '纳税人类型', + 'page.workbench.invoice.taxpayerCode': '纳税人识别号', + 'page.workbench.invoice.head': '开票抬头', + 'page.workbench.invoice.address': '开票地址', + 'page.workbench.invoice.phone': '开票电话', + 'page.workbench.invoice.bank': '开票户行', + 'page.workbench.invoice.account': '开票户行账号', + 'page.workbench.invoice.updateTime': '更新时间', + 'page.workbench.invoice.qualificationCertificate': '一般纳税人资质证明', + + 'page.workbench.bank.index': '序号', + 'page.workbench.bank.interbankNumber': '银行代码', + 'page.workbench.bank.bank': '开户行', + 'page.workbench.bank.accountName': '账户名', + 'page.workbench.bank.account': '账号', + 'page.workbench.bank.currency': '币种', + 'page.workbench.bank.nation': '国家', + 'page.workbench.bank.province': '省份', + 'page.workbench.bank.city': '城市', + 'page.workbench.bank.updateTime': '更新时间', + + 'page.workbench.attachments.index': '序号', + 'page.workbench.attachments.fileName': '文件名称', + 'page.workbench.attachments.fileType': '文件类型', + 'page.workbench.attachments.uploadTime': '上传时间', + 'page.workbench.attachments.uploader': '上传人', + 'page.workbench.attachments.action': '操作', + + 'page.workbench.contacts.index': '序号', + 'page.workbench.contacts.name': '姓名', + 'page.workbench.contacts.department': '部门', + 'page.workbench.contacts.position': '职位', + 'page.workbench.contacts.mobile': '手机号', + 'page.workbench.contacts.phone': '办公电话', + 'page.workbench.contacts.email': '邮箱', + 'page.workbench.contacts.updateTime': '更新时间', + 'page.workbench.personal.name': '姓名', + 'page.workbench.personal.gender': '性别', + 'page.workbench.personal.phone': '手机号', + 'page.workbench.personal.email': '邮箱', + 'page.workbench.personal.department': '所属部门', + 'page.workbench.personal.position': '职位', + 'page.workbench.personal.entryDate': '入职日期', + 'page.changePassword.label.newPassword': '新密码', + 'page.changePassword.label.confirmPassword': '确认新密码', + 'page.changePassword.message.enterNewPassword': '请输入新密码', + 'page.changePassword.message.enterConfirmPassword': '请再次输入新密码', + 'page.changePassword.error.passwordMismatch': '两次输入的新密码不一致', + 'page.changePassword.success': '密码修改成功', + 'page.changePassword.button.submit': '提交修改', +} \ No newline at end of file diff --git a/src/pages/supplier/admission/SupplierCategoryEntry/index.tsx b/src/pages/supplier/admission/SupplierCategoryEntry/index.tsx new file mode 100644 index 0000000..5a90750 --- /dev/null +++ b/src/pages/supplier/admission/SupplierCategoryEntry/index.tsx @@ -0,0 +1,273 @@ +import React, { useRef } from 'react'; +import ProTable from '@ant-design/pro-table'; +import type { ProColumns, ActionType } from '@ant-design/pro-table'; +import { Form, Select, Button, Row, Col, Tag, Space, DatePicker, Input } from 'antd'; +import { SearchOutlined, ReloadOutlined } from '@ant-design/icons'; + +const { RangePicker } = DatePicker; + +// 示例数据 +const tableData = [ + { + id: 1, + name: '中山市合创展包装材料有限公司', + region: '境内', + type: '民营企业', + category: '船用物料,添加剂', + time: '2025-03-03 09:30', + status: '未开始', + result: '', + }, + { + id: 2, + name: '深圳市欧阳华斯电源有限公司', + region: '境内', + type: '民营企业', + category: '绳机物料', + time: '2025-03-03 09:30', + status: '未开始', + result: '', + }, + { + id: 3, + name: '广东振兴塑胶机械有限公司', + region: '境内', + type: '民营企业', + category: '船用物料', + time: '2025-03-03 09:30', + status: '进行中', + result: '', + }, + { + id: 4, + name: '上海硕建建筑技术工程有限公司', + region: '境内', + type: '民营企业', + category: '绳机物料', + time: '2025-03-03 09:30', + status: '进行中', + result: '', + }, + { + id: 5, + name: '中山市合创展包装材料有限公司', + region: '境内', + type: '民营企业', + category: '船用物料', + time: '2025-03-03 09:30', + status: '进行中', + result: '', + }, + { + id: 6, + name: '深圳市欧阳华斯电源有限公司', + region: '境内', + type: '民营企业', + category: '绳机物料', + time: '2025-03-03 09:30', + status: '进行中', + result: '', + }, + { + id: 7, + name: '广东振兴塑胶机械有限公司', + region: '境内', + type: '民营企业', + category: '船用物料', + time: '2025-03-03 09:30', + status: '已结束', + result: '通过', + }, + { + id: 8, + name: '上海硕建建筑技术工程有限公司', + region: '境内', + type: '民营企业', + category: '绳机物料', + time: '2025-03-03 09:30', + status: '已结束', + result: '驳回', + }, + { + id: 9, + name: '广东振兴塑胶机械有限公司', + region: '境内', + type: '民营企业', + category: '船用物料', + time: '2025-03-03 09:30', + status: '已结束', + result: '驳回', + }, + { + id: 10, + name: '上海硕建建筑技术工程有限公司', + region: '境内', + type: '民营企业', + category: '绳机物料', + time: '2025-03-03 09:30', + status: '已结束', + result: '驳回', + }, +]; + +const statusColorMap: Record = { + '未开始': 'default', + '进行中': 'processing', + '已结束': 'success', +}; + +const resultColorMap: Record = { + '通过': 'success', + '驳回': 'error', +}; + +const columns: ProColumns[] = [ + { + title: '序号', + dataIndex: 'index', + valueType: 'index', + width: 48, + align: 'center', + }, + { + title: '供应商名称', + dataIndex: 'name', + align: 'center', + ellipsis: true, + }, + { + title: '境内/境外', + dataIndex: 'region', + align: 'center', + }, + { + title: '企业类型', + dataIndex: 'type', + align: 'center', + }, + { + title: '准入品类', + dataIndex: 'category', + align: 'center', + ellipsis: true, + }, + { + title: '提交时间', + dataIndex: 'time', + align: 'center', + sorter: (a, b) => new Date(a.time).getTime() - new Date(b.time).getTime(), + }, + { + title: '流程状态', + dataIndex: 'status', + align: 'center', + render: (_, record) => + {record.status}, + }, + { + title: '审批结果', + dataIndex: 'result', + align: 'center', + render: (val: string) => + val ? {val} : null, + }, + { + title: '操作', + dataIndex: 'option', + align: 'center', + valueType: 'option', + render: (_, record) => ( + + 审批 + 审批记录 + + ), + }, +]; + +// 查询项选项 +const regionOptions = [ + { label: '请选择', value: '' }, + { label: '境内', value: '境内' }, + { label: '境外', value: '境外' }, +]; +const statusOptions = [ + { label: '请选择', value: '' }, + { label: '未开始', value: '未开始' }, + { label: '进行中', value: '进行中' }, + { label: '已结束', value: '已结束' }, +]; +const resultOptions = [ + { label: '请选择', value: '' }, + { label: '通过', value: '通过' }, + { label: '驳回', value: '驳回' }, +]; + +const SupplierCategoryEntry: React.FC = () => { + const actionRef = useRef(); + + // 查询区渲染 + const searchFormRender = ( +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
i?.toString()} + scroll={{ x: true }} + bordered + style={{ marginBottom: 24 }} + /> + +
+ + +
+ + ); +}; + +export default connect()(ApproveModal); \ No newline at end of file diff --git a/src/pages/supplier/admission/admissionManagement/components/CreateModal.tsx b/src/pages/supplier/admission/admissionManagement/components/CreateModal.tsx new file mode 100644 index 0000000..718ef44 --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/components/CreateModal.tsx @@ -0,0 +1,453 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, Form, Select, Button, Tree, message, DatePicker, Radio, Upload, Input } from 'antd'; +// +import { UploadOutlined } from '@ant-design/icons'; +import type { UploadFile } from 'antd/es/upload/interface'; +import moment from 'moment'; +//组件 +import SupplierSelector from './SupplierSelector'; +import ReviewerSelector from './ReviewerSelector'; +import DivisionModal from './DivisionModal'; +// 请求 +import { categoryTree, add, uploadFile } from '../services'; +const { Option } = Select; +const { RangePicker } = DatePicker; +//selected 类型 +interface Reviewer { + key: string; + name: string; + id: string; + dept: string; +} +// 传入的人接口 +interface ReviewerSelectorData { + selected: Reviewer[]; + leader: Reviewer | null; +} +// 主体 +const CreateModal: React.FC<{ visible: boolean; onCancel: () => void; }> = ({ visible, onCancel }) => { + + const [form] = Form.useForm(); + //品类选择 + const [checkedKeys, setCheckedKeys] = useState([]); + //供应商 + const [selectedSuppliers, setSelectedSuppliers] = useState([]); + // 选择评审人员 + const [selectedReviewers, setSelectedReviewers] = useState({ + selected: [], + leader: null, + }); + //评审分工 + const [divisionData, setDivisionData] = useState([]); + //准入方式 + const [admissionMethod, setAdmissionMethod] = useState('online'); + //供应商符合性审查 + const [fileList, setFileList] = useState[]>([]); + //供应商弹出 + const [supplierModalVisible, setSupplierModalVisible] = useState(false); + const [reviewerModalVisible, setReviewerModalVisible] = useState(false); + const [divisionModalVisible, setDivisionModalVisible] = useState(false); + //品类选择渲染数据 + const [categoriesTreeData, setCategoriesTreeData] = useState([]); + //品类选择数据中字段转换 + const convertTreeData = (data: any) => { + return data.map((item: any) => ({ + ...item, + title: item.categoryName, + key: item.id, + children: item.children ? convertTreeData(item.children) : undefined, + + })); + } + function findLeafKeys(treeData: any[]): string[] { + let leafKeys: string[] = []; + function dfs(nodes: any[]) { + nodes.forEach(node => { + if (!node.children || node.children.length === 0) { + leafKeys.push(node.key); + } else { + dfs(node.children); + } + }); + } + dfs(treeData); + return leafKeys; + } + //品类选择 + const onCheck = ( + checkedKeysValue: + | React.Key[] + | { checked: React.Key[]; halfChecked: React.Key[] } + ) => { + const keys = Array.isArray(checkedKeysValue) + ? checkedKeysValue + : checkedKeysValue.checked; + + // 只取叶子节点 key + const leafKeys = findLeafKeys(convertTreeData(categoriesTreeData)); + const onlyLeafChecked = keys.filter(key => leafKeys.includes(key)); + + setCheckedKeys(keys); // UI 显示用,还是全量 + form.setFieldsValue({ categoryIds: onlyLeafChecked }); // 只存叶子到表单 + // setCheckedKeys(keys); + // form.setFieldsValue({ categoryIds: keys }); + }; + // 选择准入方式 + const onMethodChange = (e: any) => { + setAdmissionMethod(e.target.value); + form.setFieldsValue({ method: e.target.value }); + + // 如果切换到线上准入,清空下面相关字段 + if (e.target.value === 'online') { + form.setFieldsValue({ + rangePicker: undefined, + reviewers: undefined, + division: undefined, + supplierCompliance: undefined, + }); + setSelectedReviewers({ selected: [], leader: null, }); + setDivisionData([]); + } + }; + + // 自定义上传 + const handleCustomRequest = async (options: any) => { + const { file, onSuccess, onError } = options; + try { + // 1. 调用你自己写的上传接口 + const res = await uploadFile(file); + + // 2. 可根据后端返回结构,给 fileList 项增加 url 或其他字段 + const uploadedFile: UploadFile = { + ...file, + status: 'done', + url: res?.data?.url || res?.url, // 按后端返回实际字段 + name: res?.data?.name || file.name, + coscoAccessWorkAttachments: res + }; + setFileList([uploadedFile]); + // 同步到 Form + form.setFieldsValue({ supplierCompliance: [uploadedFile] }); + + onSuccess && onSuccess(res, file); + message.success('上传成功'); + } catch (e) { + onError && onError(e); + message.error('上传失败'); + } + }; + // 提交 + const onFinish = async (values: any) => { + const finalPayload = { + coscoAccessWork: { + startTime: '', + endTime: '', + accessType: '', + accessWorkName: '', + deptId: '', + }, + categoryIds: [], + supplierIds: [], + coscoAccessUserls: [], + coscoAccessItems: [], + coscoAccessWorkAttachments: {}, + }; + + //标题名称 + finalPayload.coscoAccessWork.accessWorkName = values.accessWorkName; + //准入方式 + finalPayload.coscoAccessWork.accessType = values.accessType; + //准入部门 + finalPayload.coscoAccessWork.deptId = values.deptId; + //品类选择 + finalPayload.categoryIds = values.categoryIds; + //选择供应商 + if(values.supplier.length != 0) { + values.supplier.forEach((item: { id: string }) => { + finalPayload.supplierIds.push(item.id) + }) + } + + if (values.accessType === 'online') { + // 评审时间 + if (values.rangePicker && values.rangePicker.length === 2) { + const [start, end] = values.rangePicker; + finalPayload.coscoAccessWork.startTime = start.format('YYYY-MM-DD HH:mm'); + finalPayload.coscoAccessWork.endTime = end.format('YYYY-MM-DD HH:mm'); + } + //选择评审人员 + finalPayload.coscoAccessUserls = values.reviewers.selected.map((item:{ userId:string, deptId:string,isLeader:number }) => { + return { userId: item.userId, deptId: item.deptId, isLeader: item.isLeader } + }); + //评审分工 + values.division.forEach((item: any) => { + const dataJson = { + itemName: item.itemName, + reviewBy: [] as string[], + }; + Object.entries(item.reviewerChecks || {}).forEach(([key, value]) => { + dataJson.reviewBy.push(key); + }); + finalPayload.coscoAccessItems.push(dataJson); + }); + } else { + // 供应商符合性审查 + finalPayload.coscoAccessWorkAttachments = values.supplierCompliance[0].response + } + + console.log( finalPayload, values); + const res = await add(finalPayload); + if (res?.success) { + message.success('创建成功'); + form.resetFields(); + setCheckedKeys([]); + setSelectedSuppliers([]); + setSelectedReviewers({ selected: [], leader: null, }); + setDivisionData([]); + setAdmissionMethod('online'); + onCancel(); + } else { + message.error('创建失败'); + } + }; + //初始化 + useEffect(() => { + categoryTree().then((res) => { + const { code, data } = res; + if (code == 200) { + setCategoriesTreeData(data) + } + + }) + form.setFieldsValue({ method: 'online' }); + }, [visible, form]); + return ( + { + form.resetFields(); + setCheckedKeys([]); + setSelectedSuppliers([]); + setSelectedReviewers({ selected: [], leader: null, }); + setDivisionData([]); + setAdmissionMethod('online'); + onCancel(); + }} + width="700px" + destroyOnClose + > + + + + + + + + + + + 线上准入 + 线下准入 + + + + + + + + + + + + {admissionMethod !== 'online' && ( + <> + { + if (admissionMethod === 'online') { + if (!value || value.length === 0) { + return Promise.reject(new Error('请上传供应商符合性审查文件')); + } + } + return Promise.resolve(); + }, + }, + ]} + valuePropName="fileList" + getValueFromEvent={e => (Array.isArray(e) ? e : e && e.fileList)} + > + { + if (fileList.length >= 1) { + message.error('只能上传一个文件'); + return Upload.LIST_IGNORE; + } + return true; // 允许进入 customRequest + }} + onChange={({ fileList: newFileList }) => { + setFileList(newFileList); + form.setFieldsValue({ supplierCompliance: newFileList }); + }} + onRemove={(file) => { + const newList = fileList.filter(item => item.uid !== file.uid); + setFileList(newList); + form.setFieldsValue({ supplierCompliance: newList }); + }} + accept=".pdf,.doc,.docx" // 可选,限制文件类型 + maxCount={1} // 只允许一个 + > + + + + + )} + + {admissionMethod === 'online' && ( + <> + + + + + + + + + + + + + )} + + + + + + + + setSupplierModalVisible(false)} + onSelect={(selected) => { + setSelectedSuppliers(selected); + form.setFieldsValue({ supplier: selected }); + setSupplierModalVisible(false); + }} + /> + + setReviewerModalVisible(false)} + onSelect={(result) => { + console.log(result,'result'); + + setSelectedReviewers(result); + form.setFieldsValue({ reviewers: result }); + setReviewerModalVisible(false); + }} + /> + + setDivisionModalVisible(false)} + onSelect={(data) => { + setDivisionData(data); + form.setFieldsValue({ division: data }); + setDivisionModalVisible(false); + }} + /> + + ); +}; + +export default CreateModal; diff --git a/src/pages/supplier/admission/admissionManagement/components/DivisionModal.tsx b/src/pages/supplier/admission/admissionManagement/components/DivisionModal.tsx new file mode 100644 index 0000000..77463b5 --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/components/DivisionModal.tsx @@ -0,0 +1,153 @@ +import React, { useState, useMemo } from 'react'; +import { Modal, Table, Input, Button, Checkbox } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; + +//selected 类型 +interface Reviewer { + userId: string; + name: string; + id: string; + deptId: string; +} +// 传入的人接口 +interface ReviewerSelectorData { + selected: Reviewer[]; + leader: Reviewer | null; +} +//列表参数人类型 +type ReviewerChecks = { + [reviewerKey: string]: boolean; +}; +//列表参数 +type DivisionRow = { + key: string; + itemName: string; + reviewerChecks: ReviewerChecks; +}; +//列表头接口 +interface ColumnsRow { + itemName: string; + reviewerChecks: { [userId: string]: boolean }; +} +//主体 +const DivisionModal: React.FC<{ + reviewerSelector: ReviewerSelectorData; + visible: boolean; + onCancel: () => void; + onSelect?: (divisionData: any[]) => void; +}> = ({ visible, onCancel, onSelect, reviewerSelector }) => { + + // 过滤出组长 + const nonLeaderReviewers = useMemo( + () => (reviewerSelector?.selected || []).filter( + r => reviewerSelector.leader && r.id !== reviewerSelector.leader.id + ), [reviewerSelector] + ); + //列表数据渲染 -默认第一条 + const [data, setData] = useState([ + { key: '1', itemName: '合规检查', reviewerChecks: {} }, + ]); + + // 评审项 选择 + const handleReviewerCheck = (rowIndex: number, reviewerKey: string, checked: boolean) => { + const newData = [...data]; + newData[rowIndex].reviewerChecks[reviewerKey] = checked; + setData(newData); + }; + //全部评审项 多选 + const handleReviewerCheckAllColumn = (reviewerKey: string, checked: boolean) => { + const newData = data.map(row => ({ + ...row, + reviewerChecks: { + ...row.reviewerChecks, + [reviewerKey]: checked, + }, + })); + setData(newData); + }; + //列表头 + const columns: ColumnsType = [ + { + title: '评审项', + dataIndex: 'itemName', + fixed: 'left', + width: 360, + render: (text: string, record: any, index: number) => ( + { + const newData = [...data]; + newData[index].itemName = e.target.value; + setData(newData); + }} + /> + ), + }, + ...nonLeaderReviewers.map(r => ({ + title: ( + handleReviewerCheckAllColumn(r.userId, e.target.checked)} + checked={data.every(row => row.reviewerChecks?.[r.userId])} + >{r.name} + ), + width: 140, + dataIndex: r.userId, + render: (val: any, record: any, rowIndex: number) => ( + handleReviewerCheck(rowIndex, r.userId, e.target.checked)} + /> + ), + })), + { + title: '操作', + fixed: 'right', + width: '80px', + render: (text: any, record: any, index: number) => ( + + ), + }, + ]; + //增加行 + const addRow = () => { + const newRow = { key: Date.now().toString(), itemName: '', reviewerChecks: {} }; + setData([...data, newRow]); + }; + + return ( + +
+ +
+
+ +
+ + +
+ + ); +}; + +export default DivisionModal; diff --git a/src/pages/supplier/admission/admissionManagement/components/RecordModal.tsx b/src/pages/supplier/admission/admissionManagement/components/RecordModal.tsx new file mode 100644 index 0000000..c3bb43f --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/components/RecordModal.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Modal, Table } from 'antd'; + +const RecordModal: React.FC<{ + visible: boolean; + record?: any; + onCancel: () => void; +}> = ({ visible, onCancel }) => { + const columns = [ + { title: '序号', dataIndex: 'index', render: (_: any, __: any, index: number) => index + 1 }, + { title: '操作人', dataIndex: 'user' }, + { title: '审批结果', dataIndex: 'result' }, + { title: '时间', dataIndex: 'time' }, + ]; + + const data = [ + { key: '1', user: '张三', result: '通过', time: '2025-06-10' }, + { key: '2', user: '李四', result: '驳回', time: '2025-06-11' }, + ]; + + return ( + +
+ + ); +}; + +export default RecordModal; diff --git a/src/pages/supplier/admission/admissionManagement/components/ResultModal.tsx b/src/pages/supplier/admission/admissionManagement/components/ResultModal.tsx new file mode 100644 index 0000000..8cc64f5 --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/components/ResultModal.tsx @@ -0,0 +1,203 @@ +import React from 'react'; +import { Modal, Table, Tag, Typography, Button } from 'antd'; + +const { Link } = Typography; + +const dataSource = [ + { + key: '1', + item: '供应商信息登记表', + companyA_zhangsan: '合格', + companyA_lisi: '合格', + companyB_zhangsan: '合格', + companyB_lisi: '合格', + }, + { + key: '2', + item: '供应商反商业贿赂承诺书', + companyA_zhangsan: '合格', + companyA_lisi: '合格', + companyB_zhangsan: '合格', + companyB_lisi: '合格', + }, + { + key: '3', + item: '供应商社会准则符合性自审问卷', + companyA_zhangsan: '合格', + companyA_lisi: '合格', + companyB_zhangsan: '合格', + companyB_lisi: '合格', + }, + { + key: '4', + item: '是否为禁止使用供应商', + companyA_zhangsan: '合格', + companyA_lisi: '合格', + companyB_zhangsan: '合格', + companyB_lisi: '合格', + }, + { + key: '5', + item: '3年内经营活动中没有重大违法记录的书面声明', + companyA_zhangsan: '合格', + companyA_lisi: '合格', + companyB_zhangsan: '合格', + companyB_lisi: '合格', + }, + { + key: '6', + item: '企业存续状态审核(营业执照等)', + companyA_zhangsan: '合格', + companyA_lisi: '合格', + companyB_zhangsan: '合格', + companyB_lisi: '不合格', + companyB_lisiRemark: '查看备注', + }, + { + key: '7', + item: '行业资质、认证情况(按需)', + companyA_zhangsan: '合格', + companyA_lisi: '合格', + companyB_zhangsan: '合格', + companyB_lisi: '合格', + }, + { + key: '8', + item: '现场考察、产品试用情况(按需)', + companyA_zhangsan: '合格', + companyA_zhangsanRemark: '查看备注', + companyA_lisi: '合格', + companyA_lisiRemark: '查看备注', + companyB_zhangsan: '合格', + companyB_zhangsanRemark: '查看备注', + companyB_lisi: '合格', + companyB_lisiRemark: '查看备注', + }, +]; + +const columns = [ + { + title: '序号', + dataIndex: 'key', + width: 60, + }, + { + title: '评审项', + dataIndex: 'item', + width: 260, + }, + { + title: ( +
+
中山市合创展包装材料有限公司
+
+ ), + children: [ + { + title: '张三', + dataIndex: 'companyA_zhangsan', + width: 100, + render: (text: string, record: any) => renderStatus(text, record.companyA_zhangsanRemark), + }, + { + title: '李四', + dataIndex: 'companyA_lisi', + width: 100, + render: (text: string, record: any) => renderStatus(text, record.companyA_lisiRemark), + }, + ], + }, + { + title: ( +
+
广东振兴塑胶机械有限公司
+
+ ), + children: [ + { + title: '张三', + dataIndex: 'companyB_zhangsan', + width: 100, + render: (text: string, record: any) => renderStatus(text, record.companyB_zhangsanRemark), + }, + { + title: '李四', + dataIndex: 'companyB_lisi', + width: 100, + render: (text: string, record: any) => renderStatus(text, record.companyB_lisiRemark), + }, + ], + }, +]; + +function renderStatus(text: string, remark?: string) { + const color = text === '合格' ? 'green' : text === '不合格' ? 'red' : undefined; + return ( +
+ + {text} + + {remark && ( + alert('备注内容')}> + {remark} + + )} +
+ ); +} + +const ResultModal: React.FC<{ + visible: boolean; + record?: any; + onCancel: () => void; +}> = ({ visible, onCancel }) => { + return ( + +
{ + // 汇总结果示例,模拟图片底部汇总 + return ( + + + 结果汇总 + + + 合格 + + + 不合格 + + + ); + }} + /> +
+ + + + +
+ + ); +}; + +export default ResultModal; diff --git a/src/pages/supplier/admission/admissionManagement/components/ReviewerSelector.tsx b/src/pages/supplier/admission/admissionManagement/components/ReviewerSelector.tsx new file mode 100644 index 0000000..4668802 --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/components/ReviewerSelector.tsx @@ -0,0 +1,175 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { Modal, Table, Radio, Button, message } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; + +// 评审人数据结构 +interface Reviewer { + id: string; + name: string; + userId: string; + deptId: string; + isLeader: number; +} +interface ReviewerSelectorData { + selected: Reviewer[]; + leader: Reviewer | null; +} + +interface ReviewerSelectorProps { + visible: boolean; + onCancel: () => void; + onSelect?: (selected: ReviewerSelectorData) => void; +} + +// 模拟分页接口 +const fetchReviewers = async ({ + pageNum, + pageSize, +}: { + pageNum: number; + pageSize: number; +}): Promise<{ list: Reviewer[]; total: number }> => { + return new Promise((resolve) => { + setTimeout(() => { + const all: Reviewer[] = Array.from({ length: 36 }, (_, i) => ({ + id: `u-${i + 1}`, + name: `评审人${i + 1}`, + userId: `E000${i + 1}`, + deptId: `部门${(i % 3) + 1}`, + isLeader: 0, + })); + resolve({ + list: all.slice((pageNum - 1) * pageSize, pageNum * pageSize), + total: all.length, + }); + }, 300); + }); +}; + +const ReviewerSelector: React.FC = ({ + visible, + onCancel, + onSelect, +}) => { + // 分页 + const [page, setPage] = useState(1); + const [pageSize] = useState(10); + const [total, setTotal] = useState(0); + + // 数据 + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + + // 已选评审人 & 组长 + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [leader, setLeader] = useState(''); + + // 加载数据 + useEffect(() => { + if (!visible) return; + setLoading(true); + fetchReviewers({ pageNum: page, pageSize }) + .then((res) => { + setData(res.list); + setTotal(res.total); + }) + .finally(() => setLoading(false)); + }, [visible, page, pageSize]); + + // 当前已选项(全局选中的都能做组长) + const selectedReviewerObjs = useMemo( + () => data.filter((item) => selectedRowKeys.includes(item.userId)), + [data, selectedRowKeys] + ); + + // 列 + const columns: ColumnsType = [ + { title: '姓名', dataIndex: 'name' }, + { title: '工号', dataIndex: 'userId' }, + { title: '部门', dataIndex: 'deptId' }, + { + title: '组长', + render: (_: any, record: Reviewer) => ( + setLeader(record.userId)} + /> + ), + }, + ]; + + // 提交校验 + const handleOk = () => { + if (!selectedRowKeys.length) { + message.warning('请先选择评审人员'); + return; + } + if (!leader) { + message.warning('请指定组长'); + return; + } + // 组长和其他人员isLeader赋值 + const selected = data + .filter((item) => selectedRowKeys.includes(item.userId)) + .map((item) => ({ + ...item, + isLeader: item.userId === leader ? 1 : 0, + })); + + // 完整数据传回 + onSelect?.({ + selected, + leader: selected.find((item) => item.isLeader === 1) ?? null, + }); + onCancel(); + }; + + return ( + + + rowSelection={{ + type: 'checkbox', + selectedRowKeys, + onChange: (keys) => { + setSelectedRowKeys(keys); + // 如果组长不在新选中里,清掉 + if (!keys.includes(leader)) setLeader(''); + }, + getCheckboxProps: (record) => ({ + // 可根据需要禁用某些行 + // disabled: record.disabled, + }), + }} + columns={columns} + dataSource={data} + rowKey="userId" + loading={loading} + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: false, + onChange: (p) => setPage(p), + }} + /> +
+ + +
+
+ ); +}; + +export default ReviewerSelector; diff --git a/src/pages/supplier/admission/admissionManagement/components/SupplierSelector.tsx b/src/pages/supplier/admission/admissionManagement/components/SupplierSelector.tsx new file mode 100644 index 0000000..a0f78bc --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/components/SupplierSelector.tsx @@ -0,0 +1,152 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, Input, Select, Row, Col, Table, Button, Form, Tooltip } from 'antd'; +import { RightOutlined, LeftOutlined } from '@ant-design/icons'; +import { coscoSupplierBase } from '../services'; + +const { Option } = Select; + +const SupplierSelector: React.FC<{ visible: boolean; onCancel: () => void; onSelect?: (selected: any[]) => void; }> = ({ visible, onCancel, onSelect }) => { + // 查询 + const [form] = Form.useForm(); + //列表渲染数据 + const [tableListData, setTableListData] = useState([]); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //列表加载 + const [loading, setLoading] = useState(false); + //已选供应商 + const [leftSelected, setLeftSelected] = useState([]); + //选择供应商 + const [rightSelected, setRightSelected] = useState([]); + //确认 已选供应商 + const [chosenSuppliers, setChosenSuppliers] = useState([]); + //已选供应商 去重 + const filteredData = (chosenSuppliers:any, selected:any) => { + const ids = new Set(chosenSuppliers.map((item:any) => item.id)); + // 只把 selected 里没出现过的加进去 + const newSelected = selected.filter((item:any) => !ids.has(item.id)); + return [...chosenSuppliers, ...newSelected]; + }; + //获取已选供应商 + const moveToRight = () => { + const selected = tableListData.filter((item:any) => leftSelected.includes(item.id)); + const chosenSuppliersNew = filteredData(chosenSuppliers, selected); + setChosenSuppliers(chosenSuppliersNew); + setLeftSelected([]); + }; + // 删除已选供应商 + const moveToLeft = () => { + const remaining = chosenSuppliers.filter((item:any) => !rightSelected.includes(item.id)); + setChosenSuppliers(remaining); + setRightSelected([]); + }; + // 列表方法 + const getTableList = async (values: any = {}, pageNo: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data } = await coscoSupplierBase({ ...values, pageNo, pageSize }); + if (code === 200) { + setTableListData(data.records); + setPagination({ current: pageNo, pageSize, total: data.total }); + } + } finally { + setLoading(false); + } + }; + // 初始化 + useEffect(() => { + const values = form.getFieldsValue(); + getTableList(values,1 , 10) + }, []) + //供应商名称 + const columns = [ + { title: '供应商名称', dataIndex: 'name', ellipsis: true, render: (name: string) => ( + + {name} + + ), }, + { title: '境内/境外', dataIndex: 'supplierType', render: (supplierType: string) => supplierType === 'dvs' ? '境内' : '境外'}, + ]; + return ( + + +
+ + + + + + + + + + + + + + + + +
+
待选供应商
+
{ + const values = form.getFieldsValue(); + getTableList(values, pagination.current!, pagination.pageSize!) + }} + scroll={{ y: 300 }} + /> + + + + +
+
+ + + + +
已选供应商
+
+ + + +
+ + +
+ + ); +}; + +export default SupplierSelector; diff --git a/src/pages/supplier/admission/admissionManagement/components/ViewModal.tsx b/src/pages/supplier/admission/admissionManagement/components/ViewModal.tsx new file mode 100644 index 0000000..424f9a8 --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/components/ViewModal.tsx @@ -0,0 +1,53 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, Descriptions } from 'antd'; + +import { coscoAccessWork } from '../services' + + +interface Data { + accessWorkName: string; + deptId: string; + accessTypeText: string; + createTime: string; + reviewStatusText: string; +} + +const ViewModal: React.FC<{ + visible: boolean; + record?: any; + onCancel: () => void; +}> = ({ visible, record = {}, onCancel }) => { + + const [data, setData] = useState(null); + + useEffect(() => { + if(record.id) { + coscoAccessWork(record.id).then((res) => { + const { code, data } = res; + if (code == 200) { + setData(data.coscoAccessWork) + } + }) + } + + + }, [record]) + + return ( + + {data && ( + + {data.accessWorkName} + {data.deptId} + {data.deptId} + {data.accessTypeText} + {data.createTime} + {data.reviewStatusText} + + )} + + + ); +}; + +export default ViewModal; \ No newline at end of file diff --git a/src/pages/supplier/admission/admissionManagement/index.tsx b/src/pages/supplier/admission/admissionManagement/index.tsx new file mode 100644 index 0000000..fbeafb7 --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/index.tsx @@ -0,0 +1,148 @@ +import React, { useState, useEffect } from 'react'; +import { Form, Select, Button, Table, Space } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +//查看弹窗 +import ViewModal from './components/ViewModal'; +//发起审批 弹窗 +import ApproveModal from './components/ApproveModal'; +//审批记录 弹窗 +import RecordModal from './components/RecordModal'; +//查看评审结果 弹窗 +import ResultModal from './components/ResultModal'; +//发起准入 弹窗 +import CreateModal from './components/CreateModal'; + +import { getPage } from './services' + +const { Option } = Select; + +interface Data { + id: string; + deptId: string; + accessTypeText: string; + createTime: string; + approveStatus: string; +} + +const AccessManagement: React.FC = () => { + // 查询 + const [form] = Form.useForm(); + //列表渲染数据 + const [data, setData] = useState([]); + // 发起准入、查看、发起审批、审批记录、查看评审结果 显示哪个组件状态 + const [modalInfo, setModalInfo] = useState<{ type: string; visible: boolean; record?: any }>({ type: '', visible: false, }); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //列表加载 + const [loading, setLoading] = useState(false); + // 列表方法 + const getList = async (values: any = {},pageNo: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data } = await getPage({ ...values, pageNo, pageSize }); + if (code === 200) { + setData(data.records); + setPagination({ current: pageNo, pageSize, total: data.total }); + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + const values = form.getFieldsValue(); + getList(values, 1, 10); + }, []); + //开启弹窗 + const openModal = (type: string, record?: any) => { + setModalInfo({ type, visible: true, record }); + }; + //关闭弹窗 + const closeModal = () => { + setModalInfo({ type: '', visible: false }); + }; + //列表头部数据 + const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + render: (_: any, __: any, index: number) => index + 1, + }, + { title: '准入工作', dataIndex: 'accessWorkName' }, + { title: '准入单位', dataIndex: 'deptId' }, + { title: '准入部门', dataIndex: 'deptId' }, + { title: '准入方式', dataIndex: 'accessTypeText' }, + { title: '申请时间', dataIndex: 'createTime' }, + { title: '状态', dataIndex: 'approveStatus' }, + { + title: '操作', + render: (_: any, record: any) => ( + + openModal('view', record)}>查看 + openModal('approve', record)}>发起审批 + openModal('record', record)}>审批记录 + openModal('result', record)}>查看评审结果 + + ), + }, + ]; + + return ( + <> +
+ + + + + + + + + + + + + + + + + + + +
+ +
+
{ + const values = form.getFieldsValue(); + getList(values, pagination.current!, pagination.pageSize!) + }} + /> + {/* 弹窗区 */} + + + + + + + ); +}; + +export default AccessManagement; diff --git a/src/pages/supplier/admission/admissionManagement/services.ts b/src/pages/supplier/admission/admissionManagement/services.ts new file mode 100644 index 0000000..f176f1b --- /dev/null +++ b/src/pages/supplier/admission/admissionManagement/services.ts @@ -0,0 +1,87 @@ +import request from '@/utils/request'; + +/** + * 准入列表 + */ +interface getPageData { + pageNo: number; + pageSize: number; + deptId?: string; + accessType?: string; + reviewStatus?: string; + approveStatus?: string; + categoryId?: string; +} +export const getPage = (data: getPageData) => request.post('/coscoAccessWork/getPage', { data }); + +/** + * 未准入的供应商分页列表查询 + */ +interface coscoSupplierBaseData { + pageNo: number; + pageSize: number; + accessStatus?: number; + name?: string; + supplierType?: string; +} +export const coscoSupplierBase = (data: coscoSupplierBaseData) => request.post('/coscoSupplierBase/getPage', { data }); + +/** + * 未准入的供应商分页列表查询 + */ +interface addInterface { + categoryIds: string[]; + coscoAccessItems: CoscoAccessItem[]; + coscoAccessUserls: CoscoAccessUserl[]; + coscoAccessWork: CoscoAccessWork; + supplierIds: string[]; + [property: string]: any; +} + +interface CoscoAccessItem { + itemName: string; + reviewBy: string[]; + [property: string]: any; +} + +interface CoscoAccessUserl { + deptId: string; + isLeader: number; + userId: string; + [property: string]: any; +} + +interface CoscoAccessWork { + accessType: string; + accessWorkName: string; + deptId: string; + endTime: string; + startTime: string; + [property: string]: any; +} +export const add = (data: addInterface) => request.post('/coscoAccessWork/add', { data }); + +/** + * 品类选择查询树 + */ +export const categoryTree = () => request.get('/cosco/category/categoryTree'); +/** + * 供应商准入管理详情 + */ +export const coscoAccessWork = (id: string) => request.get(`/coscoAccessWork/${id}`); + +/** + * 上传文件 + * @param file 上传的文件对象 + * @returns 上传结果 + */ +export const uploadFile = async (file: File) => { + const formData = new FormData(); + formData.append('file', file); + + return request('/fileConfig/files/upload', { + method: 'POST', + data: formData, + + }); +}; \ No newline at end of file diff --git a/src/pages/supplier/admission/admissionReviewManagement/_mock.ts b/src/pages/supplier/admission/admissionReviewManagement/_mock.ts new file mode 100644 index 0000000..a91db54 --- /dev/null +++ b/src/pages/supplier/admission/admissionReviewManagement/_mock.ts @@ -0,0 +1,343 @@ +import { Request, Response } from 'express'; + +// const data = [ +// { +// title: 'Name', +// dataIndex: '评审项', +// key: '评审项', +// }, +// { +// title: 'Other', +// children: [ +// { +// title: 'Age', +// key: '人员A', +// }, +// { +// title: 'Address', +// key: '人员B', +// }, +// ] +// }, +// ] +const dataInvoiceInfo = [ + { + id: '1', + taxpayerType: '一般纳税人', + taxpayerCode: '91345678901234567X', + head: '北京某科技有限公司', + address: '北京市朝阳区XX路99号', + phone: '010-12345678', + bank: '中国银行北京分行', + account: '6228888888888888', + updateTime: '2025-06-17 10:20:00', + voided: false, + qualificationCertificate: 'https://example.com/cert1.pdf', + }, + { + id: '2', + taxpayerType: '小规模纳税人', + taxpayerCode: '91345678901234566Y', + head: '上海某信息技术有限公司', + address: '上海市浦东新区XX大厦8楼', + phone: '021-87654321', + bank: '工商银行上海分行', + account: '6229999999999999', + updateTime: '2025-06-16 15:30:00', + voided: true, + qualificationCertificate: '', + }, +] +const mockQualificationData = [ + { + id: '1', + certificateType: '建筑业企业资质证书', + name: '建筑工程施工总承包一级', + code: 'ZJ-A123456', + typeLevel: '一级', + authority: '住房和城乡建设部', + dateTime: '2023-03-01', + termOfValidity: '2028-03-01', + updateTime: '2025-06-17 10:30:00', + }, + { + id: '2', + certificateType: '安全生产许可证', + name: '施工企业安全生产许可证', + code: 'AQ-789012', + typeLevel: 'A级', + authority: '应急管理部', + dateTime: '2022-06-15', + termOfValidity: '2025-06-15', + updateTime: '2025-06-17 11:45:00', + }, +] +export const mockData = { + base: { + "id": "123456", + "supplierType": "dvs", + "licenceAccessory": "https://example.com/license.pdf", + "licenceDate": "2025-12-31", + "enterpriseType": "company", + "name": "深圳供应商有限公司", + "nameEn": "Shenzhen Supplier Co., Ltd.", + "socialCreditCode": "91440300MA5F3XXXXQ", + "range": "电子元器件、金属材料销售", + "regAddress": "广东省深圳市南山区科技园", + "workAddress": "广东省深圳市南山区软件产业基地", + "parentCompanyInvestor": "深圳控股集团有限公司", + "legalPerson": "李四", + "idCard": "440301199001015678", + "capital": 5000, + "contactsName": "王五", + "contactsPhone": "13800138000", + "contactsType": "法人代表", + "contactsEmail": "contact@supplier.com", + "telephone": "0755-12345678", + "nation": "新加坡", + "vat": "SG12345678VAT", + "taxpayerId": "SG-TAX-998877", + "currency": "SGD", + "personName": "张三", + "personPhone": "13812345678", + "personBank": "中国银行深圳分行", + "personAccount": "6222020200123456789", + "remark": "该供应商已完成初步审核", + "accessStatus": 1, + "blacklistStatus": 0, + "greylistStatus": 1, + "fillinStatus": 0, + "fillinBy": "", + "sapCode": "SAP998877", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }, + qualifications: [{ + "id": "cert-001", + "supplierId": "supplier-123456", + "certificateType": "安全生产许可证", + "name": "建筑施工总承包一级资质", + "code": "ZJ20230605001", + "typeLevel": "一级", + "authority": "住房和城乡建设部", + "dateTime": "2023-06-05", + "termOfValidity": "2026-06-05", + "accessory": "https://example.com/certificate.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2023-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2024-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }], + invoice: { + "id": "invoice-001", + "supplierId": "supplier-123456", + "taxpayerType": "general", + "taxpayerCode": "91440300MA5F3XXXXQ", + "phone": "0755-12345678", + "account": "6222020200123456789", + "head": "深圳供应商有限公司", + "address": "深圳市南山区科技园开票楼101号", + "bank": "中国银行深圳分行", + "qualificationCertificate": "https://example.com/tax-cert.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }, + bank: [{ + "id": "bank-001", + "supplierId": "supplier-123456", + "interbankNumber": "123456789012", + "bank": "中国银行深圳分行", + "swiftCode": "BKCHCNBJ45A", + "accountName": "Shenzhen Supplier Co., Ltd.", + "account": "6222020200123456789", + "currency": "CNY", + "nation": "中国", + "province": "广东省", + "city": "深圳市", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }], + survey: { + "supplierName": "深圳供应商有限公司", + "name": "李四", + "position": "采购经理", + "phone": "13800138000", + "email": "lisi@supplier.com", + "dateTime": "2025-06-17", + }, + questionReply: [ + { + "surveyQuestion": "法律法规:\n我们确保经营和提供的产品服务遵守国家及 各业务所在地的所有使用法律、法规", + "replyValue": "是", + },{ + "surveyQuestion": "健康和安全:\n我们为员工提供符合法律法规的安全且健康 的工作场所。我们建立安全管理体系,并向 员工传达工作场所或生活设施的健康和安全 标准,致力于减少工作对员工造成的伤害和 疾病。", + "replyValue": "符合", + },{ + "surveyQuestion": "环境:\n我们能够以环境友好的方式经营。我们遵守 适用的环境法律、法规和标准;并建立有效 的环境管理体系。\n我们遵守贵集团对相关产品或服务的部分附 加环境要求,这些要求和规定体现在设计与 产品规范的合同文档中。", + "replyValue": "符合", + },{ + "surveyQuestion": "监督和记录:\n我们保留记录遵守相关法律和此行为准则的必要文件,并根据要求为贵集团提供对文件的查看权。我们会允许贵集团在适当的时候,以验证行为准则执行为目的的现场勘查", + "replyValue": "符合", + } + ], + attachments: { + "attachmentsType": "commitment", + "fileName": "anti-bribery-commitment.pdf", + "fileType": "pdf", + "fileSize": "204800", + "filePath": "/data/files/anti-bribery-commitment.pdf", + "fileUrl": "http://example.com/files/anti-bribery-commitment.pdf", + } +}; +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + // 供应商信息 + 'GET /api/system/coscoSupplier': (req: Request, res: Response) => { + res.json({ + code: 200, + data: mockData, + msg: '操作成功' + }); + }, + // + 'GET /api/system/qualifications': (req: Request, res: Response) => { + res.json({ code: 200, + data: mockQualificationData, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/invoice': (req: Request, res: Response) => { + res.json({ code: 200, + data: dataInvoiceInfo, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/bank': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + id: '1', + interbankNumber: '123456789', + bank: '中国银行', + accountName: '张三', + account: '6228480000000000000', + currency: '人民币', + nation: '中国', + province: '广东省', + city: '广州市', + updateTime: '2024-06-18', + }, + { + id: '2', + interbankNumber: '987654321', + bank: '工商银行', + accountName: '李四', + account: '6228480000000000001', + currency: '美元', + nation: '中国', + province: '江苏省', + city: '南京市', + updateTime: '2024-06-17', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/tianyancha': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + key: '1', + base: '京', + name: '北京科技有限公司', + legalPersonName: '张三', + legalPersonType: '1', + regNumber: '110108123456789', + industry: '信息技术', + companyOrgType: '有限责任公司', + regLocation: '北京市海淀区中关村', + estiblishTime: '2010-06-15', + fromTime: '2010-06-16', + toTime: '2025-06-15', + businessScope: '软件开发、技术咨询', + approvedTime: '2010-06-10', + regStatus: '存续', + regCapital: '5000万元', + regInstitute: '北京市工商局', + orgNumber: '1234567890', + creditCode: '91110108MA01A12345', + property3: 'Beijing Tech Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1001', + taxNumber: '110108123456789', + email: 'contact@bjtech.com', + website: 'http://www.bjtech.com', + phoneNumber: '010-12345678', + lastUpdateTime: '2025-06-15 10:00:00', + }, + { + key: '2', + base: '沪', + name: '上海电子商务有限公司', + legalPersonName: '李四', + legalPersonType: '1', + regNumber: '310101987654321', + industry: '电子商务', + companyOrgType: '股份有限公司', + regLocation: '上海市浦东新区', + estiblishTime: '2015-03-20', + fromTime: '2015-03-21', + toTime: '2030-03-20', + businessScope: '电子商务平台运营、广告设计', + approvedTime: '2015-03-15', + regStatus: '存续', + regCapital: '1亿元', + regInstitute: '上海市工商局', + orgNumber: '0987654321', + creditCode: '91310101MA1AB23456', + property3: 'Shanghai E-commerce Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1002', + taxNumber: '310101987654321', + email: 'info@shcommerce.com', + website: 'http://www.shcommerce.com', + phoneNumber: '021-87654321', + lastUpdateTime: '2025-06-15 09:30:00', + }, + ], + total: 2, + msg: '操作成功' }); + }, + + 'GET /api/500': (req: Request, res: Response) => { + res.status(500).send({ + timestamp: 1513932555104, + status: 500, + error: 'error', + message: 'error', + path: '/base/category/list', + }); + }, + + +}; diff --git a/src/pages/supplier/admission/admissionReviewManagement/components/ResultModal.tsx b/src/pages/supplier/admission/admissionReviewManagement/components/ResultModal.tsx new file mode 100644 index 0000000..694315f --- /dev/null +++ b/src/pages/supplier/admission/admissionReviewManagement/components/ResultModal.tsx @@ -0,0 +1,257 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Table, Button, Radio, Input, Upload, message, Spin } from 'antd'; +import { reviewInfo, uploadFile } from '../services'; // 你的接口 + +interface ResultModalProps { + visible: boolean; + record?: { id?: string; [key: string]: any } | null; + onCancel: () => void; + onSubmit: (payload: any) => void; +} + +// 单元格内容 +type CellValue = { + reviewResult?: 0 | 1; // 0合格 1不合格 + remark?: string; + file?: any; +}; + +const ResultModal: React.FC = ({ + visible, + record, + onCancel, + onSubmit +}) => { + const [suppliers, setSuppliers] = useState([]); + const [items, setItems] = useState([]); + const [cellData, setCellData] = useState<{ + [itemId: string]: { [supplierId: string]: CellValue } + }>({}); + const [loading, setLoading] = useState(false); + + // 备注弹窗 + const [remarksModalVisible, setRemarksModalVisible] = useState(false); + const [remarks, setRemarks] = useState(''); + const [fileList, setFileList] = useState([]); + const [currentCell, setCurrentCell] = useState<{ itemId: string, supplierId: string } | null>(null); + + // 拉取评审数据 + useEffect(() => { + if (visible && record?.id) { + setLoading(true); + reviewInfo({ id: record.id, userId: 'E0001' }) + .then((res: any) => { + const data = res?.data || []; + setSuppliers(data); + + // 用第一家供应商的coscoAccessItemList生成所有评审项 + const firstSupplier = data[0] || {}; + const itemList = (firstSupplier.coscoAccessItemList || []).filter((i: { itemType:string } ) => i.itemType !== 'summary'); + setItems(itemList); + + // 初始化 cellData + const newCellData: any = {}; + itemList.forEach((item: any) => { + newCellData[item.id] = {}; + data.forEach((sup: any) => { + newCellData[item.id][sup.supplierId] = {}; + }); + }); + setCellData(newCellData); + }) + .finally(() => setLoading(false)); + } + }, [visible, record]); + + // 单选 + const handleRadioChange = (itemId: string, supplierId: string, value: string) => { + setCellData(prev => ({ + ...prev, + [itemId]: { + ...prev[itemId], + [supplierId]: { + ...prev[itemId]?.[supplierId], + reviewResult: value === '合格' ? 0 : 1 + } + } + })); + }; + + // 打开备注弹窗 + const openRemarksModal = (itemId: string, supplierId: string) => { + setCurrentCell({ itemId, supplierId }); + const cell = cellData[itemId]?.[supplierId] || {}; + setRemarks(cell.remark || ''); + setFileList(cell.file + ? [{ + uid: '-1', + name: cell.file.fileName, + status: 'done', + url: cell.file.fileUrl, + response: cell.file + }] + : []); + setRemarksModalVisible(true); + }; + + // 上传 + const uploadProps = { + fileList, + maxCount: 1, + onRemove: () => setFileList([]), + customRequest: async (options: any) => { + + const res = await uploadFile(options.file); + const fileObj = res; + setFileList([{ + uid: options.file.uid, + name: fileObj.fileName, + status: 'done', + url: fileObj.url, + response: fileObj + }]); + message.success('文件上传成功'); + options.onSuccess && options.onSuccess(res, options.file); + + + } + }; + + // 备注提交 + const handleSubmitRemarks = () => { + if (!currentCell) return; + setCellData(prev => ({ + ...prev, + [currentCell.itemId]: { + ...prev[currentCell.itemId], + [currentCell.supplierId]: { + ...prev[currentCell.itemId]?.[currentCell.supplierId], + remark: remarks, + file: fileList[0]?.response || undefined + } + } + })); + setRemarksModalVisible(false); + }; + + // 提交 + const handleSubmit = () => { + // 组装参数 + let result: any[] = []; + items.forEach(item => { + suppliers.forEach(sup => { + const cell = cellData?.[item.id]?.[sup.supplierId]; + if (cell && cell.reviewResult !== undefined) { + result.push({ + id: item.id, + reviewResult: cell.reviewResult, + remark: cell.remark || '', + coscoAccessTtemAttachments: cell.file || undefined + }); + } + }); + }); + + console.log(items,'items'); + console.log(result,'result'); + + + onSubmit && onSubmit({ coscoAccessUserItemList: result }); + }; + + // 组装表头 + const columns = [ + { + title: '评审项', + dataIndex: 'itemName', + key: 'itemName', + width: 200, + fixed: 'left' + }, + ...suppliers.map(sup => ({ + title: sup.supplierName, + dataIndex: sup.supplierId, + key: sup.supplierId, + width: 300, + render: (_: any, row: any) => { + const v = cellData?.[row.key]?.[sup.supplierId] || {}; + console.log(sup,'sup'); + + return ( +
+ handleRadioChange(row.key, sup.supplierId, e.target.value)} + > + 合格 + 不合格 + + + {v.remark && 已备注} + {v.file && 已上传} +
+ ); + } + })) + ]; + + // 行数据 + const tableData = items.map(item => ({ + key: item.id, + itemName: item.itemName + })); + + return ( + 取消, + + ]} + width={1000} + bodyStyle={{ maxHeight: '60vh', overflowY: 'auto' }} + centered + destroyOnClose + > + +
+ + setRemarksModalVisible(false)} + footer={[ + , + + ]} + destroyOnClose + > + setRemarks(e.target.value)} + /> + + + + + + ); +}; + +export default ResultModal; diff --git a/src/pages/supplier/admission/admissionReviewManagement/index.tsx b/src/pages/supplier/admission/admissionReviewManagement/index.tsx new file mode 100644 index 0000000..40a5e70 --- /dev/null +++ b/src/pages/supplier/admission/admissionReviewManagement/index.tsx @@ -0,0 +1,166 @@ +import React, { useEffect, useState } from "react"; +import { connect, useIntl } from 'umi'; +import { Form, Button, Table, Select, Space, Input } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { SearchOutlined } from '@ant-design/icons'; +import { getPage } from './services'; +//查看评审结果 弹窗 +import ResultModal from './components/ResultModal'; + +interface Data { + deptName: string; + categoryName: string; + createTime: string; + exitTime: string; + exitReason: string; +} + +interface ModalInfo { + type: 'view' | 'result' | null; + visible: boolean; + record: Data | null; +} + + +const CooperateEnterprise: React.FC = () => { + const [searchForm] = Form.useForm(); + const intl = useIntl(); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + const [modalInfo, setModalInfo] = useState({ type: null, visible: false, record: null }); + + const openModal = (type: 'view' | 'result', record: Data) => { + setModalInfo({ type, visible: true, record }); + }; + + const closeModal = () => { + setModalInfo({ type: null, visible: false, record: null }); + }; + // 列表数据 + const getList = async (params: { pageNo: number; pageSize: number; parentCode: string; }) => { + setLoading(true); + try { + const { code, data } = await getPage(params); + if (code === 200) { + setData(data.records); + setPagination({ current: params.pageNo, pageSize: params.pageSize, total: data.total }); + } + } catch (error) { + console.error('Failed to fetch data:', error); + } finally { + setLoading(false); + } + }; + + const handleReset = () => { + searchForm.resetFields(); + getList({ pageNo: 1, pageSize: pagination.pageSize ?? 10, parentCode: '' }); + }; + + const handleSearch = (values: any) => { + const { parentCode } = values; + getList({ + pageNo: 1, + pageSize: pagination.pageSize ?? 10, + parentCode: parentCode || '' + }); + }; + + useEffect(() => { + getList({ pageNo: 1, pageSize: 10, parentCode: '' }); + }, []); + + const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + width: 80, + align: 'center', + render: (_: any, __: any, index: number) => index + 1, + }, + { + title: '准入工作', + dataIndex: 'accessWorkName', + key: 'accessWorkName', + }, + { + title: '发起单位', + dataIndex: 'deptId', + key: 'deptId', + }, + { + title: '准入部门', + dataIndex: 'deptId', + key: 'deptId', + }, + { + title: '品类', + dataIndex: 'exitTime', + key: 'exitTime', + }, + { + title: '准入方式', + dataIndex: 'accessTypeText', + key: 'accessTypeText', + }, + { + title: '评审时间', + dataIndex: 'createTime', + key: 'createTime', + }, + { + title: '评审状态', + dataIndex: 'reviewStatusText', + key: 'reviewStatusText', + }, + { + title: '操作', + render: (_: any, record: any) => ( + + openModal('result', record)}>审核 + + ), + }, + ]; + return ( + <> +
+ + + + + + + + + + +
getList({ pageNo: pagination.current!, pageSize: pagination.pageSize!, parentCode: '', })} + /> + + + + ); +}; + +export default connect()(CooperateEnterprise); \ No newline at end of file diff --git a/src/pages/supplier/admission/admissionReviewManagement/services.ts b/src/pages/supplier/admission/admissionReviewManagement/services.ts new file mode 100644 index 0000000..5134f6a --- /dev/null +++ b/src/pages/supplier/admission/admissionReviewManagement/services.ts @@ -0,0 +1,78 @@ +import request from '@/utils/request'; + + + + +/** + * 准入列表 + */ +interface getPageData { + pageNo: number; + pageSize: number; + parentCode?: string; +} +export const getPage = (data: getPageData) => request.post('/coscoAccessWork/getPage', { data }); + /** + * 评审修改时用的详情页 + */ + interface reviewInfoData { + id: string; + userId?: string; + } + export const reviewInfo = (params: reviewInfoData) => request.get(`/coscoAccessWork/reviewInfo`, { params }); + +/** + * 上传文件 + * @param file 上传的文件对象 + * @returns 上传结果 + */ +export const uploadFile = async (file: File) => { + const formData = new FormData(); + formData.append('file', file); + + return request('/fileConfig/files/upload', { + method: 'POST', + data: formData, + + }); +}; + + + + + +export async function coscoSupplier(params:any) { + console.log(params,'params'); + + return request('/api/system/coscoSupplier', { + method: 'GET', + params + }); +} + +export async function library(params:any) { + return request('/api/system/library', { + method: 'GET', + params + }); +} + +export async function qualifications(params:any) { + return request('/api/system/qualifications', { + method: 'GET', + params + }); +} +export async function invoice(params:any) { + return request('/api/system/invoice', { + method: 'GET', + params + }); +} +export async function bank(params:any) { + return request('/api/system/bank', { + method: 'GET', + params + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/backend/changeProgressInquiry/_mock.ts b/src/pages/supplier/backend/changeProgressInquiry/_mock.ts new file mode 100644 index 0000000..e3a8d7d --- /dev/null +++ b/src/pages/supplier/backend/changeProgressInquiry/_mock.ts @@ -0,0 +1,67 @@ +import { Request, Response } from 'express'; + +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + + 'GET /api/system/getPage': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { + deptName: '供应商名称变更', + categoryName: '2024-05-20 13:22:11', + createTime: '集团采购部', + exitTime: '已通过', + exitReason: '2024-05-21 09:10:31', + }, + { + deptName: '法人代表变更', + categoryName: '2024-05-18 08:30:55', + createTime: '分公司审核部', + exitTime: '审核中', + exitReason: '', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/getSupplierChangeDetail': (req: Request, res: Response) => { + res.json({ + "code": 200, + "msg": "success", + "data": { + baseInfo: [ + { label: '供应商名称', value: 'xxx' }, + { label: '境内/境外', value: '境内' }, + { label: '准入单位', value: 'xxxx' }, + { label: '准入部门', value: '采购部' }, + ], + changeInfo: [ + { label: '供应商名称-变更前', value: 'xxxx' }, + { label: '供应商名称-变更后', value: 'xxxx' }, + ], + "supplierName": "中山市合创展包装材料有限公司", + "accessUnit": "中远海运(青岛)有限公司", + "region": "境内", + "accessDept": "采购部", + "beforeName": "中山市合创展包装材料有限公司", + "afterName": "中山市合创展包装有限公司", + "qualifications": [ + { + "type": "CMMI资质", + "name": "CMMI资质", + "level": "高级", + "number": "546464", + "org": "XX机构", + "issueDate": "2024-09-08", + "validDate": "2028-09-10", + "file": "https://dummyimage.com/40x30/1890ff/fff.png&text=附件" + } + ] + } + } + ); + }, +}; diff --git a/src/pages/supplier/backend/changeProgressInquiry/components/DetailView.tsx b/src/pages/supplier/backend/changeProgressInquiry/components/DetailView.tsx new file mode 100644 index 0000000..14c2dec --- /dev/null +++ b/src/pages/supplier/backend/changeProgressInquiry/components/DetailView.tsx @@ -0,0 +1,144 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Table, Spin } from 'antd'; +import { getSupplierChangeDetail } from '../services'; + +interface Qualification { + type: string; + name: string; + level: string; + number: string; + org: string; + issueDate: string; + validDate: string; + file?: string; +} +interface InfoItem { + label: string; + value: string; +} +interface DetailData { + baseInfo: InfoItem[]; + changeInfo: InfoItem[]; + qualifications: Qualification[]; +} +interface DetailViewProps { + visible: boolean; + onClose: () => void; + detailId?: string | number; +} + +const DetailView: React.FC = ({ visible, onClose, detailId }) => { + + const [loading, setLoading] = useState(false); + const [detailData, setDetailData] = useState(null); + + useEffect(() => { + if (visible && detailId) { + setLoading(true); + getSupplierChangeDetail(detailId) + .then(res => { + if (res.code === 200) { + setDetailData(res.data); + } + }) + .finally(() => setLoading(false)); + } + if (!visible) setDetailData(null); + }, [visible, detailId]); + + const columns = [ + { title: '资质证书类型', dataIndex: 'type', width: 120 }, + { title: '资质名称', dataIndex: 'name', width: 120 }, + { title: '资质类别和等级', dataIndex: 'level', width: 120 }, + { title: '资质证书编号', dataIndex: 'number', width: 120 }, + { title: '发证机构', dataIndex: 'org', width: 120 }, + { title: '发证日期', dataIndex: 'issueDate', width: 120 }, + { title: '资质有效期至', dataIndex: 'validDate', width: 120 }, + { + title: '附件', + dataIndex: 'file', + width: 120, + render: (file: string) => + file ? ( + + 附件 + 更多 + + ) : ( + '-' + ), + }, + ]; + + // 把info数组两两合并成一行显示 + function renderInfoTable(infoArr: InfoItem[]) { + const rows = []; + for (let i = 0; i < infoArr.length; i += 2) { + const left = infoArr[i]; + const right = infoArr[i + 1] || { label: '', value: '' }; + rows.push( + + + + + + + ); + } + return ( +
{left.label}{left.value}{right.label}{right.value}
+ {rows} +
+ ); + } + + return ( + + + {detailData && ( +
+ {/* 基本信息 */} + {renderInfoTable(detailData.baseInfo)} + {/* 变更内容 */} +
变更内容:
+ {renderInfoTable(detailData.changeInfo)} + {/* 新增资质 */} +
新增资质1
+ + + )} + + + ); +}; + +const tdStyleTitle: React.CSSProperties = { + background: '#fafafa', + fontWeight: 700, + width: 130, + padding: 8, + border: '1px solid #f0f0f0', +}; +const tdStyle: React.CSSProperties = { + background: '#fff', + padding: 8, + border: '1px solid #f0f0f0', +}; + +export default DetailView; diff --git a/src/pages/supplier/backend/changeProgressInquiry/index.tsx b/src/pages/supplier/backend/changeProgressInquiry/index.tsx new file mode 100644 index 0000000..51a83d4 --- /dev/null +++ b/src/pages/supplier/backend/changeProgressInquiry/index.tsx @@ -0,0 +1,180 @@ +import React, { useEffect, useState } from "react"; +import { connect, useIntl } from 'umi'; +import { Form, Button, Table, Select, DatePicker, Input } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { SearchOutlined } from '@ant-design/icons'; +import { getPage } from './services'; +import moment from 'moment'; +import DetailView from './components/DetailView'; + +interface Data { + deptName: string; + categoryName: string; + createTime: string; + exitTime: string; + exitReason: string; +} + +const CooperateEnterprise: React.FC = () => { + //双语 + const intl = useIntl(); + //查询 + const [searchForm] = Form.useForm(); + //列表数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //弹出组件开关状态 + const [detailVisible, setDetailVisible] = useState(false); + //弹出组件参数 + const [currentDetail, setCurrentDetail] = useState(null); + //列表头部 + const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + width: 80, + align: 'center', + render: (_: any, __: any, index: number) => index + 1, + }, + { + title: '变更内容', + dataIndex: 'deptName', + key: 'deptName', + }, + { + title: '提交时间', + dataIndex: 'categoryName', + key: 'categoryName', + }, + { + title: '审批单位', + dataIndex: 'createTime', + key: 'createTime', + }, + { + title: '审批状态', + dataIndex: 'exitTime', + key: 'exitTime', + }, + { + title: '审批时间', + dataIndex: 'exitReason', + key: 'exitReason', + }, + { + title: '操作', + key: 'action', + render: (text: string, record: Data) => ( + + ), + }, + ]; + //重置 + const handleReset = () => { + searchForm.resetFields(); + getList({ page: 1, pageSize: pagination.pageSize ?? 10, parentCode: '', startTime: '', endTime: '' }); + }; + //搜索 + const handleSearch = (values: any) => { + const { parentCode, createTime } = values; + const startTime = createTime ? moment(createTime[0]).format('YYYY-MM-DD') : ''; + const endTime = createTime ? moment(createTime[1]).format('YYYY-MM-DD') : ''; + getList({ + page: 1, + pageSize: pagination.pageSize ?? 10, + parentCode: parentCode || '', + startTime, + endTime, + }); + }; + //开启弹出 + const handleDetail = (record: Data) => { + setCurrentDetail(record); + setDetailVisible(true); + }; + //关闭演出 + const handleDetailClose = () => { + setDetailVisible(false); + }; + //列表数据请求 + const getList = async (params: { page: number; pageSize: number; parentCode: string; startTime: string; endTime: string }) => { + setLoading(true); + try { + const { code , data, total } = await getPage(params); + if (code === 200) { + setData(data); + setPagination({ current: params.page, pageSize: params.pageSize, total }); + } + } catch (error) { + console.error('Failed to fetch data:', error); + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + getList({ page: 1, pageSize: 10, parentCode: '', startTime: '', endTime: '' }); + }, []); + + return ( + <> +
+ + + + + + + + + + + + + + + + + + + +
getList({ page: pagination.current!, pageSize: pagination.pageSize!, parentCode: '', startTime: '', endTime: '' })} + /> + + + ); +}; + +export default connect()(CooperateEnterprise); \ No newline at end of file diff --git a/src/pages/supplier/backend/changeProgressInquiry/services.ts b/src/pages/supplier/backend/changeProgressInquiry/services.ts new file mode 100644 index 0000000..cb1a94b --- /dev/null +++ b/src/pages/supplier/backend/changeProgressInquiry/services.ts @@ -0,0 +1,18 @@ +import request from '@/utils/request'; + + + + +export async function getPage(params:any) { + return request('/api/system/getPage', { + method: 'GET', + params + }); +} + +export async function getSupplierChangeDetail(params:any) { + return request('/api/system/getSupplierChangeDetail', { + method: 'GET', + params + }); +} diff --git a/src/pages/supplier/backend/cooperateEnterprise/_mock.ts b/src/pages/supplier/backend/cooperateEnterprise/_mock.ts new file mode 100644 index 0000000..29f60b1 --- /dev/null +++ b/src/pages/supplier/backend/cooperateEnterprise/_mock.ts @@ -0,0 +1,63 @@ +import { Request, Response } from 'express'; +const categoryData = { "code": 200, "success": true, "message": "success", "data": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:22:16", "updateBy": null, "updateTime": null, "remark": null, "id": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "categoryName": "添加", "parentId": "0", "type": "0", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:16", "updateBy": "1", "updateTime": "2025-06-16 10:37:30", "remark": null, "id": "0c653095-a553-48a7-9c2f-6fc708769dd6", "categoryName": "二级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "0", "orderBy": "3", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:59", "updateBy": null, "updateTime": null, "remark": null, "id": "ff59e929-864e-4080-a9c5-852cab7ab129", "categoryName": "一级商品", "parentId": "0c653095-a553-48a7-9c2f-6fc708769dd6", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6,ff59e929-864e-4080-a9c5-852cab7ab129", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }, { "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:29:09", "updateBy": null, "updateTime": null, "remark": null, "id": "ffd210c8-026b-4ec5-aa1e-40d70feb905e", "categoryName": "三级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,ffd210c8-026b-4ec5-aa1e-40d70feb905e", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }] } +export default { + + 'GET /api/system/mockList': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { + id: 1, + name: "境外燃油备件库", + category: "货物)燃料)燃油", + region: "新加坡", + creator: "集团", + validDate: "2028-03-31", + applyDate: "2025-03-02", + processStatus: "未开始", + result: "", + }, + { + id: 2, + name: "境外燃油备件库", + category: "货物)燃料)燃油", + region: "马来西亚", + creator: "集团", + validDate: "2028-03-31", + applyDate: "2025-03-02", + processStatus: "未开始", + result: "", + }, + { + id: 3, + name: "润滑油库", + category: "生产型物资)润滑剂)润滑油", + region: "全球", + creator: "集团", + validDate: "2028-03-31", + applyDate: "2025-03-02", + processStatus: "进行中", + result: "", + }, + { + id: 4, + name: "船用物料库", + category: "生产型物资)船用物料", + region: "全球", + creator: "集团", + validDate: "2028-03-31", + applyDate: "2025-03-02", + processStatus: "已结束", + result: "通过", + }, + ], + total: 188, + msg: "success", + }); + }, + + 'GET /api/system/categoryOption': (req: Request, res: Response) => { + res.json(categoryData); + }, + +}; diff --git a/src/pages/supplier/backend/cooperateEnterprise/index.tsx b/src/pages/supplier/backend/cooperateEnterprise/index.tsx new file mode 100644 index 0000000..94a89da --- /dev/null +++ b/src/pages/supplier/backend/cooperateEnterprise/index.tsx @@ -0,0 +1,161 @@ +import React, { useEffect, useState } from "react"; +import { connect, useIntl } from 'umi'; +import { Form, Button, Table, Select, DatePicker, TreeSelect } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { SearchOutlined } from '@ant-design/icons'; +import { category, categoryOption } from './services'; +import moment from 'moment'; + +interface Data { + deptName: string; + categoryName: string; + createTime: string; + exitTime: string; + exitReason: string; +} + +// 将 categoryTree 的字段从 categoryName 和 id 映射到 label 和 value +const transformTreeData = (treeData: any[]): any[] => { + return treeData.map((item) => ({ + value: item.id, + label: item.categoryName, + children: item.children ? transformTreeData(item.children) : undefined, + })); +}; +// 列表头 +const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + width: 80, + align: 'center', + render: (_: any, __: any, index: number) => index + 1, + }, + { + title: '准入部门', + dataIndex: 'deptName', + key: 'deptName', + }, + { + title: '准入品类', + dataIndex: 'categoryName', + key: 'categoryName', + }, + { + title: '准入时间', + dataIndex: 'createTime', + key: 'createTime', + }, + { + title: '退出时间', + dataIndex: 'exitTime', + key: 'exitTime', + }, + { + title: '退出原因', + dataIndex: 'exitReason', + key: 'exitReason', + ellipsis: true, + }, +]; + +const CooperateEnterprise: React.FC = () => { + //双语 + const intl = useIntl(); + //搜索 + const [searchForm] = Form.useForm(); + //列表数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //搜索中树形下拉数据 + const [categoryTree, setCategoryTree] = useState([]); + //列表数据方法 + const getList = async (params: { page: number; pageSize: number; categoryName: string; startTime: string; endTime: string }) => { + setLoading(true); + try { + const { code, data, total } = await category(params); + if (code === 200) { + setData(data); + setPagination({ current: params.page, pageSize: params.pageSize, total}); + } + } catch (error) { + console.error('Failed to fetch data:', error); + } finally { + setLoading(false); + } + }; + //搜索重置 + const handleReset = () => { + searchForm.resetFields(); + getList({ page: 1, pageSize: pagination.pageSize ?? 10, categoryName: '', startTime: '', endTime: '' }); + }; + //搜索 + const handleSearch = (values: any) => { + const { categoryName, createTime } = values; + const startTime = createTime ? moment(createTime[0]).format('YYYY-MM-DD') : ''; + const endTime = createTime ? moment(createTime[1]).format('YYYY-MM-DD') : ''; + getList({ + page: 1, + pageSize: pagination.pageSize ?? 10, + categoryName: categoryName || '', + startTime, + endTime, + }); + }; + //初始化 + useEffect(() => { + categoryOption().then((res:any) => { + const { code, data } = res; + if(code == 200) { + setCategoryTree(transformTreeData(data)) + } + }) + getList({ page: 1, pageSize: 10, categoryName: '', startTime: '', endTime: '' }); + }, []); + return ( + <> +
+ + + + + + + + + + + + + +
getList({ page: pagination.current!, pageSize: pagination.pageSize!, categoryName: '', startTime: '', endTime: '' })} + /> + + ); +}; + +export default connect()(CooperateEnterprise); \ No newline at end of file diff --git a/src/pages/supplier/backend/cooperateEnterprise/services.ts b/src/pages/supplier/backend/cooperateEnterprise/services.ts new file mode 100644 index 0000000..3638ebe --- /dev/null +++ b/src/pages/supplier/backend/cooperateEnterprise/services.ts @@ -0,0 +1,16 @@ +import request from '@/utils/request'; + + +export async function category(params:any) { + return request('/api/system/category', { + method: 'GET', + params + }); +} + +export async function categoryOption() { + return request('/api/system/categoryOption', { + method: 'GET' + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/backend/supplierNews/_mock.ts b/src/pages/supplier/backend/supplierNews/_mock.ts new file mode 100644 index 0000000..c940ee1 --- /dev/null +++ b/src/pages/supplier/backend/supplierNews/_mock.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express'; + +export default { + 'GET /api/system/bank': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + id: '1', + interbankNumber: '123456789', + bank: '中国银行', + accountName: '张三', + account: '6228480000000000000', + currency: '人民币', + nation: '中国', + province: '广东省', + city: '广州市', + updateTime: '2024-06-18', + }, + { + id: '2', + interbankNumber: '987654321', + bank: '工商银行', + accountName: '李四', + account: '6228480000000000001', + currency: '美元', + nation: '中国', + province: '江苏省', + city: '南京市', + updateTime: '2024-06-17', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + 'GET /api/system/categoryOption': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + value: '1', + label: '1', + }, + { + value: '2', + label: '3', + }, + ], + total: 2, + msg: '操作成功' + }); + }, +}; diff --git a/src/pages/supplier/backend/supplierNews/index.tsx b/src/pages/supplier/backend/supplierNews/index.tsx new file mode 100644 index 0000000..24dbe69 --- /dev/null +++ b/src/pages/supplier/backend/supplierNews/index.tsx @@ -0,0 +1,152 @@ +import React, { useEffect, useState } from "react"; +import { connect, useIntl } from 'umi'; +import { Form, Button, Table, Select, Input } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { SearchOutlined } from '@ant-design/icons'; +import { bank, categoryOption } from './services'; + +interface Data { + deptName: string; + categoryName: string; + createTime: string; + exitTime: string; + exitReason: string; +} + +interface CategoryOption { + value: string; + label: string; +} + +const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + width: 80, + align: 'center', + render: (_: any, __: any, index: number) => index + 1, + }, + { + title: '消息内容', + dataIndex: 'deptName', + key: 'deptName', + }, + { + title: '业务类型', + dataIndex: 'categoryName', + key: 'categoryName', + }, + { + title: '发送时间', + dataIndex: 'createTime', + key: 'createTime', + }, + { + title: '审核单位', + dataIndex: 'exitTime', + key: 'exitTime', + }, + { + title: '审核状态', + dataIndex: 'exitReason', + key: 'exitReason', + }, +]; + +const CooperateEnterprise: React.FC = () => { + //双语 + const intl = useIntl(); + //搜索 + const [searchForm] = Form.useForm(); + //列表数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + //下拉数据 + const [categoryOptions, setCategoryOptions] = useState([]); + //列表数据方法 + const getList = async (params: { page: number; pageSize: number; parentCode: string; }) => { + setLoading(true); + try { + const response = await bank(params); + if (response.code === 200) { + setData(response.data); + setPagination({ current: params.page, pageSize: params.pageSize, total: response.total }); + } + } catch (error) { + console.error('Failed to fetch data:', error); + } finally { + setLoading(false); + } + }; + //搜索重置 + const handleReset = () => { + searchForm.resetFields(); + getList({ page: 1, pageSize: pagination.pageSize ?? 10, parentCode: '' }); + }; + //搜索 + const handleSearch = (values: any) => { + const { parentCode } = values; + getList({ + page: 1, + pageSize: pagination.pageSize ?? 10, + parentCode: parentCode || '' + }); + }; + //初始化 + useEffect(() => { + categoryOption().then((res:any) => { + const { code, data } = res; + if(code == 200) { + setCategoryOptions(data) + } + }) + getList({ page: 1, pageSize: 10, parentCode: '', }); + }, []); + + return ( + <> +
+ + + + + + + + + + + + + +
getList({ page: pagination.current!, pageSize: pagination.pageSize!, parentCode: '', })} + /> + + ); +}; + +export default connect()(CooperateEnterprise); \ No newline at end of file diff --git a/src/pages/supplier/backend/supplierNews/services.ts b/src/pages/supplier/backend/supplierNews/services.ts new file mode 100644 index 0000000..2c1474b --- /dev/null +++ b/src/pages/supplier/backend/supplierNews/services.ts @@ -0,0 +1,16 @@ +import request from '@/utils/request'; + + +export async function bank(params:any) { + return request('/api/system/bank', { + method: 'GET', + params + }); +} + +export async function categoryOption() { + return request('/api/system/categoryOption', { + method: 'GET' + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/backend/workbenches/_mock.ts b/src/pages/supplier/backend/workbenches/_mock.ts new file mode 100644 index 0000000..a91db54 --- /dev/null +++ b/src/pages/supplier/backend/workbenches/_mock.ts @@ -0,0 +1,343 @@ +import { Request, Response } from 'express'; + +// const data = [ +// { +// title: 'Name', +// dataIndex: '评审项', +// key: '评审项', +// }, +// { +// title: 'Other', +// children: [ +// { +// title: 'Age', +// key: '人员A', +// }, +// { +// title: 'Address', +// key: '人员B', +// }, +// ] +// }, +// ] +const dataInvoiceInfo = [ + { + id: '1', + taxpayerType: '一般纳税人', + taxpayerCode: '91345678901234567X', + head: '北京某科技有限公司', + address: '北京市朝阳区XX路99号', + phone: '010-12345678', + bank: '中国银行北京分行', + account: '6228888888888888', + updateTime: '2025-06-17 10:20:00', + voided: false, + qualificationCertificate: 'https://example.com/cert1.pdf', + }, + { + id: '2', + taxpayerType: '小规模纳税人', + taxpayerCode: '91345678901234566Y', + head: '上海某信息技术有限公司', + address: '上海市浦东新区XX大厦8楼', + phone: '021-87654321', + bank: '工商银行上海分行', + account: '6229999999999999', + updateTime: '2025-06-16 15:30:00', + voided: true, + qualificationCertificate: '', + }, +] +const mockQualificationData = [ + { + id: '1', + certificateType: '建筑业企业资质证书', + name: '建筑工程施工总承包一级', + code: 'ZJ-A123456', + typeLevel: '一级', + authority: '住房和城乡建设部', + dateTime: '2023-03-01', + termOfValidity: '2028-03-01', + updateTime: '2025-06-17 10:30:00', + }, + { + id: '2', + certificateType: '安全生产许可证', + name: '施工企业安全生产许可证', + code: 'AQ-789012', + typeLevel: 'A级', + authority: '应急管理部', + dateTime: '2022-06-15', + termOfValidity: '2025-06-15', + updateTime: '2025-06-17 11:45:00', + }, +] +export const mockData = { + base: { + "id": "123456", + "supplierType": "dvs", + "licenceAccessory": "https://example.com/license.pdf", + "licenceDate": "2025-12-31", + "enterpriseType": "company", + "name": "深圳供应商有限公司", + "nameEn": "Shenzhen Supplier Co., Ltd.", + "socialCreditCode": "91440300MA5F3XXXXQ", + "range": "电子元器件、金属材料销售", + "regAddress": "广东省深圳市南山区科技园", + "workAddress": "广东省深圳市南山区软件产业基地", + "parentCompanyInvestor": "深圳控股集团有限公司", + "legalPerson": "李四", + "idCard": "440301199001015678", + "capital": 5000, + "contactsName": "王五", + "contactsPhone": "13800138000", + "contactsType": "法人代表", + "contactsEmail": "contact@supplier.com", + "telephone": "0755-12345678", + "nation": "新加坡", + "vat": "SG12345678VAT", + "taxpayerId": "SG-TAX-998877", + "currency": "SGD", + "personName": "张三", + "personPhone": "13812345678", + "personBank": "中国银行深圳分行", + "personAccount": "6222020200123456789", + "remark": "该供应商已完成初步审核", + "accessStatus": 1, + "blacklistStatus": 0, + "greylistStatus": 1, + "fillinStatus": 0, + "fillinBy": "", + "sapCode": "SAP998877", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }, + qualifications: [{ + "id": "cert-001", + "supplierId": "supplier-123456", + "certificateType": "安全生产许可证", + "name": "建筑施工总承包一级资质", + "code": "ZJ20230605001", + "typeLevel": "一级", + "authority": "住房和城乡建设部", + "dateTime": "2023-06-05", + "termOfValidity": "2026-06-05", + "accessory": "https://example.com/certificate.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2023-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2024-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }], + invoice: { + "id": "invoice-001", + "supplierId": "supplier-123456", + "taxpayerType": "general", + "taxpayerCode": "91440300MA5F3XXXXQ", + "phone": "0755-12345678", + "account": "6222020200123456789", + "head": "深圳供应商有限公司", + "address": "深圳市南山区科技园开票楼101号", + "bank": "中国银行深圳分行", + "qualificationCertificate": "https://example.com/tax-cert.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }, + bank: [{ + "id": "bank-001", + "supplierId": "supplier-123456", + "interbankNumber": "123456789012", + "bank": "中国银行深圳分行", + "swiftCode": "BKCHCNBJ45A", + "accountName": "Shenzhen Supplier Co., Ltd.", + "account": "6222020200123456789", + "currency": "CNY", + "nation": "中国", + "province": "广东省", + "city": "深圳市", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }], + survey: { + "supplierName": "深圳供应商有限公司", + "name": "李四", + "position": "采购经理", + "phone": "13800138000", + "email": "lisi@supplier.com", + "dateTime": "2025-06-17", + }, + questionReply: [ + { + "surveyQuestion": "法律法规:\n我们确保经营和提供的产品服务遵守国家及 各业务所在地的所有使用法律、法规", + "replyValue": "是", + },{ + "surveyQuestion": "健康和安全:\n我们为员工提供符合法律法规的安全且健康 的工作场所。我们建立安全管理体系,并向 员工传达工作场所或生活设施的健康和安全 标准,致力于减少工作对员工造成的伤害和 疾病。", + "replyValue": "符合", + },{ + "surveyQuestion": "环境:\n我们能够以环境友好的方式经营。我们遵守 适用的环境法律、法规和标准;并建立有效 的环境管理体系。\n我们遵守贵集团对相关产品或服务的部分附 加环境要求,这些要求和规定体现在设计与 产品规范的合同文档中。", + "replyValue": "符合", + },{ + "surveyQuestion": "监督和记录:\n我们保留记录遵守相关法律和此行为准则的必要文件,并根据要求为贵集团提供对文件的查看权。我们会允许贵集团在适当的时候,以验证行为准则执行为目的的现场勘查", + "replyValue": "符合", + } + ], + attachments: { + "attachmentsType": "commitment", + "fileName": "anti-bribery-commitment.pdf", + "fileType": "pdf", + "fileSize": "204800", + "filePath": "/data/files/anti-bribery-commitment.pdf", + "fileUrl": "http://example.com/files/anti-bribery-commitment.pdf", + } +}; +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + // 供应商信息 + 'GET /api/system/coscoSupplier': (req: Request, res: Response) => { + res.json({ + code: 200, + data: mockData, + msg: '操作成功' + }); + }, + // + 'GET /api/system/qualifications': (req: Request, res: Response) => { + res.json({ code: 200, + data: mockQualificationData, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/invoice': (req: Request, res: Response) => { + res.json({ code: 200, + data: dataInvoiceInfo, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/bank': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + id: '1', + interbankNumber: '123456789', + bank: '中国银行', + accountName: '张三', + account: '6228480000000000000', + currency: '人民币', + nation: '中国', + province: '广东省', + city: '广州市', + updateTime: '2024-06-18', + }, + { + id: '2', + interbankNumber: '987654321', + bank: '工商银行', + accountName: '李四', + account: '6228480000000000001', + currency: '美元', + nation: '中国', + province: '江苏省', + city: '南京市', + updateTime: '2024-06-17', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/tianyancha': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + key: '1', + base: '京', + name: '北京科技有限公司', + legalPersonName: '张三', + legalPersonType: '1', + regNumber: '110108123456789', + industry: '信息技术', + companyOrgType: '有限责任公司', + regLocation: '北京市海淀区中关村', + estiblishTime: '2010-06-15', + fromTime: '2010-06-16', + toTime: '2025-06-15', + businessScope: '软件开发、技术咨询', + approvedTime: '2010-06-10', + regStatus: '存续', + regCapital: '5000万元', + regInstitute: '北京市工商局', + orgNumber: '1234567890', + creditCode: '91110108MA01A12345', + property3: 'Beijing Tech Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1001', + taxNumber: '110108123456789', + email: 'contact@bjtech.com', + website: 'http://www.bjtech.com', + phoneNumber: '010-12345678', + lastUpdateTime: '2025-06-15 10:00:00', + }, + { + key: '2', + base: '沪', + name: '上海电子商务有限公司', + legalPersonName: '李四', + legalPersonType: '1', + regNumber: '310101987654321', + industry: '电子商务', + companyOrgType: '股份有限公司', + regLocation: '上海市浦东新区', + estiblishTime: '2015-03-20', + fromTime: '2015-03-21', + toTime: '2030-03-20', + businessScope: '电子商务平台运营、广告设计', + approvedTime: '2015-03-15', + regStatus: '存续', + regCapital: '1亿元', + regInstitute: '上海市工商局', + orgNumber: '0987654321', + creditCode: '91310101MA1AB23456', + property3: 'Shanghai E-commerce Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1002', + taxNumber: '310101987654321', + email: 'info@shcommerce.com', + website: 'http://www.shcommerce.com', + phoneNumber: '021-87654321', + lastUpdateTime: '2025-06-15 09:30:00', + }, + ], + total: 2, + msg: '操作成功' }); + }, + + 'GET /api/500': (req: Request, res: Response) => { + res.status(500).send({ + timestamp: 1513932555104, + status: 500, + error: 'error', + message: 'error', + path: '/base/category/list', + }); + }, + + +}; diff --git a/src/pages/supplier/backend/workbenches/components/ChangePassword.tsx b/src/pages/supplier/backend/workbenches/components/ChangePassword.tsx new file mode 100644 index 0000000..dfde55d --- /dev/null +++ b/src/pages/supplier/backend/workbenches/components/ChangePassword.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { Form, Input, Button, message } from 'antd'; +import { useIntl } from 'umi'; + +const ChangePassword: React.FC = () => { + const intl = useIntl(); + const [form] = Form.useForm(); + + const handleSubmit = async (values: any) => { + if (values.newPassword !== values.confirmPassword) { + message.error(intl.formatMessage({ id: 'page.changePassword.error.passwordMismatch' })); + return; + } + // 模拟提交 + console.log('修改密码请求参数:', values); + message.success(intl.formatMessage({ id: 'page.changePassword.success' })); + form.resetFields(); + }; + + return ( +
+
+ + + + + + + + + + + + +
+ ); +}; + +export default ChangePassword; \ No newline at end of file diff --git a/src/pages/supplier/backend/workbenches/components/CustomTable.less b/src/pages/supplier/backend/workbenches/components/CustomTable.less new file mode 100644 index 0000000..23b3fc3 --- /dev/null +++ b/src/pages/supplier/backend/workbenches/components/CustomTable.less @@ -0,0 +1,21 @@ +.custom-table { + // 表头样式 + .ant-table-thead > tr > th { + background-color: #f2f6fc; // 表头背景色 + color: #94989e; // 表头字体颜色 + border-right: none; // 去掉竖线 + } + + // 表体样式,去除竖线 + .ant-table-tbody > tr > td { + color: #262626; + border-right: none; + } + + // 可选:去除最右边列的边框(避免因 border-collapse 出现残留) + + .ant-table-tbody > tr > td:last-child { + border-right: none; + } + } + \ No newline at end of file diff --git a/src/pages/supplier/backend/workbenches/components/PersonalInfo.tsx b/src/pages/supplier/backend/workbenches/components/PersonalInfo.tsx new file mode 100644 index 0000000..e2db0c3 --- /dev/null +++ b/src/pages/supplier/backend/workbenches/components/PersonalInfo.tsx @@ -0,0 +1,36 @@ +import React, { useEffect, useState } from 'react'; +import { Descriptions } from 'antd'; +import { coscoSupplier } from '../services' +import { useIntl } from 'umi'; + +const PersonalInfo: React.FC = () => { + const intl = useIntl(); + const [registerInfo, setRegisterInfo] = useState({ base: {} }); + + const fetchData = async () => { + const { code, data } = await coscoSupplier({}); + if (code === 200) { + setRegisterInfo(data); + } + }; + // 个人信息 + useEffect(() => { + fetchData() + }, []); + + return ( +
+ + {registerInfo.base.name} + {registerInfo.base.gender} + {registerInfo.base.phone} + {registerInfo.base.email} + {registerInfo.base.department} + {registerInfo.base.position} + {registerInfo.base.entryDate} + +
+ ); +}; + +export default PersonalInfo; \ No newline at end of file diff --git a/src/pages/supplier/backend/workbenches/index.tsx b/src/pages/supplier/backend/workbenches/index.tsx new file mode 100644 index 0000000..1a85799 --- /dev/null +++ b/src/pages/supplier/backend/workbenches/index.tsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react'; +import { Tabs, Row, Col } from 'antd'; +import CompanyInfo from '@/components/CompanyInfo'; +import PersonalInfo from './components/PersonalInfo'; +import ChangePassword from './components/ChangePassword'; +const { TabPane } = Tabs; + +const Workbench: React.FC = () => { + const [activeTab, setActiveTab] = useState('company'); + + return ( +
+
+

工作台

+
+ + +
+ + + + + + + + + + + + + + + ); +}; + +export default Workbench; diff --git a/src/pages/supplier/backend/workbenches/services.ts b/src/pages/supplier/backend/workbenches/services.ts new file mode 100644 index 0000000..c0313b1 --- /dev/null +++ b/src/pages/supplier/backend/workbenches/services.ts @@ -0,0 +1,38 @@ +import request from '@/utils/request'; + + +export async function coscoSupplier(params:any) { + console.log(params,'params'); + + return request('/api/system/coscoSupplier', { + method: 'GET', + params + }); +} + +export async function library(params:any) { + return request('/api/system/library', { + method: 'GET', + params + }); +} + +export async function qualifications(params:any) { + return request('/api/system/qualifications', { + method: 'GET', + params + }); +} +export async function invoice(params:any) { + return request('/api/system/invoice', { + method: 'GET', + params + }); +} +export async function bank(params:any) { + return request('/api/system/bank', { + method: 'GET', + params + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/category/CategoryLibraryManage/_mock.ts b/src/pages/supplier/category/CategoryLibraryManage/_mock.ts new file mode 100644 index 0000000..86f883d --- /dev/null +++ b/src/pages/supplier/category/CategoryLibraryManage/_mock.ts @@ -0,0 +1,35 @@ +import { Request, Response } from 'express'; +const categoryData = { "code": 200, "success": true, "message": "success", "data": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:22:16", "updateBy": null, "updateTime": null, "remark": null, "id": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "categoryName": "添加", "parentId": "0", "type": "0", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:16", "updateBy": "1", "updateTime": "2025-06-16 10:37:30", "remark": null, "id": "0c653095-a553-48a7-9c2f-6fc708769dd6", "categoryName": "二级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "0", "orderBy": "3", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:59", "updateBy": null, "updateTime": null, "remark": null, "id": "ff59e929-864e-4080-a9c5-852cab7ab129", "categoryName": "一级商品", "parentId": "0c653095-a553-48a7-9c2f-6fc708769dd6", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6,ff59e929-864e-4080-a9c5-852cab7ab129", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }, { "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:29:09", "updateBy": null, "updateTime": null, "remark": null, "id": "ffd210c8-026b-4ec5-aa1e-40d70feb905e", "categoryName": "三级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,ffd210c8-026b-4ec5-aa1e-40d70feb905e", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }] } +export default { + + 'GET /api/system/category': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { + id: '1', + deptName: '信息技术部', + categoryName: '软件开发', + createTime: '2024-01-15', + exitTime: '2024-06-30', + exitReason: '项目结束', + }, + { + id: '2', + deptName: '市场部', + categoryName: '市场推广', + createTime: '2024-02-20', + exitTime: '2024-07-15', + exitReason: '岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/categoryOption': (req: Request, res: Response) => { + res.json(categoryData); + }, + +}; diff --git a/src/pages/supplier/category/CategoryLibraryManage/index.tsx b/src/pages/supplier/category/CategoryLibraryManage/index.tsx new file mode 100644 index 0000000..50aeb18 --- /dev/null +++ b/src/pages/supplier/category/CategoryLibraryManage/index.tsx @@ -0,0 +1,214 @@ +import React, { useEffect, useState } from "react"; +// antd组件 +import { Form, Button, Table, Select, Input, Space, DatePicker, message } from "antd"; +import { SearchOutlined, ReloadOutlined } from "@ant-design/icons"; +// umi相关 +import { connect } from 'umi'; + +import { mockList } from './services' +// 下拉数据接口 +type OptionType = { label: string; value: string }; + +// 表格数据接口 +interface Data { + id: number; + name: string; + category: string; + region: string; + creator: string; + validDate: string; + applyDate: string; + processStatus: string; + result: string; +} + + + +// 流程状态、审批结果 下拉选项 +const processOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "未开始", value: "未开始" }, + { label: "进行中", value: "进行中" }, + { label: "已结束", value: "已结束" }, +]; +const resultOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "通过", value: "通过" }, + { label: "未通过", value: "未通过" }, +]; + +const CategoryLibraryManage: React.FC = () => { + // 搜索表单 + const [form] = Form.useForm(); + // 列表数据 + const [data, setData] = useState([]); + // 加载 + const [loading, setLoading] = useState(false); + // 分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 188 }); + + // 查询 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 列表查询方法 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, total, msg } = await mockList({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg); + } + } finally { + setLoading(false); + } + }; + + // 初始化 + useEffect(() => { + getList(); + }, []); + + // 表格字段 + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + align: 'center', + width: 60, + render: (_: any, __: any, idx: number) => idx + 1, + }, + { + title: '品类库名称', + dataIndex: 'name', + key: 'name', + align: 'center', + }, + { + title: '品类', + dataIndex: 'category', + key: 'category', + align: 'center', + }, + { + title: '区域', + dataIndex: 'region', + key: 'region', + align: 'center', + }, + { + title: '创建单位', + dataIndex: 'creator', + key: 'creator', + align: 'center', + }, + { + title: '有效期至', + dataIndex: 'validDate', + key: 'validDate', + align: 'center', + }, + { + title: '申请时间', + dataIndex: 'applyDate', + key: 'applyDate', + align: 'center', + }, + { + title: '流程状态', + dataIndex: 'processStatus', + key: 'processStatus', + align: 'center', + }, + { + title: '审批结果', + dataIndex: 'result', + key: 'result', + align: 'center', + }, + { + title: '操作', + key: 'option', + align: 'center', + render: (record: any) => ( + + { /* 审批记录弹窗 */ }}>审批记录 + + ), + }, + ]; + + return ( + <> + {/* 查询栏 */} +
+ + + + + + + + + + + + + + + + + + + + + {/* 表格 */} +
getList(pg.current!, pg.pageSize!)} + bordered + /> + + ); +}; + +export default connect()(CategoryLibraryManage); diff --git a/src/pages/supplier/category/CategoryLibraryManage/services.ts b/src/pages/supplier/category/CategoryLibraryManage/services.ts new file mode 100644 index 0000000..f51b4f1 --- /dev/null +++ b/src/pages/supplier/category/CategoryLibraryManage/services.ts @@ -0,0 +1,16 @@ +import request from '@/utils/request'; + + +export async function mockList(params:any) { + return request('/api/system/mockList', { + method: 'GET', + params + }); +} + +export async function categoryOption() { + return request('/api/system/categoryOption', { + method: 'GET' + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/category/CategoryLibraryReview/index.tsx b/src/pages/supplier/category/CategoryLibraryReview/index.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/supplier/category/CategoryManage/_mock.ts b/src/pages/supplier/category/CategoryManage/_mock.ts new file mode 100644 index 0000000..3eb2175 --- /dev/null +++ b/src/pages/supplier/category/CategoryManage/_mock.ts @@ -0,0 +1,20 @@ +import { Request, Response } from 'express'; + +export default { + 'GET /api/system/getCategoryStoreList': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { id: 1, storeName: '境外燃油备件库', category: '燃油', region: '新加坡', unit: '集团', owner: '张三', validTo: '2028-03-31', supplierCount: 3 }, + { id: 2, storeName: '境外燃油备件库', category: '燃油', region: '香港', unit: '集团', owner: '李四', validTo: '2028-03-31', supplierCount: 12 }, + { id: 3, storeName: '润滑油库', category: '润滑油', region: '-', unit: '集团', owner: '张三', validTo: '2028-03-31', supplierCount: 10 }, + { id: 4, storeName: '船用物料库', category: '船用物料', region: '-', unit: '集团', owner: '李四', validTo: '2028-03-31', supplierCount: 5 }, + ], + total: 4, + msg: '操作成功' + }); + }, + +}; + + diff --git a/src/pages/supplier/category/CategoryManage/components/CategoryAddModal.tsx b/src/pages/supplier/category/CategoryManage/components/CategoryAddModal.tsx new file mode 100644 index 0000000..f760ae5 --- /dev/null +++ b/src/pages/supplier/category/CategoryManage/components/CategoryAddModal.tsx @@ -0,0 +1,167 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, Form, Input, DatePicker, Button, Tree, Select, message } from 'antd'; + +const treeData = [ + { + title: '货物', + key: 'goods', + children: [ + { title: '燃油', key: 'fuel' }, + { title: '润滑油', key: 'oil' }, + { title: '船用物料', key: 'marine' }, + ], + }, + { + title: '工程', + key: 'project', + children: [ + { title: '土建', key: 'civil' }, + { title: '机电', key: 'me' }, + ], + }, + { + title: '服务', + key: 'service', + children: [ + { title: '运输', key: 'transport' }, + { title: '维修', key: 'maintenance' }, + ], + }, +]; + +const regionOptions = [ + { label: '全球', value: 'global' }, + { label: '新加坡', value: 'singapore' }, + { label: '香港', value: 'hongkong' }, +]; + +interface Props { + visible: boolean; + onCancel: () => void; + onSuccess?: () => void; +} +const CategoryAddModal: React.FC = ({ visible, onCancel, onSuccess }) => { + const [form] = Form.useForm(); + // 受控 checkedKeys + const [checkedKeys, setCheckedKeys] = useState([]); + // 切换“选择品类”时,自动清除“区域选择” + useEffect(() => { + if (!visible) { + setCheckedKeys([]); + form.resetFields(); + } + }, [visible]); + // 只能同一级单选 + const handleTreeCheck = (checkedKeysValue: any, info: any) => { + console.log(checkedKeysValue); + + let checked = Array.isArray(checkedKeysValue) ? checkedKeysValue : checkedKeysValue.checked; + // 记录每个一级key的选中(只保留同级最后一次点击) + const map: Record = {}; + checked.forEach(k => { + const parent = treeData.find(node => + node.key === k || node.children?.some(c => c.key === k) + ); + if (parent) map[parent.key] = k; + }); + const onlyOneEachLevel = Object.values(map); + console.log(onlyOneEachLevel,'onlyOneEachLevel'); + + + setCheckedKeys(onlyOneEachLevel); // 实时刷新 + form.setFieldsValue({ categoryKeys: onlyOneEachLevel }); + }; + + // 提交校验 + const handleOk = async () => { + try { + await form.validateFields(); + message.success('提交成功(模拟)'); + onSuccess && onSuccess(); + onCancel(); + form.resetFields(); + } catch { } + }; + + return ( + +
+ 品类库名称} + name="storeName" + rules={[{ required: true, message: '请输入品类库名称' }]} + > + + + 有效期至} + name="validTo" + rules={[{ required: true, message: '请选择有效期' }]} + > + + + 负责部门} + name="ownerDept" + rules={[{ required: true, message: '请输入品类库负责人' }]} + > + + + 选择品类 } + name="categoryKeys" + required + rules={[ { required: true, message: '请选择品类' }, + ]} + > + + + (注:同一级品类不可多选) + + + 区域选择(仅燃油品)} + name="region" + rules={[{ required: true, message: '请选择区域' }]} + required + > +
+ + ); +}; + +export default SupplierAddModal; diff --git a/src/pages/supplier/category/CategoryManage/components/SupplierListModal.tsx b/src/pages/supplier/category/CategoryManage/components/SupplierListModal.tsx new file mode 100644 index 0000000..fc5c278 --- /dev/null +++ b/src/pages/supplier/category/CategoryManage/components/SupplierListModal.tsx @@ -0,0 +1,105 @@ +import React, { useState } from 'react'; +import { Modal, Table, Button, Checkbox, Popconfirm, message, Descriptions } from 'antd'; + +// 示例供应商数据 +const mockSuppliers = [ + { + id: 1, + name: '中山市合创展包装材料有限公司', + region: '境内', + creditCode: '910000000000000000', + type: '中央企业', + }, + { + id: 2, + name: '深圳市欧阳华斯电源有限公司', + region: '境内', + creditCode: '910000000000000000', + type: '地方国有企业', + }, + { + id: 3, + name: '广东振兴塑胶机械有限公司', + region: '境内', + creditCode: '910000000000000000', + type: '民营企业', + }, +]; + +const SupplierListModal = ({ visible, onCancel, onOk, categoryInfo }) => { + // 供应商数据(受控) + const [suppliers, setSuppliers] = useState(mockSuppliers); + + + + // 列表列 + const columns = [ + { title: '序号', dataIndex: 'id', align: 'center', width: 60, render: (t, r, i) => i + 1 }, + { title: '供应商名称', dataIndex: 'name', align: 'center' }, + { title: '境内/境外', dataIndex: 'region', align: 'center' }, + { title: '统一社会信用代码', dataIndex: 'creditCode', align: 'center' }, + { title: '供应商分类', dataIndex: 'type', align: 'center' }, + + ]; + + return ( + 关闭, + ]} + width={820} + bodyStyle={{ padding: 24 }} + destroyOnClose + > + {/* 基本信息区 */} +
+ + + {categoryInfo?.name || '燃油'} + + + {categoryInfo?.validDate || '2028-03-31'} + + + {categoryInfo?.manager || '李四'} + + + {categoryInfo?.structure || '货物 燃料'} + + +
+ + + {/* 供应商表格 */} +
+ 选择供应商 + +
+
+ + ); +}; + +export default SupplierListModal; diff --git a/src/pages/supplier/category/CategoryManage/index.tsx b/src/pages/supplier/category/CategoryManage/index.tsx new file mode 100644 index 0000000..4aca262 --- /dev/null +++ b/src/pages/supplier/category/CategoryManage/index.tsx @@ -0,0 +1,237 @@ +import React, { useEffect, useState } from "react"; +// antd 组件 +import { Form, Button, Table, Select, Input, Space, message } from 'antd'; +import { SearchOutlined, ReloadOutlined, PlusOutlined } from '@ant-design/icons'; +// 类型定义 +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +// umi 相关 +import { connect } from 'umi'; +// 弹窗组件 +import CategoryAddModal from './components/CategoryAddModal'; +import SupplierAddModal from './components/SupplierAddModal'; +import SupplierListModal from './components/SupplierListModal'; +// mock服务 +import { getCategoryStoreList } from './services'; + +const { Option } = Select; + +// 列表数据接口 +interface Data { + id: number; + storeName: string; + category: string; + region: string; + unit: string; + owner: string; + validTo: string; + supplierCount: number; +} + +const CategoryManage: React.FC = () => { + // 搜索表单 + const [form] = Form.useForm(); + // 列表数据 + const [data, setData] = useState([]); + // 列表加载 + const [loading, setLoading] = useState(false); + // 分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 188 }); + + // 新增弹窗 + const [addVisible, setAddVisible] = useState(false); + // 添加供应商弹窗 + const [addSupplierVisible, setAddSupplierVisible] = useState(false); + const [currentStoreId, setCurrentStoreId] = useState(null); + // 库内供应商数量弹窗 + const [supplierListVisible, setSupplierListVisible] = useState(false); + + // 获取列表 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + // mock 请求 + const { code, data, total } = await getCategoryStoreList({ page, pageSize, ...form.getFieldsValue() }); + if (code === 200) { + setData(data); + setPagination({ ...pagination, current: page, pageSize, total }); + } + } finally { + setLoading(false); + } + }; + + // 查询 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(1, pagination.pageSize || 10); + }; + + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(1, pagination.pageSize || 10); + }; + + // 初始化 + useEffect(() => { + getList(); + }, []); + + // 列表表头 + const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + align: 'center', + width: 60, + render: (_: any, __: any, idx: number) => ((pagination.current! - 1) * (pagination.pageSize || 10)) + idx + 1, + }, + { + title: '品类库名称', + dataIndex: 'storeName', + key: 'storeName', + align: 'center', + }, + { + title: '品类', + dataIndex: 'category', + key: 'category', + align: 'center', + }, + { + title: '区域', + dataIndex: 'region', + key: 'region', + align: 'center', + }, + { + title: '创建单位', + dataIndex: 'unit', + key: 'unit', + align: 'center', + }, + { + title: '负责人', + dataIndex: 'owner', + key: 'owner', + align: 'center', + }, + { + title: '有效期至', + dataIndex: 'validTo', + key: 'validTo', + align: 'center', + }, + { + title: '库内供应商数量', + dataIndex: 'supplierCount', + key: 'supplierCount', + align: 'center', + render: (val: number, record) => ( + { + setCurrentStoreId(record.id); + setSupplierListVisible(true); + }} + > + {val} + + ), + }, + { + title: '操作', + key: 'option', + align: 'center', + render: (record: any) => ( + { + setCurrentStoreId(record.id); + setAddSupplierVisible(true); + }} + > + 添加供应商 + + ), + }, + ]; + + return ( + <> + {/* 查询区 */} + + + + + + + + + + + + + + + + {/* 表格 */} +
getList(page, pageSize), + }} + scroll={{ x: 'max-content' }} + /> + + {/* 新增品类库弹窗 */} + setAddVisible(false)} + onSuccess={() => { + setAddVisible(false); + getList(); + }} + /> + {/* 添加供应商弹窗 */} + setAddSupplierVisible(false)} + onSuccess={() => { + setAddSupplierVisible(false); + getList(); + }} + /> + {/* 库内供应商数量弹窗 */} + setSupplierListVisible(false)} + /> + + ); +}; + +export default connect()(CategoryManage); diff --git a/src/pages/supplier/category/CategoryManage/services.ts b/src/pages/supplier/category/CategoryManage/services.ts new file mode 100644 index 0000000..952a4d1 --- /dev/null +++ b/src/pages/supplier/category/CategoryManage/services.ts @@ -0,0 +1,11 @@ +import request from '@/utils/request'; + + + +export async function getCategoryStoreList(params:any) { + return request('/api/system/getCategoryStoreList', { + method: 'GET', + params + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/category/SupplierEntryManage/_mock.ts b/src/pages/supplier/category/SupplierEntryManage/_mock.ts new file mode 100644 index 0000000..86f883d --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryManage/_mock.ts @@ -0,0 +1,35 @@ +import { Request, Response } from 'express'; +const categoryData = { "code": 200, "success": true, "message": "success", "data": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:22:16", "updateBy": null, "updateTime": null, "remark": null, "id": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "categoryName": "添加", "parentId": "0", "type": "0", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:16", "updateBy": "1", "updateTime": "2025-06-16 10:37:30", "remark": null, "id": "0c653095-a553-48a7-9c2f-6fc708769dd6", "categoryName": "二级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "0", "orderBy": "3", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:59", "updateBy": null, "updateTime": null, "remark": null, "id": "ff59e929-864e-4080-a9c5-852cab7ab129", "categoryName": "一级商品", "parentId": "0c653095-a553-48a7-9c2f-6fc708769dd6", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6,ff59e929-864e-4080-a9c5-852cab7ab129", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }, { "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:29:09", "updateBy": null, "updateTime": null, "remark": null, "id": "ffd210c8-026b-4ec5-aa1e-40d70feb905e", "categoryName": "三级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,ffd210c8-026b-4ec5-aa1e-40d70feb905e", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }] } +export default { + + 'GET /api/system/category': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { + id: '1', + deptName: '信息技术部', + categoryName: '软件开发', + createTime: '2024-01-15', + exitTime: '2024-06-30', + exitReason: '项目结束', + }, + { + id: '2', + deptName: '市场部', + categoryName: '市场推广', + createTime: '2024-02-20', + exitTime: '2024-07-15', + exitReason: '岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/categoryOption': (req: Request, res: Response) => { + res.json(categoryData); + }, + +}; diff --git a/src/pages/supplier/category/SupplierEntryManage/components/ApproveRecordModal.tsx b/src/pages/supplier/category/SupplierEntryManage/components/ApproveRecordModal.tsx new file mode 100644 index 0000000..d23edbd --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryManage/components/ApproveRecordModal.tsx @@ -0,0 +1,140 @@ +import React, { useEffect, useState } from "react"; +import { Modal, Table, Descriptions } from "antd"; + +// 审批记录数据类型 +interface ApproveRecord { + id: number; + step: string; // 流程节点 + approver: string; // 审批人 + status: string; // 审批状态 + remark: string; // 审批意见 + time: string; // 审批时间 +} + +// mock 获取审批记录接口 +const mockFetchApproveRecords = async (entryId: number | string) => { + return Promise.resolve({ + code: 200, + data: [ + { + id: 1, + step: "申请提交", + approver: "张三", + status: "提交", + remark: "请审批", + time: "2025-03-03 14:20", + }, + { + id: 2, + step: "一级审批", + approver: "李四", + status: "同意", + remark: "同意入库", + time: "2025-03-04 09:20", + }, + { + id: 3, + step: "二级审批", + approver: "王五", + status: "驳回", + remark: "资料不全,请补充", + time: "2025-03-05 11:45", + }, + ], + msg: "success" + }); +}; + +interface ApproveRecordModalProps { + visible: boolean; + entryId?: number | string; + onCancel: () => void; +} + +const ApproveRecordModal: React.FC = ({ + visible, + entryId, + onCancel, +}) => { + const [loading, setLoading] = useState(false); + const [records, setRecords] = useState([]); + // 供应商数据(受控) + const [suppliers, setSuppliers] = useState([]); + useEffect(() => { + if (visible && entryId) { + setLoading(true); + mockFetchApproveRecords(entryId).then(res => { + if (res.code === 200) setRecords(res.data); + }).finally(() => setLoading(false)); + } + if (!visible) setRecords([]); + }, [visible, entryId]); + + const columns = [ + { title: '流程节点', dataIndex: 'step', align: 'center' }, + { title: '审批人', dataIndex: 'approver', align: 'center' }, + { title: '审批状态', dataIndex: 'status', align: 'center' }, + { title: '审批意见', dataIndex: 'remark', align: 'center' }, + { title: '审批时间', dataIndex: 'time', align: 'center' }, + ]; + + return ( + +
+ + {/* + {categoryInfo?.name || '燃油'} + + + {categoryInfo?.validDate || '2028-03-31'} + + + {categoryInfo?.manager || '李四'} + + + {categoryInfo?.structure || '货物 燃料'} + */} + +
+ + + {/* 供应商表格 */} +
+ 选择供应商 + +
+
+ + ); +}; + +export default ApproveRecordModal; + + diff --git a/src/pages/supplier/category/SupplierEntryManage/index.tsx b/src/pages/supplier/category/SupplierEntryManage/index.tsx new file mode 100644 index 0000000..5199346 --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryManage/index.tsx @@ -0,0 +1,325 @@ +import React, { useEffect, useState } from "react"; +import { Form, Button, Table, Select, Input, Space, DatePicker, message } from "antd"; +import { SearchOutlined, ReloadOutlined } from "@ant-design/icons"; +import { connect } from 'umi'; + +import ApproveRecordModal from './components/ApproveRecordModal' + +// 下拉选项类型 +type OptionType = { label: string; value: string }; + +// 列表数据接口 +interface Data { + id: number; + categoryLib: string; + region: string; + supplier: string; + applier: string; + applyTime: string; + processStatus: string; + result: string; +} + +// mock接口 +const mockList = async (params?: any) => { + return Promise.resolve({ + code: 200, + data: [ + { + id: 1, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "未开始", + result: "", + }, + { + id: 2, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "未开始", + result: "", + }, + { + id: 3, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 4, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 5, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 6, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 7, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "通过", + }, + { + id: 8, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "驳回", + }, + { + id: 9, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "驳回", + }, + { + id: 10, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "驳回", + }, + ], + total: 188, + msg: "success", + }); +}; + +const processOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "未开始", value: "未开始" }, + { label: "进行中", value: "进行中" }, + { label: "已结束", value: "已结束" }, +]; +const resultOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "通过", value: "通过" }, + { label: "驳回", value: "驳回" }, +]; +const regionOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "新加坡", value: "新加坡" }, + { label: "全球", value: "全球" }, +]; + +const SupplierEntryManage: React.FC = () => { + // 搜索表单 + const [form] = Form.useForm(); + // 列表数据 + const [data, setData] = useState([]); + // 加载 + const [loading, setLoading] = useState(false); + // 分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 188 }); + // 添加供应商弹窗 + const [currentStoreId, setCurrentStoreId] = useState(null); + // 库内供应商数量弹窗 + const [approveRecordVisible, setApproveRecordVisible] = useState(false); + // 查询 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 获取列表 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, total, msg } = await mockList({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg); + } + } finally { + setLoading(false); + } + }; + + useEffect(() => { + getList(); + }, []); + + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + align: 'center', + width: 60, + render: (_: any, __: any, idx: number) => idx + 1, + }, + { + title: '品类库', + dataIndex: 'categoryLib', + key: 'categoryLib', + align: 'center', + }, + { + title: '区域', + dataIndex: 'region', + key: 'region', + align: 'center', + }, + { + title: '入库供应商', + dataIndex: 'supplier', + key: 'supplier', + align: 'center', + }, + { + title: '申请人', + dataIndex: 'applier', + key: 'applier', + align: 'center', + }, + { + title: '申请时间', + dataIndex: 'applyTime', + key: 'applyTime', + align: 'center', + }, + { + title: '流程状态', + dataIndex: 'processStatus', + key: 'processStatus', + align: 'center', + }, + { + title: '审批结果', + dataIndex: 'result', + key: 'result', + align: 'center', + }, + { + title: '操作', + key: 'option', + align: 'center', + render: (record: any) => ( + + { setCurrentStoreId(record.id); setApproveRecordVisible(true); }}>审批记录 + + ), + }, + ]; + + return ( + <> + {/* 查询栏 */} +
+ + + + + + + + + + + + + + + + + + + + + {/* 表格 */} +
getList(pg.current!, pg.pageSize!)} + bordered + /> + setApproveRecordVisible(false)} + /> + + ); +}; + +export default connect()(SupplierEntryManage); diff --git a/src/pages/supplier/category/SupplierEntryManage/services.ts b/src/pages/supplier/category/SupplierEntryManage/services.ts new file mode 100644 index 0000000..f51b4f1 --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryManage/services.ts @@ -0,0 +1,16 @@ +import request from '@/utils/request'; + + +export async function mockList(params:any) { + return request('/api/system/mockList', { + method: 'GET', + params + }); +} + +export async function categoryOption() { + return request('/api/system/categoryOption', { + method: 'GET' + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/category/SupplierEntryReview/_mock.ts b/src/pages/supplier/category/SupplierEntryReview/_mock.ts new file mode 100644 index 0000000..86f883d --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryReview/_mock.ts @@ -0,0 +1,35 @@ +import { Request, Response } from 'express'; +const categoryData = { "code": 200, "success": true, "message": "success", "data": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:22:16", "updateBy": null, "updateTime": null, "remark": null, "id": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "categoryName": "添加", "parentId": "0", "type": "0", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:16", "updateBy": "1", "updateTime": "2025-06-16 10:37:30", "remark": null, "id": "0c653095-a553-48a7-9c2f-6fc708769dd6", "categoryName": "二级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "0", "orderBy": "3", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": [{ "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:32:59", "updateBy": null, "updateTime": null, "remark": null, "id": "ff59e929-864e-4080-a9c5-852cab7ab129", "categoryName": "一级商品", "parentId": "0c653095-a553-48a7-9c2f-6fc708769dd6", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,0c653095-a553-48a7-9c2f-6fc708769dd6,ff59e929-864e-4080-a9c5-852cab7ab129", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }, { "pageNo": null, "pageSize": null, "createBy": "1", "createTime": "2025-06-16 10:29:09", "updateBy": null, "updateTime": null, "remark": null, "id": "ffd210c8-026b-4ec5-aa1e-40d70feb905e", "categoryName": "三级商品", "parentId": "5fb622f6-929c-41c2-be0d-508fecbff3d9", "type": "1", "orderBy": "1", "ancestors": "0,5fb622f6-929c-41c2-be0d-508fecbff3d9,ffd210c8-026b-4ec5-aa1e-40d70feb905e", "delFlag": "normal", "lastUpdateTime": null, "basePageRequest": null, "children": null }] }] } +export default { + + 'GET /api/system/category': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { + id: '1', + deptName: '信息技术部', + categoryName: '软件开发', + createTime: '2024-01-15', + exitTime: '2024-06-30', + exitReason: '项目结束', + }, + { + id: '2', + deptName: '市场部', + categoryName: '市场推广', + createTime: '2024-02-20', + exitTime: '2024-07-15', + exitReason: '岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整岗位调整', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/categoryOption': (req: Request, res: Response) => { + res.json(categoryData); + }, + +}; diff --git a/src/pages/supplier/category/SupplierEntryReview/components/ApproveRecordModal.tsx b/src/pages/supplier/category/SupplierEntryReview/components/ApproveRecordModal.tsx new file mode 100644 index 0000000..d23edbd --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryReview/components/ApproveRecordModal.tsx @@ -0,0 +1,140 @@ +import React, { useEffect, useState } from "react"; +import { Modal, Table, Descriptions } from "antd"; + +// 审批记录数据类型 +interface ApproveRecord { + id: number; + step: string; // 流程节点 + approver: string; // 审批人 + status: string; // 审批状态 + remark: string; // 审批意见 + time: string; // 审批时间 +} + +// mock 获取审批记录接口 +const mockFetchApproveRecords = async (entryId: number | string) => { + return Promise.resolve({ + code: 200, + data: [ + { + id: 1, + step: "申请提交", + approver: "张三", + status: "提交", + remark: "请审批", + time: "2025-03-03 14:20", + }, + { + id: 2, + step: "一级审批", + approver: "李四", + status: "同意", + remark: "同意入库", + time: "2025-03-04 09:20", + }, + { + id: 3, + step: "二级审批", + approver: "王五", + status: "驳回", + remark: "资料不全,请补充", + time: "2025-03-05 11:45", + }, + ], + msg: "success" + }); +}; + +interface ApproveRecordModalProps { + visible: boolean; + entryId?: number | string; + onCancel: () => void; +} + +const ApproveRecordModal: React.FC = ({ + visible, + entryId, + onCancel, +}) => { + const [loading, setLoading] = useState(false); + const [records, setRecords] = useState([]); + // 供应商数据(受控) + const [suppliers, setSuppliers] = useState([]); + useEffect(() => { + if (visible && entryId) { + setLoading(true); + mockFetchApproveRecords(entryId).then(res => { + if (res.code === 200) setRecords(res.data); + }).finally(() => setLoading(false)); + } + if (!visible) setRecords([]); + }, [visible, entryId]); + + const columns = [ + { title: '流程节点', dataIndex: 'step', align: 'center' }, + { title: '审批人', dataIndex: 'approver', align: 'center' }, + { title: '审批状态', dataIndex: 'status', align: 'center' }, + { title: '审批意见', dataIndex: 'remark', align: 'center' }, + { title: '审批时间', dataIndex: 'time', align: 'center' }, + ]; + + return ( + +
+ + {/* + {categoryInfo?.name || '燃油'} + + + {categoryInfo?.validDate || '2028-03-31'} + + + {categoryInfo?.manager || '李四'} + + + {categoryInfo?.structure || '货物 燃料'} + */} + +
+ + + {/* 供应商表格 */} +
+ 选择供应商 + +
+
+ + ); +}; + +export default ApproveRecordModal; + + diff --git a/src/pages/supplier/category/SupplierEntryReview/index.tsx b/src/pages/supplier/category/SupplierEntryReview/index.tsx new file mode 100644 index 0000000..b216125 --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryReview/index.tsx @@ -0,0 +1,325 @@ +import React, { useEffect, useState } from "react"; +import { Form, Button, Table, Select, Input, Space, DatePicker, message } from "antd"; +import { SearchOutlined, ReloadOutlined } from "@ant-design/icons"; +import { connect } from 'umi'; + +import ApproveRecordModal from './components/ApproveRecordModal' + +// 下拉选项类型 +type OptionType = { label: string; value: string }; + +// 列表数据接口 +interface Data { + id: number; + categoryLib: string; + region: string; + supplier: string; + applier: string; + applyTime: string; + processStatus: string; + result: string; +} + +// mock接口 +const mockList = async (params?: any) => { + return Promise.resolve({ + code: 200, + data: [ + { + id: 1, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "未开始", + result: "", + }, + { + id: 2, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "未开始", + result: "", + }, + { + id: 3, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 4, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 5, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 6, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "进行中", + result: "", + }, + { + id: 7, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "通过", + }, + { + id: 8, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "驳回", + }, + { + id: 9, + categoryLib: "燃油库", + region: "新加坡", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "张三", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "驳回", + }, + { + id: 10, + categoryLib: "备件库", + region: "全球", + supplier: "中山市合创展包装材料有限公司等3家公司", + applier: "李四", + applyTime: "2025-03-03 14:20", + processStatus: "已结束", + result: "驳回", + }, + ], + total: 188, + msg: "success", + }); +}; + +const processOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "未开始", value: "未开始" }, + { label: "进行中", value: "进行中" }, + { label: "已结束", value: "已结束" }, +]; +const resultOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "通过", value: "通过" }, + { label: "驳回", value: "驳回" }, +]; +const regionOptions: OptionType[] = [ + { label: "请选择", value: "" }, + { label: "新加坡", value: "新加坡" }, + { label: "全球", value: "全球" }, +]; + +const SupplierEntryReview: React.FC = () => { + // 搜索表单 + const [form] = Form.useForm(); + // 列表数据 + const [data, setData] = useState([]); + // 加载 + const [loading, setLoading] = useState(false); + // 分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 188 }); + // 添加供应商弹窗 + const [currentStoreId, setCurrentStoreId] = useState(null); + // 库内供应商数量弹窗 + const [approveRecordVisible, setApproveRecordVisible] = useState(false); + // 查询 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 获取列表 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, total, msg } = await mockList({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg); + } + } finally { + setLoading(false); + } + }; + + useEffect(() => { + getList(); + }, []); + + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + align: 'center', + width: 60, + render: (_: any, __: any, idx: number) => idx + 1, + }, + { + title: '品类库', + dataIndex: 'categoryLib', + key: 'categoryLib', + align: 'center', + }, + { + title: '区域', + dataIndex: 'region', + key: 'region', + align: 'center', + }, + { + title: '入库供应商', + dataIndex: 'supplier', + key: 'supplier', + align: 'center', + }, + { + title: '申请人', + dataIndex: 'applier', + key: 'applier', + align: 'center', + }, + { + title: '申请时间', + dataIndex: 'applyTime', + key: 'applyTime', + align: 'center', + }, + { + title: '流程状态', + dataIndex: 'processStatus', + key: 'processStatus', + align: 'center', + }, + { + title: '审批结果', + dataIndex: 'result', + key: 'result', + align: 'center', + }, + { + title: '操作', + key: 'option', + align: 'center', + render: (record: any) => ( + + { setCurrentStoreId(record.id); setApproveRecordVisible(true); }}>审批记录 + + ), + }, + ]; + + return ( + <> + {/* 查询栏 */} +
+ + + + + + + + + + + + + + + + + + + + + {/* 表格 */} +
getList(pg.current!, pg.pageSize!)} + bordered + /> + setApproveRecordVisible(false)} + /> + + ); +}; + +export default connect()(SupplierEntryReview); diff --git a/src/pages/supplier/category/SupplierEntryReview/services.ts b/src/pages/supplier/category/SupplierEntryReview/services.ts new file mode 100644 index 0000000..f51b4f1 --- /dev/null +++ b/src/pages/supplier/category/SupplierEntryReview/services.ts @@ -0,0 +1,16 @@ +import request from '@/utils/request'; + + +export async function mockList(params:any) { + return request('/api/system/mockList', { + method: 'GET', + params + }); +} + +export async function categoryOption() { + return request('/api/system/categoryOption', { + method: 'GET' + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/informationManagement/SupplierChangeManage/_mock.ts b/src/pages/supplier/informationManagement/SupplierChangeManage/_mock.ts new file mode 100644 index 0000000..4535259 --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeManage/_mock.ts @@ -0,0 +1,104 @@ +import { Request, Response } from 'express'; + +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + 'GET /api/supplier/getSupplierChangeList': (req: Request, res: Response) => { + res.send({ + code: 200, + msg: 'success', + data: [ + { + id: 1, + name: '中山市合创展包装材料有限公司', + region: '境内', + supplierType: '中央企业', + accessTime: '2025-03-03 09:30', + changeTime: '2025-03-03 09:30', + status: '未开始', + result: '', + }, + ], + total: 10 + }); + }, + 'GET /api/supplier/list': (req: Request, res: Response) => { + res.send({ + code: 200, + msg: 'success', + data: [ + { + id: 1, + name: '中山市合创展包装材料有限公司', + region: '境内', + supplierType: '中央企业', + accessTime: '2025-03-03 09:30', + changeTime: '2025-03-03 09:30', + status: '未开始', + result: '', + }, + ], + total: 10 + }); + }, + 'GET /api/system/getPage': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { + deptName: '供应商名称变更', + categoryName: '2024-05-20 13:22:11', + createTime: '集团采购部', + exitTime: '已通过', + exitReason: '2024-05-21 09:10:31', + }, + { + deptName: '法人代表变更', + categoryName: '2024-05-18 08:30:55', + createTime: '分公司审核部', + exitTime: '审核中', + exitReason: '', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/getSupplierChangeDetail': (req: Request, res: Response) => { + res.json({ + "code": 200, + "msg": "success", + "data": { + baseInfo: [ + { label: '供应商名称', value: 'xxx' }, + { label: '境内/境外', value: '境内' }, + { label: '准入单位', value: 'xxxx' }, + { label: '准入部门', value: '采购部' }, + ], + changeInfo: [ + { label: '供应商名称-变更前', value: 'xxxx' }, + { label: '供应商名称-变更后', value: 'xxxx' }, + ], + "supplierName": "中山市合创展包装材料有限公司", + "accessUnit": "中远海运(青岛)有限公司", + "region": "境内", + "accessDept": "采购部", + "beforeName": "中山市合创展包装材料有限公司", + "afterName": "中山市合创展包装有限公司", + "qualifications": [ + { + "type": "CMMI资质", + "name": "CMMI资质", + "level": "高级", + "number": "546464", + "org": "XX机构", + "issueDate": "2024-09-08", + "validDate": "2028-09-10", + "file": "https://dummyimage.com/40x30/1890ff/fff.png&text=附件" + } + ] + } + } + ); + }, +}; diff --git a/src/pages/supplier/informationManagement/SupplierChangeManage/components/DetailView.tsx b/src/pages/supplier/informationManagement/SupplierChangeManage/components/DetailView.tsx new file mode 100644 index 0000000..14c2dec --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeManage/components/DetailView.tsx @@ -0,0 +1,144 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Table, Spin } from 'antd'; +import { getSupplierChangeDetail } from '../services'; + +interface Qualification { + type: string; + name: string; + level: string; + number: string; + org: string; + issueDate: string; + validDate: string; + file?: string; +} +interface InfoItem { + label: string; + value: string; +} +interface DetailData { + baseInfo: InfoItem[]; + changeInfo: InfoItem[]; + qualifications: Qualification[]; +} +interface DetailViewProps { + visible: boolean; + onClose: () => void; + detailId?: string | number; +} + +const DetailView: React.FC = ({ visible, onClose, detailId }) => { + + const [loading, setLoading] = useState(false); + const [detailData, setDetailData] = useState(null); + + useEffect(() => { + if (visible && detailId) { + setLoading(true); + getSupplierChangeDetail(detailId) + .then(res => { + if (res.code === 200) { + setDetailData(res.data); + } + }) + .finally(() => setLoading(false)); + } + if (!visible) setDetailData(null); + }, [visible, detailId]); + + const columns = [ + { title: '资质证书类型', dataIndex: 'type', width: 120 }, + { title: '资质名称', dataIndex: 'name', width: 120 }, + { title: '资质类别和等级', dataIndex: 'level', width: 120 }, + { title: '资质证书编号', dataIndex: 'number', width: 120 }, + { title: '发证机构', dataIndex: 'org', width: 120 }, + { title: '发证日期', dataIndex: 'issueDate', width: 120 }, + { title: '资质有效期至', dataIndex: 'validDate', width: 120 }, + { + title: '附件', + dataIndex: 'file', + width: 120, + render: (file: string) => + file ? ( + + 附件 + 更多 + + ) : ( + '-' + ), + }, + ]; + + // 把info数组两两合并成一行显示 + function renderInfoTable(infoArr: InfoItem[]) { + const rows = []; + for (let i = 0; i < infoArr.length; i += 2) { + const left = infoArr[i]; + const right = infoArr[i + 1] || { label: '', value: '' }; + rows.push( + + + + + + + ); + } + return ( +
{left.label}{left.value}{right.label}{right.value}
+ {rows} +
+ ); + } + + return ( + + + {detailData && ( +
+ {/* 基本信息 */} + {renderInfoTable(detailData.baseInfo)} + {/* 变更内容 */} +
变更内容:
+ {renderInfoTable(detailData.changeInfo)} + {/* 新增资质 */} +
新增资质1
+ + + )} + + + ); +}; + +const tdStyleTitle: React.CSSProperties = { + background: '#fafafa', + fontWeight: 700, + width: 130, + padding: 8, + border: '1px solid #f0f0f0', +}; +const tdStyle: React.CSSProperties = { + background: '#fff', + padding: 8, + border: '1px solid #f0f0f0', +}; + +export default DetailView; diff --git a/src/pages/supplier/informationManagement/SupplierChangeManage/index.tsx b/src/pages/supplier/informationManagement/SupplierChangeManage/index.tsx new file mode 100644 index 0000000..a4fd5ff --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeManage/index.tsx @@ -0,0 +1,202 @@ +import React, { useEffect, useState } from 'react'; +import { Table, Form, Input, Select, Button, DatePicker, Tag, Space, message } from 'antd'; +import { SearchOutlined, ReloadOutlined } from '@ant-design/icons'; +import DetailView from './components/DetailView'; +import { getSupplierChangeList, list } from './services'; + +const { RangePicker } = DatePicker; + +const statusColorMap = { + '未开始': 'default', + '进行中': 'processing', + '已结束': 'success', +}; + +const resultColorMap = { + '通过': 'success', + '驳回': 'error', +}; + +const regionOptions = [ + { label: '请选择', value: '' }, + { label: '境内', value: '境内' }, + { label: '境外', value: '境外' }, +]; +const statusOptions = [ + { label: '请选择', value: '' }, + { label: '未开始', value: '未开始' }, + { label: '进行中', value: '进行中' }, + { label: '已结束', value: '已结束' }, +]; +const resultOptions = [ + { label: '请选择', value: '' }, + { label: '通过', value: '通过' }, + { label: '驳回', value: '驳回' }, +]; + +const SupplierChangeManage: React.FC = () => { + const [form] = Form.useForm(); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + const [detailVisible, setDetailVisible] = useState(false); + const [currentDetailId, setCurrentDetailId] = useState(null); + + // 获取表格数据 + const fetchData = async (params = {}) => { + setLoading(true); + try { + const { code, data, total, msg } = await list({ + page: pagination.current, + pageSize: pagination.pageSize, + ...params, + }); + if (code === 200) { + setData(data); + setPagination(p => ({ ...p, total: total || 0 })); + } else { + message.error(msg || '获取数据失败'); + } + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchData(); + }, [pagination.current, pagination.pageSize]); + + // 查询 + const handleSearch = () => { + setPagination(p => ({ ...p, current: 1 })); + fetchData(form.getFieldsValue()); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination(p => ({ ...p, current: 1 })); + fetchData(); + }; + + const columns = [ + { + title: '序号', + dataIndex: 'index', + width: 48, + align: 'center', + render: (_: any, __: any, idx: number) => (pagination.current - 1) * pagination.pageSize + idx + 1, + }, + { + title: '供应商名称', + dataIndex: 'name', + align: 'center', + ellipsis: true, + }, + { + title: '境内/境外', + dataIndex: 'region', + align: 'center', + }, + { + title: '供应商分类', + dataIndex: 'supplierType', + align: 'center', + }, + { + title: '准入时间', + dataIndex: 'accessTime', + align: 'center', + }, + { + title: '提交变更时间', + dataIndex: 'changeTime', + align: 'center', + }, + { + title: '流程状态', + dataIndex: 'status', + align: 'center', + render: (val: any) => {val}, + }, + { + title: '审批结果', + dataIndex: 'result', + align: 'center', + render: (val: any) => val ? {val} : null, + }, + { + title: '操作', + key: 'action', + align: 'center', + render: (_: any, record: any) => ( + + ), + }, + ]; + + // 弹窗操作 + const handleDetail = (record: any) => { + setCurrentDetailId(record.id); + setDetailVisible(true); + }; + const handleDetailClose = () => { + setDetailVisible(false); + setCurrentDetailId(null); + }; + + return ( + <> + {/* 查询表单 */} +
+ + + + + + + +
setPagination({ ...pagination, current }), + }} + /> + {/* 审批记录弹窗 */} + + + ); +}; + +export default SupplierChangeManage; diff --git a/src/pages/supplier/informationManagement/SupplierChangeManage/services.ts b/src/pages/supplier/informationManagement/SupplierChangeManage/services.ts new file mode 100644 index 0000000..444cbd5 --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeManage/services.ts @@ -0,0 +1,22 @@ +import request from '@/utils/request'; + + +export async function list(params:any) { + return request('/api/system/list', { + method: 'GET', + params + }); +} +export async function getPage(params:any) { + return request('/api/system/getPage', { + method: 'GET', + params + }); +} + +export async function getSupplierChangeDetail(params:any) { + return request('/api/system/getSupplierChangeDetail', { + method: 'GET', + params + }); +} diff --git a/src/pages/supplier/informationManagement/SupplierChangeReviewManage/_mock.ts b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/_mock.ts new file mode 100644 index 0000000..4535259 --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/_mock.ts @@ -0,0 +1,104 @@ +import { Request, Response } from 'express'; + +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + 'GET /api/supplier/getSupplierChangeList': (req: Request, res: Response) => { + res.send({ + code: 200, + msg: 'success', + data: [ + { + id: 1, + name: '中山市合创展包装材料有限公司', + region: '境内', + supplierType: '中央企业', + accessTime: '2025-03-03 09:30', + changeTime: '2025-03-03 09:30', + status: '未开始', + result: '', + }, + ], + total: 10 + }); + }, + 'GET /api/supplier/list': (req: Request, res: Response) => { + res.send({ + code: 200, + msg: 'success', + data: [ + { + id: 1, + name: '中山市合创展包装材料有限公司', + region: '境内', + supplierType: '中央企业', + accessTime: '2025-03-03 09:30', + changeTime: '2025-03-03 09:30', + status: '未开始', + result: '', + }, + ], + total: 10 + }); + }, + 'GET /api/system/getPage': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { + deptName: '供应商名称变更', + categoryName: '2024-05-20 13:22:11', + createTime: '集团采购部', + exitTime: '已通过', + exitReason: '2024-05-21 09:10:31', + }, + { + deptName: '法人代表变更', + categoryName: '2024-05-18 08:30:55', + createTime: '分公司审核部', + exitTime: '审核中', + exitReason: '', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/getSupplierChangeDetail': (req: Request, res: Response) => { + res.json({ + "code": 200, + "msg": "success", + "data": { + baseInfo: [ + { label: '供应商名称', value: 'xxx' }, + { label: '境内/境外', value: '境内' }, + { label: '准入单位', value: 'xxxx' }, + { label: '准入部门', value: '采购部' }, + ], + changeInfo: [ + { label: '供应商名称-变更前', value: 'xxxx' }, + { label: '供应商名称-变更后', value: 'xxxx' }, + ], + "supplierName": "中山市合创展包装材料有限公司", + "accessUnit": "中远海运(青岛)有限公司", + "region": "境内", + "accessDept": "采购部", + "beforeName": "中山市合创展包装材料有限公司", + "afterName": "中山市合创展包装有限公司", + "qualifications": [ + { + "type": "CMMI资质", + "name": "CMMI资质", + "level": "高级", + "number": "546464", + "org": "XX机构", + "issueDate": "2024-09-08", + "validDate": "2028-09-10", + "file": "https://dummyimage.com/40x30/1890ff/fff.png&text=附件" + } + ] + } + } + ); + }, +}; diff --git a/src/pages/supplier/informationManagement/SupplierChangeReviewManage/components/DetailView.tsx b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/components/DetailView.tsx new file mode 100644 index 0000000..14c2dec --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/components/DetailView.tsx @@ -0,0 +1,144 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Table, Spin } from 'antd'; +import { getSupplierChangeDetail } from '../services'; + +interface Qualification { + type: string; + name: string; + level: string; + number: string; + org: string; + issueDate: string; + validDate: string; + file?: string; +} +interface InfoItem { + label: string; + value: string; +} +interface DetailData { + baseInfo: InfoItem[]; + changeInfo: InfoItem[]; + qualifications: Qualification[]; +} +interface DetailViewProps { + visible: boolean; + onClose: () => void; + detailId?: string | number; +} + +const DetailView: React.FC = ({ visible, onClose, detailId }) => { + + const [loading, setLoading] = useState(false); + const [detailData, setDetailData] = useState(null); + + useEffect(() => { + if (visible && detailId) { + setLoading(true); + getSupplierChangeDetail(detailId) + .then(res => { + if (res.code === 200) { + setDetailData(res.data); + } + }) + .finally(() => setLoading(false)); + } + if (!visible) setDetailData(null); + }, [visible, detailId]); + + const columns = [ + { title: '资质证书类型', dataIndex: 'type', width: 120 }, + { title: '资质名称', dataIndex: 'name', width: 120 }, + { title: '资质类别和等级', dataIndex: 'level', width: 120 }, + { title: '资质证书编号', dataIndex: 'number', width: 120 }, + { title: '发证机构', dataIndex: 'org', width: 120 }, + { title: '发证日期', dataIndex: 'issueDate', width: 120 }, + { title: '资质有效期至', dataIndex: 'validDate', width: 120 }, + { + title: '附件', + dataIndex: 'file', + width: 120, + render: (file: string) => + file ? ( + + 附件 + 更多 + + ) : ( + '-' + ), + }, + ]; + + // 把info数组两两合并成一行显示 + function renderInfoTable(infoArr: InfoItem[]) { + const rows = []; + for (let i = 0; i < infoArr.length; i += 2) { + const left = infoArr[i]; + const right = infoArr[i + 1] || { label: '', value: '' }; + rows.push( + + + + + + + ); + } + return ( +
{left.label}{left.value}{right.label}{right.value}
+ {rows} +
+ ); + } + + return ( + + + {detailData && ( +
+ {/* 基本信息 */} + {renderInfoTable(detailData.baseInfo)} + {/* 变更内容 */} +
变更内容:
+ {renderInfoTable(detailData.changeInfo)} + {/* 新增资质 */} +
新增资质1
+ + + )} + + + ); +}; + +const tdStyleTitle: React.CSSProperties = { + background: '#fafafa', + fontWeight: 700, + width: 130, + padding: 8, + border: '1px solid #f0f0f0', +}; +const tdStyle: React.CSSProperties = { + background: '#fff', + padding: 8, + border: '1px solid #f0f0f0', +}; + +export default DetailView; diff --git a/src/pages/supplier/informationManagement/SupplierChangeReviewManage/index.tsx b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/index.tsx new file mode 100644 index 0000000..515b16a --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/index.tsx @@ -0,0 +1,202 @@ +import React, { useEffect, useState } from 'react'; +import { Table, Form, Input, Select, Button, DatePicker, Tag, Space, message } from 'antd'; +import { SearchOutlined, ReloadOutlined } from '@ant-design/icons'; +import DetailView from './components/DetailView'; +import { list } from './services'; + +const { RangePicker } = DatePicker; + +const statusColorMap = { + '未开始': 'default', + '进行中': 'processing', + '已结束': 'success', +}; + +const resultColorMap = { + '通过': 'success', + '驳回': 'error', +}; + +const regionOptions = [ + { label: '请选择', value: '' }, + { label: '境内', value: '境内' }, + { label: '境外', value: '境外' }, +]; +const statusOptions = [ + { label: '请选择', value: '' }, + { label: '未开始', value: '未开始' }, + { label: '进行中', value: '进行中' }, + { label: '已结束', value: '已结束' }, +]; +const resultOptions = [ + { label: '请选择', value: '' }, + { label: '通过', value: '通过' }, + { label: '驳回', value: '驳回' }, +]; + +const SupplierChangeReviewManage: React.FC = () => { + const [form] = Form.useForm(); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); + const [detailVisible, setDetailVisible] = useState(false); + const [currentDetailId, setCurrentDetailId] = useState(null); + + // 获取表格数据 + const fetchData = async (params = {}) => { + setLoading(true); + try { + const { code, data, total, msg } = await list({ + page: pagination.current, + pageSize: pagination.pageSize, + ...params, + }); + if (code === 200) { + setData(data); + setPagination(p => ({ ...p, total: total || 0 })); + } else { + message.error(msg || '获取数据失败'); + } + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchData(); + }, [pagination.current, pagination.pageSize]); + + // 查询 + const handleSearch = () => { + setPagination(p => ({ ...p, current: 1 })); + fetchData(form.getFieldsValue()); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination(p => ({ ...p, current: 1 })); + fetchData(); + }; + + const columns = [ + { + title: '序号', + dataIndex: 'index', + width: 48, + align: 'center', + render: (_: any, __: any, idx: number) => (pagination.current - 1) * pagination.pageSize + idx + 1, + }, + { + title: '供应商名称', + dataIndex: 'name', + align: 'center', + ellipsis: true, + }, + { + title: '境内/境外', + dataIndex: 'region', + align: 'center', + }, + { + title: '供应商分类', + dataIndex: 'supplierType', + align: 'center', + }, + { + title: '准入时间', + dataIndex: 'accessTime', + align: 'center', + }, + { + title: '提交变更时间', + dataIndex: 'changeTime', + align: 'center', + }, + { + title: '流程状态', + dataIndex: 'status', + align: 'center', + render: (val: any) => {val}, + }, + { + title: '审批结果', + dataIndex: 'result', + align: 'center', + render: (val: any) => val ? {val} : null, + }, + { + title: '操作', + key: 'action', + align: 'center', + render: (_: any, record: any) => ( + + ), + }, + ]; + + // 弹窗操作 + const handleDetail = (record: any) => { + setCurrentDetailId(record.id); + setDetailVisible(true); + }; + const handleDetailClose = () => { + setDetailVisible(false); + setCurrentDetailId(null); + }; + + return ( + <> + {/* 查询表单 */} + + + + + + + + +
setPagination({ ...pagination, current }), + }} + /> + {/* 审批记录弹窗 */} + + + ); +}; + +export default SupplierChangeReviewManage; diff --git a/src/pages/supplier/informationManagement/SupplierChangeReviewManage/services.ts b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/services.ts new file mode 100644 index 0000000..fd4652c --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierChangeReviewManage/services.ts @@ -0,0 +1,26 @@ +import request from '@/utils/request'; + + +export async function getSupplierChangeList(params: any) { + return request('/api/supplier/getSupplierChangeList', { method: 'GET', params }); +} + +export async function list(params:any) { + return request('/api/system/list', { + method: 'GET', + params + }); +} +export async function getPage(params:any) { + return request('/api/system/getPage', { + method: 'GET', + params + }); +} + +export async function getSupplierChangeDetail(params:any) { + return request('/api/system/getSupplierChangeDetail', { + method: 'GET', + params + }); +} diff --git a/src/pages/supplier/informationManagement/SupplierRegisterAgent/index.tsx b/src/pages/supplier/informationManagement/SupplierRegisterAgent/index.tsx new file mode 100644 index 0000000..507ba0e --- /dev/null +++ b/src/pages/supplier/informationManagement/SupplierRegisterAgent/index.tsx @@ -0,0 +1,212 @@ +import React, { useRef, useState } from 'react'; +import ProTable from '@ant-design/pro-table'; +import type { ProColumns, ActionType } from '@ant-design/pro-table'; +import { Form, Input, Button, Row, Col, DatePicker, Tabs, Space } from 'antd'; +import { SearchOutlined, ReloadOutlined, PlusOutlined } from '@ant-design/icons'; + +const { RangePicker } = DatePicker; + +// 示例数据 +const tableData = [ + { + id: 1, + name: '中山市合创展包装材料有限公司', + type: '经销商', + unit: '重工', + dept: '采购部', + createTime: '2025-03-03', + }, + { + id: 2, + name: '深圳市欧阳华斯电源有限公司', + type: '服务商', + unit: '重工二级单位XXX', + dept: '采购部', + createTime: '2023-10-26', + }, + { + id: 3, + name: '广东振兴塑胶机械有限公司', + type: '代理商', + unit: '重工', + dept: '采购部', + createTime: '2023-10-18', + }, + { + id: 4, + name: '上海硕建建筑技术工程有限公司', + type: '服务商', + unit: '重工二级单位XXX', + dept: '采购部', + createTime: '2023-09-12', + }, + { + id: 5, + name: '中山市合创展包装材料有限公司', + type: '经销商', + unit: '重工', + dept: '采购部', + createTime: '2023-08-30', + }, + { + id: 6, + name: '深圳市欧阳华斯电源有限公司', + type: '服务商', + unit: '重工二级单位XXX', + dept: '采购部', + createTime: '2023-07-31', + }, + { + id: 7, + name: '广东振兴塑胶机械有限公司', + type: '代理商', + unit: '重工', + dept: '采购部', + createTime: '2023-07-25', + }, + { + id: 8, + name: '上海硕建建筑技术工程有限公司', + type: '服务商', + unit: '重工二级单位XXX', + dept: '采购部', + createTime: '2023-07-19', + }, + { + id: 9, + name: '广东振兴塑胶机械有限公司', + type: '代理商', + unit: '重工', + dept: '采购部', + createTime: '2023-07-17', + }, + { + id: 10, + name: '上海硕建建筑技术工程有限公司', + type: '服务商', + unit: '重工二级单位XXX', + dept: '采购部', + createTime: '2023-07-10', + }, +]; + +const columns: ProColumns[] = [ + { + title: '序号', + dataIndex: 'index', + valueType: 'index', + width: 48, + align: 'center', + }, + { + title: '供应商名称', + dataIndex: 'name', + align: 'center', + ellipsis: true, + }, + { + title: '供应商类型', + dataIndex: 'type', + align: 'center', + }, + { + title: '创建单位', + dataIndex: 'unit', + align: 'center', + ellipsis: true, + }, + { + title: '创建部门', + dataIndex: 'dept', + align: 'center', + }, + { + title: '创建时间', + dataIndex: 'createTime', + align: 'center', + sorter: (a, b) => new Date(a.createTime).getTime() - new Date(b.createTime).getTime(), + }, + { + title: '操作', + dataIndex: 'option', + align: 'center', + valueType: 'option', + render: (_, record) => ( + + 查看 + 编辑 + + ), + }, +]; + + + +const SupplierRegisterAgent: React.FC = () => { + const actionRef = useRef(); + const [tabKey, setTabKey] = useState('inland'); + + // 查询区 + const searchFormRender = ( + + + + + + + + + + + + + + + + + + + ); + + return ( +
+ {/* 查询区 */} + {searchFormRender} + +
+ + + + + + + + + + + {/* 表格 */} + + + ); +}; + +export default SupplierRegisterAgent; diff --git a/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/_mock.ts b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/_mock.ts new file mode 100644 index 0000000..0903604 --- /dev/null +++ b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/_mock.ts @@ -0,0 +1,84 @@ +import { Request, Response } from 'express'; +export default { + + 'GET /api/system/list': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + + ], + total: 2, + msg: '操作成功' + }); + }, + 'GET /api/system/treeData': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ { + title: '中国远洋海运集团集团总部', + key: 'head', + children: [ + { + title: '集采', + key: 'collection', + children: [ + { title: '镇江分公司', key: 'zhenjiang' }, + { title: '浦东分公司', key: 'pudong' }, + ], + }, + ], + },], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/regionOptions': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { label: '境内', value: '境内' }, + { label: '境外', value: '境外' }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/categoryOptions': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { label: '原材料', value: '原材料' }, + { label: '配件', value: '配件' }, + { label: '燃油', value: '燃油' }, + ], + total: 2, + msg: '操作成功' + }); + }, + 'GET /api/system/storeOptions': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { label: '库1', value: '库1' }, + { label: '库2', value: '库2' }, + ], + total: 2, + msg: '操作成功' + }); + }, + +'GET /api/system/supplierDetail': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { id: 1, unit: "中国远洋海运集团", category: "燃油", accessTime: "2025-01-05", exitTime: "2025-01-05", grayTime: "2024-01-01~2025-01-02", blackTime: "" }, + { id: 2, unit: "中国远洋海运集团", category: "配件", accessTime: "2025-01-05", exitTime: "", grayTime: "", blackTime: "" }, + { id: 3, unit: "中远海运(天津)有限公司", category: "天然气", accessTime: "2025-03-05", exitTime: "", grayTime: "", blackTime: "" }, + { id: 4, unit: "中远海运(青岛)有限公司", category: "天然气", accessTime: "2025-03-09", exitTime: "", grayTime: "", blackTime: "" }, + ], + total: 10, + msg: '操作成功' }); + }, + +}; diff --git a/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/CategoryAddModal.tsx b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/CategoryAddModal.tsx new file mode 100644 index 0000000..109373c --- /dev/null +++ b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/CategoryAddModal.tsx @@ -0,0 +1,98 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, TreeSelect, Button, message } from 'antd'; +//本地服务/接口 +import { treeData } from '../services'; + + +interface Props { + visible: boolean; + onCancel: () => void; +} + +const CategoryAddModal: React.FC = ({ visible, onCancel }) => { + const [value, setValue] = useState([]); + // 树数据 + const [dataTree, setDataTree] = useState([]); + + const [submitting, setSubmitting] = useState(false); + + useEffect(() => { + if (!visible) setValue([]); + //tree数据 + treeData().then((res) => { + const { code, data } = res; + if (code == 200) { + setDataTree(data) + } + }) + }, [visible]); + + // 提交方法 + const handleOk = async () => { + if (value.length === 0) { + message.warning('请选择品类'); + return; + } + setSubmitting(true); + try { + // TODO: 替换为你自己的接口调用 + // const res = await addCategory({ categoryIds: value }); + // 假设接口成功 + // if (res.code === 200) { + // message.success('新增品类成功'); + // onCancel(); + // } else { + // message.error(res.msg || '操作失败'); + // } + // 示例(去掉上面的注释用你自己的接口) + setTimeout(() => { + message.success('新增品类成功'); + setSubmitting(false); + onCancel(); + }, 800); + } catch (e) { + message.error('提交失败'); + setSubmitting(false); + } + }; + + return ( + + + + + } + width={600} + destroyOnClose + bodyStyle={{ minHeight: 300 }} + > + + (node.title as string).toLowerCase().includes(input.toLowerCase()) + } + onChange={setValue} + dropdownStyle={{ maxHeight: 350, overflow: 'auto' }} + /> + + ); +}; + +export default CategoryAddModal; diff --git a/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/SupplierDetailModal.tsx b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/SupplierDetailModal.tsx new file mode 100644 index 0000000..f69db4b --- /dev/null +++ b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/SupplierDetailModal.tsx @@ -0,0 +1,179 @@ +import React, { useState, useEffect } from "react"; +//第三方UI库/组件 +import { Modal, Form, Input, Button, Table, message, Tooltip } from "antd"; +//类型定义 +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +//umi 相关 +import { connect, useIntl } from 'umi'; +//本地服务/接口 +import { supplierDetail } from '../services'; +//本地组件 +import CategoryAddModal from './CategoryAddModal'; + +interface Data { + id: number; + unit: string; + category: string; + accessTime: string; + exitTime?: string; + grayTime?: string; + blackTime?: string; +} +//主体接口 +interface SupplierAccessDetailModalProps { + visible: boolean; + onCancel: () => void; + record?: any; // 你可以定义具体类型 +} +//主体 +const SupplierAccessDetailModal: React.FC = ({ visible, onCancel, record, }) => { + //双语 + const intl = useIntl(); + //查询表单 + const [form] = Form.useForm(); + //列表渲染数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); + //品类弹窗状态 + const [addModalVisible, setAddModalVisible] = useState(false); + // 搜索 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + //列表方法 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, msg, total } = await supplierDetail({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg) + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + getList(); + }, []) + // 列表头部数据 + const columns:ColumnsType = [ + { + title: "序号", + dataIndex: "index", + key: "index", + align: "center", + render: (_: any, __: any, idx: number) => idx + 1, + width: 60, + }, + { + title: "准入单位", + dataIndex: "unit", + key: "unit", + align: "left", + ellipsis: true, + render: (dom, record) => + + {record.unit} + , + }, + { + title: "准入品类", + dataIndex: "category", + key: "category", + align: "center", + }, + { + title: "准入时间", + dataIndex: "accessTime", + key: "accessTime", + align: "center", + }, + { + title: "退出时间", + dataIndex: "exitTime", + key: "exitTime", + align: "center", + }, + { + title: "进入灰名单时间", + dataIndex: "grayTime", + key: "grayTime", + align: "center", + }, + { + title: "进入黑名单时间", + dataIndex: "blackTime", + key: "blackTime", + align: "center", + }, + ]; + return ( + + {/* 查询栏 */} +
+ + + + + + + + + + + + + + {/* 表格内容 */} +
getList(pagination.current!, pagination.pageSize!)} + /> + {/* 新增品类弹窗 */} + setAddModalVisible(false)} + // onOk={...} // 根据你的业务需要加 + /> + + ); +}; + +export default connect()(SupplierAccessDetailModal); diff --git a/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/SupplierViewModal.tsx b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/SupplierViewModal.tsx new file mode 100644 index 0000000..0bff06d --- /dev/null +++ b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/components/SupplierViewModal.tsx @@ -0,0 +1,21 @@ +import React from "react"; +//第三方UI库/组件 +import { Modal } from "antd"; +//本地组件、业务逻辑 +import CompanyInfo from '@/components/CompanyInfo'; +//主体接口 +interface SupplierViewModalProps { + visible: boolean; + onCancel: () => void; + record?: any; +} +// 查看主体 +const SupplierViewModal: React.FC = ({ visible, onCancel, record }) => { + return ( + + + + ); +}; + +export default SupplierViewModal; diff --git a/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/index.tsx b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/index.tsx new file mode 100644 index 0000000..f4de1cf --- /dev/null +++ b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/index.tsx @@ -0,0 +1,294 @@ +import React, { useEffect, useState } from "react"; +//第三方UI库/组件 +import { Form, Button, Table, Select, Input, Tree, Row, Col, Space, message } from 'antd'; +import { SearchOutlined, DownloadOutlined, ReloadOutlined } from '@ant-design/icons'; +//类型定义 +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +//umi 相关 +import { connect } from 'umi'; +//本地组件、弹窗、业务逻辑 +import SupplierViewModal from './components/SupplierViewModal'; +import SupplierDetailModal from './components/SupplierDetailModal'; +//本地服务/接口 +import { list, treeData, systemDict } from './services'; + +const { Option } = Select; +//下拉数据接口 +type OptionType = { label: string; value: string }; +// 列表数据接口 +interface Data { + id: number; + name: string; + region: string; + supplierType: string; + regTime: string; + status: string; +} + +const groupQualifiedSupplierQuery: React.FC = () => { + //搜搜表单 + const [form] = Form.useForm(); + // 树数据 + const [dataTree, setDataTree] = useState([]); + //默认选中树 + const [treeSelected, setTreeSelected] = useState([]); + //当前树key + const [selectedKeys, setSelectedKeys] = useState(''); + //列表数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 188 }); + //查看是否显示状态 + const [viewVisible, setViewVisible] = useState(false); + //准入明细是否显示状态 + const [detailVisible, setDetailVisible] = useState(false); + //查看、准入明细 参数传递 + const [currentRecord, setCurrentRecord] = useState(null); + // 境内/境外下拉数据 + const [regionOptions, setRegionOptions] = useState([]); + //集采类别 + const [categoryOptions, setCategoryOptions] = useState([]); + //集采库 + const [storeOptions, setStoreOptions] = useState([]); + // 导出 + const handleExport = () => { + message.success('导出成功(此处为前端示例,实际应调接口)'); + }; + // 查询 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(selectedKeys); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(selectedKeys); + }; + // 点击树节点请求右表 + const handleTreeSelect = (keys: React.Key[]) => { + const key = keys[0] as string; + setSelectedKeys(key); + getList(key) + }; + //列表方法 + const getList = async (treeId: string, page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, msg, total } = await list({ page, pageSize, treeId }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg) + } + } finally { + setLoading(false); + } + }; + // 辅助递归找第一个叶子 key(一般最下层为叶子) + const findFirstLeafKey = (nodes: any[]): string | undefined => { + for (const node of nodes) { + if (!node.children || node.children.length === 0) return node.key; + const found = findFirstLeafKey(node.children); + if (found) return found; + } + return undefined; + }; + // 初始化时选中树第一个叶子节点,并请求右表 + useEffect(() => { + // 境内/境外 下拉 + systemDict('regionOptions').then((res) => { + const { code, data } = res; + if (code == 200) { + setRegionOptions(data) + } + }); + // 集采类别 下拉 + systemDict('categoryOptions').then((res) => { + const { code, data } = res; + if (code == 200) { + setCategoryOptions(data) + } + }); + // 集采库 下拉 + systemDict('storeOptions').then((res) => { + const { code, data } = res; + if (code == 200) { + setStoreOptions(data) + } + }); + //tree数据 + treeData().then((res) => { + const { code, data } = res; + if (code == 200) { + setDataTree(data) + const key = findFirstLeafKey(data); + if (key) { + setSelectedKeys(key); + setTreeSelected([key]) + getList(key) + } + } + }) + }, []); + + + const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + align: 'center', + width: 60, + render: (_: any, __: any, idx: number) => idx + 1, + }, + { + title: '供应商名称', + dataIndex: 'name', + key: 'name', + align: 'center', + }, + { + title: '统一社会信用代码/税号', + dataIndex: 'code', + key: 'code', + align: 'center', + }, + { + title: '境内/境外', + dataIndex: 'region', + key: 'region', + align: 'center', + }, + { + title: '企业类型', + dataIndex: 'type', + key: 'type', + align: 'center', + }, + { + title: '注册时间', + dataIndex: 'regTime', + key: 'regTime', + align: 'center', + }, + { + title: '操作', + key: 'option', + align: 'center', + render: (record: any) => ( + + { setCurrentRecord(record); setViewVisible(true); }} + >查看 + { setCurrentRecord(record); setDetailVisible(true); }} + >准入明细 + + ), + }, + ]; + + return ( + <> +
+ + {/* 左侧树 */} +
+
+ {dataTree.length > 0 && ( + + )} + +
+ + {/* 右侧主体 */} + + {/* 查询表单 */} + + + + + + + + + + + + + + + + + + + + {/* 表格 */} +
getList(selectedKeys, pagination.current!, pagination.pageSize!)} + /> + + + + {/* 查看组件 */} + setViewVisible(false)} + /> + {/* 准入明细组件 */} + setDetailVisible(false)} + /> + + ); +}; + +export default connect()(groupQualifiedSupplierQuery); diff --git a/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/services.ts b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/services.ts new file mode 100644 index 0000000..bb6a1dd --- /dev/null +++ b/src/pages/supplier/informationRetrieval/groupQualifiedSupplierQuery/services.ts @@ -0,0 +1,36 @@ +import request from '@/utils/request'; + +export interface ListParams { + page: number; + pageSize: number; + treeId?: string; + tmpToken?: string; +} +export async function list(params:ListParams) { + return request('/api/system/library', { + method: 'GET', + params + }); +} +export interface supplierDetailParams { + page: number; + pageSize: number; + captcha?: string; + tmpToken?: string; +} +export async function supplierDetail(params:supplierDetailParams) { + return request(`/api/system/supplierDetail`, { + method: 'GET', + params + }); +} +export async function treeData() { + return request('/api/system/treeData', { + method: 'GET' + }); +} +export async function systemDict(dict:String) { + return request(`/api/system/${dict}`, { + method: 'GET' + }); +} \ No newline at end of file diff --git a/src/pages/supplier/informationRetrieval/mySupplierInquiry/_mock.ts b/src/pages/supplier/informationRetrieval/mySupplierInquiry/_mock.ts new file mode 100644 index 0000000..0903604 --- /dev/null +++ b/src/pages/supplier/informationRetrieval/mySupplierInquiry/_mock.ts @@ -0,0 +1,84 @@ +import { Request, Response } from 'express'; +export default { + + 'GET /api/system/list': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + + ], + total: 2, + msg: '操作成功' + }); + }, + 'GET /api/system/treeData': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ { + title: '中国远洋海运集团集团总部', + key: 'head', + children: [ + { + title: '集采', + key: 'collection', + children: [ + { title: '镇江分公司', key: 'zhenjiang' }, + { title: '浦东分公司', key: 'pudong' }, + ], + }, + ], + },], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/regionOptions': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { label: '境内', value: '境内' }, + { label: '境外', value: '境外' }, + ], + total: 2, + msg: '操作成功' + }); + }, + + 'GET /api/system/categoryOptions': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { label: '原材料', value: '原材料' }, + { label: '配件', value: '配件' }, + { label: '燃油', value: '燃油' }, + ], + total: 2, + msg: '操作成功' + }); + }, + 'GET /api/system/storeOptions': (req: Request, res: Response) => { + res.json({ + code: 200, + data: [ + { label: '库1', value: '库1' }, + { label: '库2', value: '库2' }, + ], + total: 2, + msg: '操作成功' + }); + }, + +'GET /api/system/supplierDetail': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { id: 1, unit: "中国远洋海运集团", category: "燃油", accessTime: "2025-01-05", exitTime: "2025-01-05", grayTime: "2024-01-01~2025-01-02", blackTime: "" }, + { id: 2, unit: "中国远洋海运集团", category: "配件", accessTime: "2025-01-05", exitTime: "", grayTime: "", blackTime: "" }, + { id: 3, unit: "中远海运(天津)有限公司", category: "天然气", accessTime: "2025-03-05", exitTime: "", grayTime: "", blackTime: "" }, + { id: 4, unit: "中远海运(青岛)有限公司", category: "天然气", accessTime: "2025-03-09", exitTime: "", grayTime: "", blackTime: "" }, + ], + total: 10, + msg: '操作成功' }); + }, + +}; diff --git a/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/CategoryAddModal.tsx b/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/CategoryAddModal.tsx new file mode 100644 index 0000000..109373c --- /dev/null +++ b/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/CategoryAddModal.tsx @@ -0,0 +1,98 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, TreeSelect, Button, message } from 'antd'; +//本地服务/接口 +import { treeData } from '../services'; + + +interface Props { + visible: boolean; + onCancel: () => void; +} + +const CategoryAddModal: React.FC = ({ visible, onCancel }) => { + const [value, setValue] = useState([]); + // 树数据 + const [dataTree, setDataTree] = useState([]); + + const [submitting, setSubmitting] = useState(false); + + useEffect(() => { + if (!visible) setValue([]); + //tree数据 + treeData().then((res) => { + const { code, data } = res; + if (code == 200) { + setDataTree(data) + } + }) + }, [visible]); + + // 提交方法 + const handleOk = async () => { + if (value.length === 0) { + message.warning('请选择品类'); + return; + } + setSubmitting(true); + try { + // TODO: 替换为你自己的接口调用 + // const res = await addCategory({ categoryIds: value }); + // 假设接口成功 + // if (res.code === 200) { + // message.success('新增品类成功'); + // onCancel(); + // } else { + // message.error(res.msg || '操作失败'); + // } + // 示例(去掉上面的注释用你自己的接口) + setTimeout(() => { + message.success('新增品类成功'); + setSubmitting(false); + onCancel(); + }, 800); + } catch (e) { + message.error('提交失败'); + setSubmitting(false); + } + }; + + return ( + + + + + } + width={600} + destroyOnClose + bodyStyle={{ minHeight: 300 }} + > + + (node.title as string).toLowerCase().includes(input.toLowerCase()) + } + onChange={setValue} + dropdownStyle={{ maxHeight: 350, overflow: 'auto' }} + /> + + ); +}; + +export default CategoryAddModal; diff --git a/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/SupplierDetailModal.tsx b/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/SupplierDetailModal.tsx new file mode 100644 index 0000000..f69db4b --- /dev/null +++ b/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/SupplierDetailModal.tsx @@ -0,0 +1,179 @@ +import React, { useState, useEffect } from "react"; +//第三方UI库/组件 +import { Modal, Form, Input, Button, Table, message, Tooltip } from "antd"; +//类型定义 +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +//umi 相关 +import { connect, useIntl } from 'umi'; +//本地服务/接口 +import { supplierDetail } from '../services'; +//本地组件 +import CategoryAddModal from './CategoryAddModal'; + +interface Data { + id: number; + unit: string; + category: string; + accessTime: string; + exitTime?: string; + grayTime?: string; + blackTime?: string; +} +//主体接口 +interface SupplierAccessDetailModalProps { + visible: boolean; + onCancel: () => void; + record?: any; // 你可以定义具体类型 +} +//主体 +const SupplierAccessDetailModal: React.FC = ({ visible, onCancel, record, }) => { + //双语 + const intl = useIntl(); + //查询表单 + const [form] = Form.useForm(); + //列表渲染数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); + //品类弹窗状态 + const [addModalVisible, setAddModalVisible] = useState(false); + // 搜索 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + //列表方法 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, msg, total } = await supplierDetail({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg) + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + getList(); + }, []) + // 列表头部数据 + const columns:ColumnsType = [ + { + title: "序号", + dataIndex: "index", + key: "index", + align: "center", + render: (_: any, __: any, idx: number) => idx + 1, + width: 60, + }, + { + title: "准入单位", + dataIndex: "unit", + key: "unit", + align: "left", + ellipsis: true, + render: (dom, record) => + + {record.unit} + , + }, + { + title: "准入品类", + dataIndex: "category", + key: "category", + align: "center", + }, + { + title: "准入时间", + dataIndex: "accessTime", + key: "accessTime", + align: "center", + }, + { + title: "退出时间", + dataIndex: "exitTime", + key: "exitTime", + align: "center", + }, + { + title: "进入灰名单时间", + dataIndex: "grayTime", + key: "grayTime", + align: "center", + }, + { + title: "进入黑名单时间", + dataIndex: "blackTime", + key: "blackTime", + align: "center", + }, + ]; + return ( + + {/* 查询栏 */} +
+ + + + + + + + + + + + + + {/* 表格内容 */} +
getList(pagination.current!, pagination.pageSize!)} + /> + {/* 新增品类弹窗 */} + setAddModalVisible(false)} + // onOk={...} // 根据你的业务需要加 + /> + + ); +}; + +export default connect()(SupplierAccessDetailModal); diff --git a/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/SupplierViewModal.tsx b/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/SupplierViewModal.tsx new file mode 100644 index 0000000..0bff06d --- /dev/null +++ b/src/pages/supplier/informationRetrieval/mySupplierInquiry/components/SupplierViewModal.tsx @@ -0,0 +1,21 @@ +import React from "react"; +//第三方UI库/组件 +import { Modal } from "antd"; +//本地组件、业务逻辑 +import CompanyInfo from '@/components/CompanyInfo'; +//主体接口 +interface SupplierViewModalProps { + visible: boolean; + onCancel: () => void; + record?: any; +} +// 查看主体 +const SupplierViewModal: React.FC = ({ visible, onCancel, record }) => { + return ( + + + + ); +}; + +export default SupplierViewModal; diff --git a/src/pages/supplier/informationRetrieval/mySupplierInquiry/index.tsx b/src/pages/supplier/informationRetrieval/mySupplierInquiry/index.tsx new file mode 100644 index 0000000..91c3626 --- /dev/null +++ b/src/pages/supplier/informationRetrieval/mySupplierInquiry/index.tsx @@ -0,0 +1,232 @@ +import React, { useEffect, useState } from "react"; +//第三方UI库/组件 +import { Form, Button, Table, Select, Input, Space, message } from 'antd'; +import { SearchOutlined, DownloadOutlined, ReloadOutlined } from '@ant-design/icons'; +//类型定义 +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +//umi 相关 +import { connect } from 'umi'; +//本地组件、弹窗、业务逻辑 +import SupplierViewModal from './components/SupplierViewModal'; +import SupplierDetailModal from './components/SupplierDetailModal'; +//本地服务/接口 +import { list, systemDict } from './services'; + +const { Option } = Select; +//下拉数据接口 +type OptionType = { label: string; value: string }; +// 列表数据接口 +interface Data { + id: number; + name: string; + region: string; + supplierType: string; + regTime: string; + status: string; +} + +const mySupplierInquiry: React.FC = () => { + //搜搜表单 + const [form] = Form.useForm(); + //列表数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 188 }); + //查看是否显示状态 + const [viewVisible, setViewVisible] = useState(false); + //准入明细是否显示状态 + const [detailVisible, setDetailVisible] = useState(false); + //查看、准入明细 参数传递 + const [currentRecord, setCurrentRecord] = useState(null); + // 境内/境外下拉数据 + const [regionOptions, setRegionOptions] = useState([]); + //集采类别 + const [categoryOptions, setCategoryOptions] = useState([]); + //集采库 + const [storeOptions, setStoreOptions] = useState([]); + // 导出 + const handleExport = () => { + message.success('导出成功(此处为前端示例,实际应调接口)'); + }; + // 查询 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + //列表方法 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, msg, total } = await list({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg) + } + } finally { + setLoading(false); + } + }; + // 初始化 + useEffect(() => { + // 境内/境外 下拉 + systemDict('regionOptions').then((res) => { + const { code, data } = res; + if (code == 200) { + setRegionOptions(data) + } + }); + // 集采类别 下拉 + systemDict('categoryOptions').then((res) => { + const { code, data } = res; + if (code == 200) { + setCategoryOptions(data) + } + }); + // 集采库 下拉 + systemDict('storeOptions').then((res) => { + const { code, data } = res; + if (code == 200) { + setStoreOptions(data) + } + }); + getList(); + }, []); + + + const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + align: 'center', + width: 60, + render: (_: any, __: any, idx: number) => idx + 1, + }, + { + title: '供应商名称', + dataIndex: 'name', + key: 'name', + align: 'center', + }, + { + title: '统一社会信用代码/税号', + dataIndex: 'code', + key: 'code', + align: 'center', + }, + { + title: '境内/境外', + dataIndex: 'region', + key: 'region', + align: 'center', + }, + { + title: '企业类型', + dataIndex: 'type', + key: 'type', + align: 'center', + }, + { + title: '注册时间', + dataIndex: 'regTime', + key: 'regTime', + align: 'center', + }, + { + title: '操作', + key: 'option', + align: 'center', + render: (record: any) => ( + + { setCurrentRecord(record); setViewVisible(true); }} + >查看 + { setCurrentRecord(record); setDetailVisible(true); }} + >准入明细 + + ), + }, + ]; + + return ( + <> +
+ + + + + + + + + + + + + + + + + + + {/* 表格 */} +
getList(pagination.current!, pagination.pageSize!)} + /> + {/* 查看组件 */} + setViewVisible(false)} + /> + {/* 准入明细组件 */} + setDetailVisible(false)} + /> + + ); +}; + +export default connect()(mySupplierInquiry); diff --git a/src/pages/supplier/informationRetrieval/mySupplierInquiry/services.ts b/src/pages/supplier/informationRetrieval/mySupplierInquiry/services.ts new file mode 100644 index 0000000..bb6a1dd --- /dev/null +++ b/src/pages/supplier/informationRetrieval/mySupplierInquiry/services.ts @@ -0,0 +1,36 @@ +import request from '@/utils/request'; + +export interface ListParams { + page: number; + pageSize: number; + treeId?: string; + tmpToken?: string; +} +export async function list(params:ListParams) { + return request('/api/system/library', { + method: 'GET', + params + }); +} +export interface supplierDetailParams { + page: number; + pageSize: number; + captcha?: string; + tmpToken?: string; +} +export async function supplierDetail(params:supplierDetailParams) { + return request(`/api/system/supplierDetail`, { + method: 'GET', + params + }); +} +export async function treeData() { + return request('/api/system/treeData', { + method: 'GET' + }); +} +export async function systemDict(dict:String) { + return request(`/api/system/${dict}`, { + method: 'GET' + }); +} \ No newline at end of file diff --git a/src/pages/supplier/informationRetrieval/registrationQuery/_mock.ts b/src/pages/supplier/informationRetrieval/registrationQuery/_mock.ts new file mode 100644 index 0000000..7646604 --- /dev/null +++ b/src/pages/supplier/informationRetrieval/registrationQuery/_mock.ts @@ -0,0 +1,392 @@ +import { Request, Response } from 'express'; + +// const data = [ +// { +// title: 'Name', +// dataIndex: '评审项', +// key: '评审项', +// }, +// { +// title: 'Other', +// children: [ +// { +// title: 'Age', +// key: '人员A', +// }, +// { +// title: 'Address', +// key: '人员B', +// }, +// ] +// }, +// ] +const dataInvoiceInfo = [ + { + id: '1', + taxpayerType: '一般纳税人', + taxpayerCode: '91345678901234567X', + head: '北京某科技有限公司', + address: '北京市朝阳区XX路99号', + phone: '010-12345678', + bank: '中国银行北京分行', + account: '6228888888888888', + updateTime: '2025-06-17 10:20:00', + voided: false, + qualificationCertificate: 'https://example.com/cert1.pdf', + }, + { + id: '2', + taxpayerType: '小规模纳税人', + taxpayerCode: '91345678901234566Y', + head: '上海某信息技术有限公司', + address: '上海市浦东新区XX大厦8楼', + phone: '021-87654321', + bank: '工商银行上海分行', + account: '6229999999999999', + updateTime: '2025-06-16 15:30:00', + voided: true, + qualificationCertificate: '', + }, +] +const mockQualificationData = [ + { + id: '1', + certificateType: '建筑业企业资质证书', + name: '建筑工程施工总承包一级', + code: 'ZJ-A123456', + typeLevel: '一级', + authority: '住房和城乡建设部', + dateTime: '2023-03-01', + termOfValidity: '2028-03-01', + updateTime: '2025-06-17 10:30:00', + }, + { + id: '2', + certificateType: '安全生产许可证', + name: '施工企业安全生产许可证', + code: 'AQ-789012', + typeLevel: 'A级', + authority: '应急管理部', + dateTime: '2022-06-15', + termOfValidity: '2025-06-15', + updateTime: '2025-06-17 11:45:00', + }, +] +export const mockData = { + base: { + "id": "123456", + "supplierType": "dvs", + "licenceAccessory": "https://example.com/license.pdf", + "licenceDate": "2025-12-31", + "enterpriseType": "company", + "name": "深圳供应商有限公司", + "nameEn": "Shenzhen Supplier Co., Ltd.", + "socialCreditCode": "91440300MA5F3XXXXQ", + "range": "电子元器件、金属材料销售", + "regAddress": "广东省深圳市南山区科技园", + "workAddress": "广东省深圳市南山区软件产业基地", + "parentCompanyInvestor": "深圳控股集团有限公司", + "legalPerson": "李四", + "idCard": "440301199001015678", + "capital": 5000, + "contactsName": "王五", + "contactsPhone": "13800138000", + "contactsType": "法人代表", + "contactsEmail": "contact@supplier.com", + "telephone": "0755-12345678", + "nation": "新加坡", + "vat": "SG12345678VAT", + "taxpayerId": "SG-TAX-998877", + "currency": "SGD", + "personName": "张三", + "personPhone": "13812345678", + "personBank": "中国银行深圳分行", + "personAccount": "6222020200123456789", + "remark": "该供应商已完成初步审核", + "accessStatus": 1, + "blacklistStatus": 0, + "greylistStatus": 1, + "fillinStatus": 0, + "fillinBy": "", + "sapCode": "SAP998877", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }, + qualifications: [{ + "id": "cert-001", + "supplierId": "supplier-123456", + "certificateType": "安全生产许可证", + "name": "建筑施工总承包一级资质", + "code": "ZJ20230605001", + "typeLevel": "一级", + "authority": "住房和城乡建设部", + "dateTime": "2023-06-05", + "termOfValidity": "2026-06-05", + "accessory": "https://example.com/certificate.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2023-06-01 10:00:00", + "updateBy": "admin", + "updateTime": "2024-06-01 11:00:00", + "lastUpdateTime": "2025-06-17 09:30:00" + }], + invoice: { + "id": "invoice-001", + "supplierId": "supplier-123456", + "taxpayerType": "general", + "taxpayerCode": "91440300MA5F3XXXXQ", + "phone": "0755-12345678", + "account": "6222020200123456789", + "head": "深圳供应商有限公司", + "address": "深圳市南山区科技园开票楼101号", + "bank": "中国银行深圳分行", + "qualificationCertificate": "https://example.com/tax-cert.pdf", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }, + bank: [{ + "id": "bank-001", + "supplierId": "supplier-123456", + "interbankNumber": "123456789012", + "bank": "中国银行深圳分行", + "swiftCode": "BKCHCNBJ45A", + "accountName": "Shenzhen Supplier Co., Ltd.", + "account": "6222020200123456789", + "currency": "CNY", + "nation": "中国", + "province": "广东省", + "city": "深圳市", + "delFlag": "normal", + "createBy": "admin", + "createTime": "2024-06-01 09:00:00", + "updateBy": "admin", + "updateTime": "2025-06-01 10:00:00", + "lastUpdateTime": "2025-06-17 08:30:00" + }], + survey: { + "supplierName": "深圳供应商有限公司", + "name": "李四", + "position": "采购经理", + "phone": "13800138000", + "email": "lisi@supplier.com", + "dateTime": "2025-06-17", + }, + questionReply: [ + { + "surveyQuestion": "法律法规:\n我们确保经营和提供的产品服务遵守国家及 各业务所在地的所有使用法律、法规", + "replyValue": "是", + },{ + "surveyQuestion": "健康和安全:\n我们为员工提供符合法律法规的安全且健康 的工作场所。我们建立安全管理体系,并向 员工传达工作场所或生活设施的健康和安全 标准,致力于减少工作对员工造成的伤害和 疾病。", + "replyValue": "符合", + },{ + "surveyQuestion": "环境:\n我们能够以环境友好的方式经营。我们遵守 适用的环境法律、法规和标准;并建立有效 的环境管理体系。\n我们遵守贵集团对相关产品或服务的部分附 加环境要求,这些要求和规定体现在设计与 产品规范的合同文档中。", + "replyValue": "符合", + },{ + "surveyQuestion": "监督和记录:\n我们保留记录遵守相关法律和此行为准则的必要文件,并根据要求为贵集团提供对文件的查看权。我们会允许贵集团在适当的时候,以验证行为准则执行为目的的现场勘查", + "replyValue": "符合", + } + ], + attachments: { + "attachmentsType": "commitment", + "fileName": "anti-bribery-commitment.pdf", + "fileType": "pdf", + "fileSize": "204800", + "filePath": "/data/files/anti-bribery-commitment.pdf", + "fileUrl": "http://example.com/files/anti-bribery-commitment.pdf", + } +}; +// 代码中会兼容本地 service mock 以及部署站点的静态数据 +export default { + // 供应商信息 + 'GET /api/system/coscoSupplier': (req: Request, res: Response) => { + res.json({ + code: 200, + data: mockData, + msg: '操作成功' + }); + }, + // + 'GET /api/system/qualifications': (req: Request, res: Response) => { + res.json({ code: 200, + data: mockQualificationData, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/invoice': (req: Request, res: Response) => { + res.json({ code: 200, + data: dataInvoiceInfo, + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/bank': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + id: '1', + interbankNumber: '123456789', + bank: '中国银行', + accountName: '张三', + account: '6228480000000000000', + currency: '人民币', + nation: '中国', + province: '广东省', + city: '广州市', + updateTime: '2024-06-18', + }, + { + id: '2', + interbankNumber: '987654321', + bank: '工商银行', + accountName: '李四', + account: '6228480000000000001', + currency: '美元', + nation: '中国', + province: '江苏省', + city: '南京市', + updateTime: '2024-06-17', + }, + ], + total: 2, + msg: '操作成功' + }); + }, + // + 'GET /api/system/tianyancha': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { + key: '1', + base: '京', + name: '北京科技有限公司', + legalPersonName: '张三', + legalPersonType: '1', + regNumber: '110108123456789', + industry: '信息技术', + companyOrgType: '有限责任公司', + regLocation: '北京市海淀区中关村', + estiblishTime: '2010-06-15', + fromTime: '2010-06-16', + toTime: '2025-06-15', + businessScope: '软件开发、技术咨询', + approvedTime: '2010-06-10', + regStatus: '存续', + regCapital: '5000万元', + regInstitute: '北京市工商局', + orgNumber: '1234567890', + creditCode: '91110108MA01A12345', + property3: 'Beijing Tech Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1001', + taxNumber: '110108123456789', + email: 'contact@bjtech.com', + website: 'http://www.bjtech.com', + phoneNumber: '010-12345678', + lastUpdateTime: '2025-06-15 10:00:00', + }, + { + key: '2', + base: '沪', + name: '上海电子商务有限公司', + legalPersonName: '李四', + legalPersonType: '1', + regNumber: '310101987654321', + industry: '电子商务', + companyOrgType: '股份有限公司', + regLocation: '上海市浦东新区', + estiblishTime: '2015-03-20', + fromTime: '2015-03-21', + toTime: '2030-03-20', + businessScope: '电子商务平台运营、广告设计', + approvedTime: '2015-03-15', + regStatus: '存续', + regCapital: '1亿元', + regInstitute: '上海市工商局', + orgNumber: '0987654321', + creditCode: '91310101MA1AB23456', + property3: 'Shanghai E-commerce Co., Ltd.', + updatetime: '2025-06-15', + companyId: '1002', + taxNumber: '310101987654321', + email: 'info@shcommerce.com', + website: 'http://www.shcommerce.com', + phoneNumber: '021-87654321', + lastUpdateTime: '2025-06-15 09:30:00', + }, + ], + total: 2, + msg: '操作成功' }); + }, + // + 'GET /api/system/regionDict': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { label: '境内', value: '境内' }, + { label: '境外', value: '境外' }, + ], + total: 2, + msg: '操作成功' }); + }, + 'GET /api/system/status': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { label: '已准入', value: '已准入' }, + { label: '未准入', value: '未准入' }, + { label: '已驳回', value: '已驳回' }, + { label: '已退出', value: '已退出' }, + ], + total: 2, + msg: '操作成功' }); + }, + 'GET /api/system/list': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { id: 1, name: '中山市合创展包装材料有限公司', region: '境内', supplierType: '中央企业', regTime: '2025-03-03', status: '已准入' }, + { id: 2, name: '深圳市欧阳华斯电源有限公司', region: '境内', supplierType: '民营企业', regTime: '2023-10-26', status: '已准入' }, + { id: 3, name: '广东振兴塑胶机械有限公司', region: '境内', supplierType: '中央企业', regTime: '2023-10-18', status: '已驳回' }, + { id: 4, name: '上海硕建建筑技术工程有限公司', region: '境内', supplierType: '民营企业', regTime: '2023-09-12', status: '已退出' }, + { id: 5, name: '中山市合创展包装材料有限公司', region: '境内', supplierType: '中央企业', regTime: '2023-08-30', status: '未准入' }, + { id: 6, name: '深圳市欧阳华斯电源有限公司', region: '境内', supplierType: '民营企业', regTime: '2023-07-31', status: '未准入' }, + { id: 7, name: '广东振兴塑胶机械有限公司', region: '境内', supplierType: '中央企业', regTime: '2023-07-25', status: '未准入' }, + { id: 8, name: '上海硕建建筑技术工程有限公司', region: '境内', supplierType: '民营企业', regTime: '2023-07-17', status: '未准入' }, + { id: 9, name: '广东振兴塑胶机械有限公司', region: '境内', supplierType: '中央企业', regTime: '2023-07-11', status: '未准入' }, + { id: 10, name: '上海硕建建筑技术工程有限公司', region: '境内', supplierType: '民营企业', regTime: '2023-07-10', status: '未准入' }, + ], + total: 10, + msg: '操作成功' }); + }, + 'GET /api/system/supplierDetail': (req: Request, res: Response) => { + res.json({ code: 200, + data: [ + { id: 1, unit: "中国远洋海运集团", category: "燃油", accessTime: "2025-01-05", exitTime: "2025-01-05", grayTime: "2024-01-01~2025-01-02", blackTime: "" }, + { id: 2, unit: "中国远洋海运集团", category: "配件", accessTime: "2025-01-05", exitTime: "", grayTime: "", blackTime: "" }, + { id: 3, unit: "中远海运(天津)有限公司", category: "天然气", accessTime: "2025-03-05", exitTime: "", grayTime: "", blackTime: "" }, + { id: 4, unit: "中远海运(青岛)有限公司", category: "天然气", accessTime: "2025-03-09", exitTime: "", grayTime: "", blackTime: "" }, + ], + total: 10, + msg: '操作成功' }); + }, + + 'GET /api/500': (req: Request, res: Response) => { + res.status(500).send({ + timestamp: 1513932555104, + status: 500, + error: 'error', + message: 'error', + path: '/base/category/list', + }); + }, + + +}; diff --git a/src/pages/supplier/informationRetrieval/registrationQuery/components/SupplierDetailModal.tsx b/src/pages/supplier/informationRetrieval/registrationQuery/components/SupplierDetailModal.tsx new file mode 100644 index 0000000..a11412c --- /dev/null +++ b/src/pages/supplier/informationRetrieval/registrationQuery/components/SupplierDetailModal.tsx @@ -0,0 +1,160 @@ +import React, { useState, useEffect } from "react"; +//第三方UI库/组件 +import { Modal, Form, Input, Button, Table, message, Tooltip } from "antd"; +//类型定义 +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +//umi 相关 +import { connect, useIntl } from 'umi'; +//本地服务/接口 +import { supplierDetail } from '../services'; + +interface Data { + id: number; + unit: string; + category: string; + accessTime: string; + exitTime?: string; + grayTime?: string; + blackTime?: string; +} +//主体接口 +interface SupplierAccessDetailModalProps { + visible: boolean; + onCancel: () => void; + record?: any; // 你可以定义具体类型 +} +//主体 +const SupplierAccessDetailModal: React.FC = ({ visible, onCancel, record, }) => { + //双语 + const intl = useIntl(); + //查询表单 + const [form] = Form.useForm(); + //列表渲染数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); + // 搜索 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + //列表方法 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, msg, total } = await supplierDetail({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg) + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + getList(); + }, []) + // 列表头部数据 + const columns:ColumnsType = [ + { + title: "序号", + dataIndex: "index", + key: "index", + align: "center", + render: (_: any, __: any, idx: number) => idx + 1, + width: 60, + }, + { + title: "准入单位", + dataIndex: "unit", + key: "unit", + align: "left", + ellipsis: true, + render: (dom, record) => + + {record.unit} + , + }, + { + title: "准入品类", + dataIndex: "category", + key: "category", + align: "center", + }, + { + title: "准入时间", + dataIndex: "accessTime", + key: "accessTime", + align: "center", + }, + { + title: "退出时间", + dataIndex: "exitTime", + key: "exitTime", + align: "center", + }, + { + title: "进入灰名单时间", + dataIndex: "grayTime", + key: "grayTime", + align: "center", + }, + { + title: "进入黑名单时间", + dataIndex: "blackTime", + key: "blackTime", + align: "center", + }, + ]; + return ( + + {/* 查询栏 */} +
+ + + + + + + + + + + {/* 表格内容 */} +
getList(pagination.current!, pagination.pageSize!)} + /> + + ); +}; + +export default connect()(SupplierAccessDetailModal); diff --git a/src/pages/supplier/informationRetrieval/registrationQuery/components/SupplierViewModal.tsx b/src/pages/supplier/informationRetrieval/registrationQuery/components/SupplierViewModal.tsx new file mode 100644 index 0000000..0bff06d --- /dev/null +++ b/src/pages/supplier/informationRetrieval/registrationQuery/components/SupplierViewModal.tsx @@ -0,0 +1,21 @@ +import React from "react"; +//第三方UI库/组件 +import { Modal } from "antd"; +//本地组件、业务逻辑 +import CompanyInfo from '@/components/CompanyInfo'; +//主体接口 +interface SupplierViewModalProps { + visible: boolean; + onCancel: () => void; + record?: any; +} +// 查看主体 +const SupplierViewModal: React.FC = ({ visible, onCancel, record }) => { + return ( + + + + ); +}; + +export default SupplierViewModal; diff --git a/src/pages/supplier/informationRetrieval/registrationQuery/index.tsx b/src/pages/supplier/informationRetrieval/registrationQuery/index.tsx new file mode 100644 index 0000000..f2fd762 --- /dev/null +++ b/src/pages/supplier/informationRetrieval/registrationQuery/index.tsx @@ -0,0 +1,224 @@ +import React, { useEffect, useState } from "react"; +//第三方UI库/组件 +import { Form, Button, Table, Select, Input, Space, Tooltip, message } from 'antd'; +import { SearchOutlined } from '@ant-design/icons'; +//类型定义 +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +//umi 相关 +import { connect, useIntl } from 'umi'; +//本地组件、弹窗、业务逻辑 +import SupplierViewModal from './components/SupplierViewModal'; +import SupplierDetailModal from './components/SupplierDetailModal'; +//本地服务/接口 +import { systemDict, list } from './services'; + +// 列表数据接口 +interface Data { + id: number; + name: string; + region: string; + supplierType: string; + regTime: string; + status: string; +} +//下拉数据接口 +type OptionType = { label: string; value: string }; +//准入状态 +const statusColor = (status: string) => { + if (status === '已驳回' || status === '已退出') return 'red'; + if (status === '已准入') return 'green'; + return undefined; +}; +// 页面主体 +const RegistrationQuery: React.FC = () => { + //双语 + const intl = useIntl(); + //查询表单 + const [form] = Form.useForm(); + //列表渲染数据 + const [data, setData] = useState([]); + //列表加载 + const [loading, setLoading] = useState(false); + //列表分页 + const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); + //查看是否显示状态 + const [viewVisible, setViewVisible] = useState(false); + //准入明细是否显示状态 + const [detailVisible, setDetailVisible] = useState(false); + //查看、准入明细 参数传递 + const [currentRecord, setCurrentRecord] = useState(null); + // 境内/境外下拉数据 + const [regionOptions, setRegionOptions] = useState([]); + //状态 下拉数据 + const [statusOptions, setStatusOptions] = useState([]); + // 搜索 + const handleSearch = () => { + setPagination({ ...pagination, current: 1 }); + getList(); + }; + // 重置 + const handleReset = () => { + form.resetFields(); + setPagination({ ...pagination, current: 1 }); + getList(); + }; + //列表方法 + const getList = async (page: number = 1, pageSize: number = 10) => { + setLoading(true); + try { + const { code, data, msg, total } = await list({ page, pageSize }); + if (code === 200) { + setData(data); + setPagination({ current: page, pageSize, total }); + } else { + message.error(msg) + } + } finally { + setLoading(false); + } + }; + //初始化 + useEffect(() => { + // 境内/境外 下拉 + systemDict('regionDict').then((res) => { + const { code, data } = res; + if (code == 200) { + setRegionOptions(data) + } + }); + // 状态 下拉 + systemDict('status').then((res) => { + const { code, data } = res; + if (code == 200) { + setStatusOptions(data) + } + }); + //列表 + getList(); + }, []) + // 列表头部数据 + const columns: ColumnsType = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + width: 70, + align: 'center', + render: (_: any, __: any, idx: number) => idx + 1, + }, + { + title: '供应商名称', + dataIndex: 'name', + key: 'name', + align: 'left', + ellipsis: true, + render: (dom, record) => + + {record.name} + , + }, + { + title: '境内/境外', + dataIndex: 'region', + key: 'region', + align: 'center', + }, + { + title: '供应商分类', + dataIndex: 'supplierType', + key: 'supplierType', + align: 'center', + + }, + { + title: '注册时间', + dataIndex: 'regTime', + key: 'regTime', + align: 'center', + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + align: 'center', + render: (val: string) => + {val} + }, + { + title: '操作', + key: 'option', + align: 'center', + render: (record) => ( + + { setCurrentRecord(record); setViewVisible(true); }} + >查看 + { setCurrentRecord(record); setDetailVisible(true); }} + >准入明细 + + ), + }, + ]; + return ( + <> + {/* 查询区 */} +
+ + + + + + + + + + + + + + + + + {/* 列表 */} +
getList(pagination.current!, pagination.pageSize!)} + /> + {/* 查看组件 */} + setViewVisible(false)} + /> + {/* 准入明细组件 */} + setDetailVisible(false)} + /> + + ); +}; +export default connect()(RegistrationQuery); \ No newline at end of file diff --git a/src/pages/supplier/informationRetrieval/registrationQuery/services.ts b/src/pages/supplier/informationRetrieval/registrationQuery/services.ts new file mode 100644 index 0000000..e50e6a3 --- /dev/null +++ b/src/pages/supplier/informationRetrieval/registrationQuery/services.ts @@ -0,0 +1,37 @@ +import request from '@/utils/request'; + + + +export async function systemDict(dict:String) { + return request(`/api/system/${dict}`, { + method: 'GET' + }); +} + + +export interface ListParams { + page: number; + pageSize: number; + captcha?: string; + tmpToken?: string; +} +export async function list(params:ListParams) { + return request(`/api/system/list`, { + method: 'GET', + params + }); +} + +export interface supplierDetailParams { + page: number; + pageSize: number; + captcha?: string; + tmpToken?: string; +} +export async function supplierDetail(params:supplierDetailParams) { + return request(`/api/system/supplierDetail`, { + method: 'GET', + params + }); +} + \ No newline at end of file diff --git a/src/pages/supplier/register/expert.tsx b/src/pages/supplier/register/expert.tsx new file mode 100644 index 0000000..f581335 --- /dev/null +++ b/src/pages/supplier/register/expert.tsx @@ -0,0 +1,241 @@ +// 专家注册 +import React, { useState } from 'react'; +import { useIntl, history } from 'umi'; +import { Form, Input, Button, message, Card, Select, Row, Col } from 'antd'; +import { + UserOutlined, + LockOutlined, + MobileOutlined, + IdcardOutlined, + HomeOutlined, +} from '@ant-design/icons'; +import './register.less'; + +const { Option } = Select; + +const ExpertRegister: React.FC = () => { + const [form] = Form.useForm(); + const intl = useIntl(); + const [loading, setLoading] = useState(false); + const [countdown, setCountdown] = useState(0); + + // 获取短信验证码 + const handleGetCaptcha = () => { + form + .validateFields(['phone']) + .then((values) => { + message.success(`验证码已发送至 ${values.phone}`); + let count = 60; + setCountdown(count); + + const timer = setInterval(() => { + count--; + setCountdown(count); + if (count === 0) { + clearInterval(timer); + } + }, 1000); + }) + .catch((errorInfo) => { + message.error('请先输入正确的手机号'); + }); + }; + + const onFinish = (values: any) => { + setLoading(true); + console.log('专家注册信息:', values); + // 这里添加注册逻辑 + setTimeout(() => { + setLoading(false); + message.success('注册成功,请登录'); + history.push('/login'); + }, 1000); + }; + + return ( +
+
+ + +
{intl.formatMessage({ id: 'register.expert.title' })}
+ +
+ + } + placeholder={intl.formatMessage({ id: 'register.username.placeholder' })} + /> + + + + } + placeholder={intl.formatMessage({ id: 'register.phone.placeholder' })} + /> + + + + + + + + } + placeholder={intl.formatMessage({ id: 'register.expert.idCard.placeholder' })} + /> + + + + } + placeholder={intl.formatMessage({ id: 'register.password.placeholder' })} + /> + + + ({ + validator(_, value) { + if (!value || getFieldValue('password') === value) { + return Promise.resolve(); + } + return Promise.reject( + new Error(intl.formatMessage({ id: 'register.confirmPassword.notMatch' })), + ); + }, + }), + ]} + > + } + placeholder={intl.formatMessage({ id: 'register.confirmPassword.placeholder' })} + /> + + + + +
+ + + + + + + + + + + + + + + + ); +}; + +export default ExpertRegister; diff --git a/src/pages/supplier/register/register.less b/src/pages/supplier/register/register.less new file mode 100644 index 0000000..4a038b7 --- /dev/null +++ b/src/pages/supplier/register/register.less @@ -0,0 +1,108 @@ +// @import '~@/baseStyle.less'; + +// 注册页面整体布局 +.register-page { + // display: flex; + // justify-content: center; + // align-items: center; + // min-height: 100vh; + // background: #f0f2f5; + // // background-image: url('~@/assets/img/loginBg.jpg'); + // background-size: cover; + // background-position: center; + // position: relative; +} + +// 注册容器 +.register-container { + &.large-width{ + width: 100%; + + } +} + +// 标题样式 +.register-title { + margin-bottom: 20px; + // color: @main-color; + font-weight: bold; + font-size: 24px; + text-align: center; +} + +// 返回首页链接 +.back-home { + position: absolute; + top: 10px; + left: 10px; + + a { + display: flex; + align-items: center; + // color: @main-color; + font-size: 14px; + + &:hover { + // color: lighten(@main-color, 10%); + } + + .anticon { + margin-right: 4px; + } + } +} +.form-section-title { + font-size: 16px; + font-weight: 600; + margin: 20px 0 16px 0; + padding-left: 12px; + border-left: 4px solid #1890ff; +} + +.questionnaire-header, +.questionnaire-questions { + font-weight: 500; + margin: 10px 0; + padding: 5px 0; +} + +.question-text { + margin-bottom: 10px; + color: #333; +} + +.commitment-upload { + padding: 15px; + background-color: #f9f9f9; + border-radius: 4px; +} + +.qualification-table { + .ant-table-thead > tr > th { + background-color: #f0f5ff; + font-weight: 500; + } + + .ant-form-item { + margin-bottom: 0; + } + + .ant-table-cell { + padding: 8px; + } +} + +.questionnaire-header { + font-size: 15px; + font-weight: 500; + margin: 15px 0 10px 0; + color: rgba(0, 0, 0, 0.85); +} + + +.register-form{ + height: 75vh; + overflow-x: hidden; + overflow-y: auto; + padding-right: 31px; +} \ No newline at end of file diff --git a/src/pages/supplier/register/supplier.tsx b/src/pages/supplier/register/supplier.tsx new file mode 100644 index 0000000..9d52efd --- /dev/null +++ b/src/pages/supplier/register/supplier.tsx @@ -0,0 +1,226 @@ +// 供应商注册 +import React, { useState, useEffect } from 'react'; +import { useIntl, history } from 'umi'; +import { Form, Button, message, Radio, Checkbox, Modal, Spin } from 'antd'; +import { HomeOutlined } from '@ant-design/icons'; +import DomesticForm from './supplier/DomesticForm'; +import ForeignForm from './supplier/ForeignForm'; +// import { coscoSupplierBaseAdd, fetchSurveyQuestions } from '@/servers/api/register'; +import './register.less'; + +const SupplierRegister: React.FC = () => { + const [form] = Form.useForm(); + const intl = useIntl(); + const [supplierType, setSupplierType] = useState('dvs'); + const [loading, setLoading] = useState(false); + const [countdown, setCountdown] = useState(0); + const [modalVisible, setModalVisible] = useState(false); + const [surveyQuestions, setSurveyQuestions] = useState([]); + const [fetchingQuestions, setFetchingQuestions] = useState(false); + + // 获取问卷列表 + useEffect(() => { + // const fetchQuestions = async () => { + // setFetchingQuestions(true); + // try { + // const response = await fetchSurveyQuestions(); + // if (response.success) { + // setSurveyQuestions(response.data || []); + // } else { + // message.error(response.message || '获取问卷列表失败'); + // } + // } catch (error) { + // console.error('获取问卷列表出错:', error); + // message.error('获取问卷列表出错'); + // } finally { + // setFetchingQuestions(false); + // } + // }; + + // fetchQuestions(); + }, []); + + // 获取短信验证码 + const handleGetCaptcha = () => { + form + .validateFields(['contactPhone']) + .then((values) => { + message.success(`验证码已发送至 ${values.contactPhone}`); + let count = 60; + setCountdown(count); + + const timer = setInterval(() => { + count--; + setCountdown(count); + if (count === 0) { + clearInterval(timer); + } + }, 1000); + }) + .catch((errorInfo) => { + message.error('请先输入正确的手机号'); + }); + }; + + const onFinish = async (values: any) => { + setLoading(true); + try { + // 确保供应商类型添加到表单值中 + values.coscoSupplierBase = values.coscoSupplierBase || {}; + values.coscoSupplierBase.supplierType = supplierType; + + console.log('供应商注册信息:', values); + + // // 直接调用API + // const response = await coscoSupplierBaseAdd(values); + + // if (response.success) { + // message.success('注册成功,请登录'); + // history.push('/login'); + // } else { + // message.error(response.message || '注册失败,请重试'); + // } + } catch (error) { + console.error('注册出错:', error); + message.error('注册失败,请稍后重试'); + } finally { + setLoading(false); + } + }; + + const handleSupplierTypeChange = (e: any) => { + form.resetFields(); + setSupplierType(e.target.value); + }; + + return ( +
+
+ {/* */} + + {/*
+ {intl.formatMessage({ id: 'register.supplier.title' })} +
*/} + + +
+ {/* + + 境内企业/机构 + 境外企业 + + */} + + {supplierType === 'dvs' ? ( + + ) : ( + + )} + {/* + value + ? Promise.resolve() + : Promise.reject(new Error('请阅读并同意注册信息承诺书')), + }, + ]} + > + + 我已阅读并同意 + + + */} + +
+ +
+
+ +
+
+ + {/* 注册信息承诺书弹窗 */} + { + console.log('点击了确定按钮'); + setModalVisible(false); + }} + onCancel={() => { + console.log('点击了取消按钮'); + setModalVisible(false); + }} + width={700} + okText="我知道了" + cancelButtonProps={{ style: { display: 'none' } }} + destroyOnClose + // maskClosable={false} + > +
+

尊敬的用户:

+

感谢您注册使用我们的平台。请您仔细阅读以下承诺内容:

+
    +
  1. + 本人/单位承诺所提供的注册信息真实、准确、完整,不存在虚假记载、误导性陈述或重大遗漏。 +
  2. +
  3. 本人/单位承诺遵守平台的各项规则和要求,不从事任何违法违规活动。
  4. +
  5. + 本人/单位了解并同意,如提供虚假信息或违反平台规则,平台有权终止服务并追究相关责任。 +
  6. +
  7. 本人/单位承诺妥善保管账号和密码,对账号下的所有操作和行为负责。
  8. +
  9. 本人/单位同意平台在必要范围内合法使用所提供的信息,用于提供服务和改善用户体验。
  10. +
  11. + 本人/单位承诺遵守国家法律法规和行业规范,恪守商业道德,不从事任何损害平台或其他用户利益的行为。 +
  12. +
  13. 本人/单位承诺所上传的资质文件和证明材料真实有效,并对其真实性和合法性负责。
  14. +
  15. 本人/单位了解并同意,平台有权根据业务需要对用户资料进行审核,并保留最终解释权。
  16. +
+

特此承诺!

+
+
+
+ ); +}; + +export default SupplierRegister; diff --git a/src/pages/supplier/register/supplier/CommonFormSections.tsx b/src/pages/supplier/register/supplier/CommonFormSections.tsx new file mode 100644 index 0000000..66f8b98 --- /dev/null +++ b/src/pages/supplier/register/supplier/CommonFormSections.tsx @@ -0,0 +1,770 @@ +/** + * 供应商注册表单通用部分 + * 封装了国内企业和境外企业注册表单相同的部分 + */ +import React from 'react'; +import { + Form, + Input, + Button, + Select, + Upload, + DatePicker, + Row, + Col, + Table, + Radio, + Cascader, + Empty, +} from 'antd'; +import { UploadOutlined, PlusOutlined, DeleteOutlined } from '@ant-design/icons'; +import { message } from 'antd'; +import { validateFileSize } from '@/utils/utils'; + +const { Option } = Select; + +// 中国省市区级联数据 +export const addressOptions = [ + { + value: '330000', + label: '浙江省', + children: [ + { + value: '330100', + label: '杭州市', + children: [ + { value: '330102', label: '上城区' }, + { value: '330103', label: '下城区' }, + { value: '330104', label: '江干区' }, + { value: '330105', label: '拱墅区' }, + { value: '330106', label: '西湖区' }, + { value: '330108', label: '滨江区' }, + ], + }, + ], + }, + { + value: '310000', + label: '上海市', + children: [ + { + value: '310100', + label: '上海市', + children: [ + { value: '310101', label: '黄浦区' }, + { value: '310104', label: '徐汇区' }, + { value: '310105', label: '长宁区' }, + ], + }, + ], + }, +]; + +interface CommonFormSectionsProps { + form: any; +} + +// 扩展问卷部分的属性接口 +interface SurveySectionProps extends CommonFormSectionsProps { + surveyQuestions?: API.SurveyQuestionResponse; +} + +/** + * 资质信息表单部分 + * 包含资质证书类型、名称、编号、等级、发证机构、发证日期、有效期等 + */ +export const QualificationSection: React.FC = ({ form }) => { + return ( + <> +
资质信息
+ + + {(fields, { add, remove }) => ( + <> +
({ + ...field, + key: field.key, + fieldKey: field.name, + }))} + pagination={false} + bordered + size="middle" + rowKey="key" + columns={[ + { + title: '序号', + dataIndex: 'name', + width: 60, + render: (_, __, index) => index + 1, + }, + { + title: '资质证书类型', + dataIndex: 'certType', + render: (_, record) => ( + + + + ), + }, + { + title: '资质名称', + dataIndex: 'certName', + render: (_, record) => ( + + + + ), + }, + { + title: '资质证书编号', + dataIndex: 'certNumber', + render: (_, record) => ( + + + + ), + }, + { + title: '资质类别和等级', + dataIndex: 'certLevel', + render: (_, record) => ( + + + + ), + }, + { + title: '发证机构', + dataIndex: 'issuingAuthority', + render: (_, record) => ( + + + + ), + }, + { + title: '发证日期', + dataIndex: 'issueDate', + render: (_, record) => ( + + + + ), + }, + { + title: '资质有效期至', + dataIndex: 'expiryDate', + render: (_, record) => ( + + + + ), + }, + { + title: '附件', + dataIndex: 'certFile', + render: (_, record) => ( + + + + + + ), + }, + { + title: '操作', + width: 70, + render: (_, record) => ( + + ), + }, + ]} + /> + + + + + )} + + + ); +}; + +/** + * 开票信息表单部分 + * 包含纳税人类型、开票抬头、纳税人识别号、开票地址等 + */ +export const InvoiceSection: React.FC = ({ form }) => { + return ( + <> +
开票信息
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + validateFileSize(file, 10, ['pdf', 'jpg', 'jpeg', 'png'])} + > + + + + + + + ); +}; + +/** + * 银行账户表单部分 + * 包含开户银行、账户名称、账号、所在地区等 + */ +export const BankAccountSection: React.FC = ({ form }) => { + return ( + <> +
银行账户
+ + + {(fields, { add, remove }) => ( + <> +
({ + ...field, + key: field.key, + fieldKey: field.name, + }))} + pagination={false} + bordered + size="middle" + rowKey="key" + columns={[ + { + title: '序号', + dataIndex: 'name', + width: 60, + render: (_, __, index) => index + 1, + }, + { + title: '开户银行', + dataIndex: 'bankName', + render: (_, record) => ( + + + + ), + }, + { + title: '账户名称', + dataIndex: 'accountName', + render: (_, record) => ( + + + + ), + }, + { + title: '账号', + dataIndex: 'accountNumber', + render: (_, record) => ( + + + + ), + }, + { + title: '国家、省、市', + dataIndex: 'location', + render: (_, record) => ( + + { + return path.some((option) => { + if (typeof option.label === 'string') { + return ( + option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1 + ); + } + return false; + }); + }, + }} + /> + + ), + }, + { + title: '操作', + width: 70, + render: (_, record) => ( + + ), + }, + ]} + /> + + + + + )} + + + ); +}; + +/** + * 社会准则符合性自查问卷部分 + * 包含填写人信息和问卷内容 + */ +export const SurveySection: React.FC = ({ form, surveyQuestions }) => { + // 使用API获取的问卷数据,如果没有则显示无数据状态 + const hasQuestions = surveyQuestions && surveyQuestions.length > 0; + + return ( + <> +
社会准则符合性自查问卷
+ +
填写人信息:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 问卷: +
+ + {hasQuestions ? ( + + {(fields, { add, remove }) => { + // 确保有足够的表单项对应每个问题 + if (fields.length < surveyQuestions.length) { + const diff = surveyQuestions.length - fields.length; + for (let i = 0; i < diff; i++) { + add(); + } + } + + return ( +
index + 1, + }, + { + title: '问题', + dataIndex: 'questionName', + render: (text) =>
{text}
, + }, + { + title: '回复', + width: 650, + render: (_, record, index) => ( + <> + + + + {record.coscoSurveyQuestionOptionList?.map((option: any) => ( + + {option.optionName} + + ))} + + + + ), + }, + ]} + /> + ); + }} + + ) : ( +
+ +
+ )} + + ); +}; + +/** + * 供应商反商业贿赂承诺书和其他附件部分 + */ +export const AttachmentSection: React.FC = ({ form }) => { + return ( + <> +
供应商反商业贿赂承诺书
+ +
+
+ 请加盖公司公章后上传 + +
+ + + {(fields, { add, remove }) => { + // 确保至少有一项用于反商业贿赂承诺书 + if (fields.length === 0) { + add({ attachmentsType: 'commitment' }); + } + + return ( +
+ {fields.map((field, index) => ( +
+ + + + + validateFileSize(file, 10, ['pdf', 'doc', 'docx']) + } + onChange={(info) => { + if (info.file.status === 'done') { + const response = info.file.response; + if (response && response.success) { + // 填充文件信息 + form.setFieldsValue({ + coscoSupplierSurveyAttachments: [ + { + ...form.getFieldValue([ + 'coscoSupplierSurveyAttachments', + field.name, + ]), + fileName: info.file.name, + fileType: info.file.type, + fileSize: info.file.size.toString(), + filePath: response.filePath || response.url, + }, + ], + }); + message.success(`${info.file.name} 上传成功`); + } else { + message.error(`${info.file.name} 上传失败`); + } + } + }} + > + + + +
+ ))} +
+ ); + }} +
+ + + +
其他附件
+ + + + {(fields, { add, remove }) => ( + <> +
其他附件(非必须上传)
+ {fields.map((field, index) => ( +
0 || fields.length === 1 ? 'block' : 'none', + marginBottom: 8, + }} + > + {index > 0 && ( + + )} + + {index > 0 && ( + + validateFileSize(file, 20, ['*'])} + onChange={(info) => { + if (info.file.status === 'done') { + const response = info.file.response; + if (response && response.success) { + // 填充文件信息 + const fieldValue = form.getFieldValue([ + 'coscoSupplierSurveyAttachments', + field.name, + ]); + form.setFieldsValue({ + coscoSupplierSurveyAttachments: [ + { + ...fieldValue, + fileName: info.file.name, + fileType: info.file.type, + fileSize: info.file.size.toString(), + filePath: response.filePath || response.url, + }, + ], + }); + message.success(`${info.file.name} 上传成功`); + } else { + message.error(`${info.file.name} 上传失败`); + } + } + }} + > + + + + )} +
+ ))} + + + + )} +
+ + + + ); +}; diff --git a/src/pages/supplier/register/supplier/DomesticForm.tsx b/src/pages/supplier/register/supplier/DomesticForm.tsx new file mode 100644 index 0000000..0748663 --- /dev/null +++ b/src/pages/supplier/register/supplier/DomesticForm.tsx @@ -0,0 +1,282 @@ +/* 境内企业/机构 表单项 */ +import React from 'react'; +import { Form, Input, Button, Select, Upload, DatePicker, Row, Col, message } from 'antd'; +import { + MobileOutlined, + MailOutlined, + EnvironmentOutlined, + UploadOutlined, +} from '@ant-design/icons'; + +/** + * 引入通用表单组件 + */ +import { + QualificationSection, + InvoiceSection, + BankAccountSection, + SurveySection, + AttachmentSection, +} from './CommonFormSections'; +import { validateFileSize } from '@/utils/utils'; + +const { Option } = Select; +const { TextArea } = Input; + +// 移除不再需要的addressOptions + +interface DomesticFormProps { + form: any; + countdown: number; + handleGetCaptcha: () => void; + surveyQuestions?: any; // 本身就是数组类型 +} + +/** + * 境内企业注册表单 + * 基本信息部分为境内企业特有 + * 其他部分使用通用表单组件 + */ +const DomesticForm: React.FC = ({ + form, + countdown, + handleGetCaptcha, + surveyQuestions, +}) => { + return ( + <> +
基本信息
+ + {/* 营业执照附件和有效期 */} + +
+ + validateFileSize(file, 10, ['pdf', 'jpg', 'jpeg', 'png'])} + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } placeholder="上海市普陀区XX路1888号" /> + + + + + } + placeholder="请具体注明省、市、区、路、门牌号" + /> + + + + + + + + + + } placeholder="请输入11位手机号码" /> + + + { false && ( + + + + + + + + + + + + + )} + + + + + + + + + + + + + + } placeholder="请输入企业联系电话" /> + + + + + + + + + +