From 4acff1e030f932430126759611cbb982fde425ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E6=99=AF=E5=AD=A6?= <5412262+sun_jing_xue@user.noreply.gitee.com> Date: Thu, 7 Aug 2025 09:22:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A9=E7=9C=BC=E6=9F=A5=E4=B8=8E=E5=87=86?= =?UTF-8?q?=E5=85=A5=E5=AE=A1=E6=A0=B8=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/router.config.ts | 4 + src/app.ts | 2 +- src/components/GlobalModal/index.tsx | 54 ++- .../SupplierDetail.tsx | 50 ++- .../ViewReviewPage/GlobalModal/_mock.ts | 277 +++++++++++++ .../components/AccessCategoryTable.tsx | 210 ++++++++++ .../GlobalModal/components/ContactsInfo.tsx | 122 ++++++ .../GlobalModal/components/RiskList.tsx | 87 ++++ .../components/SupplierRegisterInfo.tsx | 384 ++++++++++++++++++ .../GlobalModal/components/TianyanchaInfo.tsx | 136 +++++++ .../ViewReviewPage/GlobalModal/index.tsx | 142 +++++++ .../ViewReviewPage/GlobalModal/services.ts | 67 +++ .../ViewReviewPage/components/ResultModal.tsx | 263 ++++++++++++ .../ViewReviewPage/components/ViewModal.tsx | 161 ++++++++ src/pages/supplier/ViewReviewPage/index.tsx | 41 ++ src/pages/supplier/ViewReviewPage/services.ts | 126 ++++++ 16 files changed, 2085 insertions(+), 41 deletions(-) create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/_mock.ts create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/components/AccessCategoryTable.tsx create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/components/ContactsInfo.tsx create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/components/RiskList.tsx create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/components/SupplierRegisterInfo.tsx create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/components/TianyanchaInfo.tsx create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/index.tsx create mode 100644 src/pages/supplier/ViewReviewPage/GlobalModal/services.ts create mode 100644 src/pages/supplier/ViewReviewPage/components/ResultModal.tsx create mode 100644 src/pages/supplier/ViewReviewPage/components/ViewModal.tsx create mode 100644 src/pages/supplier/ViewReviewPage/index.tsx create mode 100644 src/pages/supplier/ViewReviewPage/services.ts diff --git a/config/router.config.ts b/config/router.config.ts index 613fdf2..c2e577a 100644 --- a/config/router.config.ts +++ b/config/router.config.ts @@ -3,6 +3,10 @@ export default [ path: '/login', component: '@/pages/login/login', }, + { + path: '/ViewReviewPage', + component: '@/pages/supplier/ViewReviewPage/index', + }, { path: '/register', routes: [ diff --git a/src/app.ts b/src/app.ts index b2f304e..81ea0d6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,7 +2,7 @@ import { history } from 'umi'; export function onRouteChange({ location }: any) { const token = sessionStorage.getItem('token'); - const whiteList = ['/login', '/forgot', '/register/supplier', '/register/expert', '/403', '/404']; + const whiteList = ['/login', '/forgot', '/register/supplier', '/register/expert', '/403', '/404', '/ViewReviewPage']; if (!token && !whiteList.includes(location.pathname)) { history.replace('/login'); } diff --git a/src/components/GlobalModal/index.tsx b/src/components/GlobalModal/index.tsx index a074840..9aec395 100644 --- a/src/components/GlobalModal/index.tsx +++ b/src/components/GlobalModal/index.tsx @@ -78,7 +78,7 @@ const GlobalModal = ({ visible, id, dispatch }: any) => { } > - {(registerInfo?.coscoSupplierBase.supplierType !== 'pe' && registerInfo ) ? + {(registerInfo?.coscoSupplierBase.supplierType !== 'pe' && registerInfo) ? (
@@ -86,33 +86,43 @@ const GlobalModal = ({ visible, id, dispatch }: any) => { - +
{renderContent()}
) : ( - - - {registerInfo?.coscoSupplierBase.personName} - - - {registerInfo?.coscoSupplierBase.idCard} - - - {registerInfo?.coscoSupplierBase.personPhone} - - - {registerInfo?.coscoSupplierBase.personBank} - - - {registerInfo?.coscoSupplierBase.personAccount} - - {/* - {registerInfo?.coscoSupplierBase.accessory} - */} - +
+ + + + +
+ {modalType === 'category' && ( + + )} + {modalType === 'register' && ( + + + {registerInfo?.coscoSupplierBase?.personName} + + + {registerInfo?.coscoSupplierBase?.idCard} + + + {registerInfo?.coscoSupplierBase?.personPhone} + + + {registerInfo?.coscoSupplierBase?.personBank} + + + {registerInfo?.coscoSupplierBase?.personAccount} + + + )} +
+
) } diff --git a/src/components/SupplierDetailModalContext/SupplierDetail.tsx b/src/components/SupplierDetailModalContext/SupplierDetail.tsx index 6c4b8ec..aa0e1bf 100644 --- a/src/components/SupplierDetailModalContext/SupplierDetail.tsx +++ b/src/components/SupplierDetailModalContext/SupplierDetail.tsx @@ -73,30 +73,44 @@ const SupplierDetail: React.FC = ({ supplierId }) => { - +
{renderContent()}
) : ( - - - {registerInfo?.coscoSupplierBase?.personName} - - - {registerInfo?.coscoSupplierBase?.idCard} - - - {registerInfo?.coscoSupplierBase?.personPhone} - - - {registerInfo?.coscoSupplierBase?.personBank} - - - {registerInfo?.coscoSupplierBase?.personAccount} - - +
+ + + + +
+ { modalType === 'category' && ( + + ) } + {modalType === 'register' && ( + + + {registerInfo?.coscoSupplierBase?.personName} + + + {registerInfo?.coscoSupplierBase?.idCard} + + + {registerInfo?.coscoSupplierBase?.personPhone} + + + {registerInfo?.coscoSupplierBase?.personBank} + + + {registerInfo?.coscoSupplierBase?.personAccount} + + + )} +
+
+ ) ); }; diff --git a/src/pages/supplier/ViewReviewPage/GlobalModal/_mock.ts b/src/pages/supplier/ViewReviewPage/GlobalModal/_mock.ts new file mode 100644 index 0000000..2d67325 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/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/pages/supplier/ViewReviewPage/GlobalModal/components/AccessCategoryTable.tsx b/src/pages/supplier/ViewReviewPage/GlobalModal/components/AccessCategoryTable.tsx new file mode 100644 index 0000000..5da0bd8 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/GlobalModal/components/AccessCategoryTable.tsx @@ -0,0 +1,210 @@ +import React, { useEffect, useState } from 'react'; +import { Table, Tabs, message } from 'antd'; +import { useIntl } from 'umi'; +import { getCategoryPage, supplierIdPage } from '../services'; +// 类型定义 +import type { ColumnsType } from 'antd/es/table'; + +//统一列表分页 +import tableProps from '@/utils/tableProps' +const { TabPane } = Tabs; +interface Data { + id: number; + storeName: string; + category: string; + region: string; + unit: string; + owner: string; + validTo: string; + supplierCount: number; +} +const AccessCategoryTable = ({id}:{id:string}) => { + const intl = useIntl(); + const columns = [ + { + title: '准入单位', + dataIndex: 'orgName', + key: 'orgName', + ellipsis: true + }, + { + title: '准入部门', + dataIndex: 'deptName', + key: 'deptName', + ellipsis: true + }, + { + title: '准入品类', + dataIndex: 'categoryName', + key: 'categoryName', + }, + { + title: '准入时间', + dataIndex: 'updateTime', + key: 'updateTime', + }, + { + title: '退出时间', + dataIndex: 'exitTime', + key: 'exitTime', + }, + { + title: '退出原因', + dataIndex: 'exitReason', + key: 'exitReason', + ellipsis: true, + width: 120, + } + ] + //品类 + const categoryColumns: ColumnsType = [ + { + title: '品类库名称', + dataIndex: 'name', + key: 'name', + align: 'center', + ellipsis: true, + }, + { + title: '品类', + dataIndex: 'categoryNames', + key: 'categoryNames', + align: 'center', + width: 100, + ellipsis: true, + }, + { + title: '区域', + dataIndex: 'area', + key: 'area', + align: 'center', + ellipsis: true, + }, + { + title: '创建单位', + dataIndex: 'deptName', + key: 'deptName', + align: 'center', + ellipsis: true, + }, + { + title: '负责人', + dataIndex: 'createName', + key: 'createName', + align: 'center', + }, + { + title: '有效期至', + dataIndex: 'termOfValidity', + key: 'termOfValidity', + align: 'center', + ellipsis: true, + }, + ] + + //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 } = await getCategoryPage({ pageNo: current, pageSize, supplierId:id }); + if (code === 200) { + setAccessData(data.records); + setAccessPagination({ current, pageSize, total: data.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 } = await supplierIdPage({ basePageRequest: { pageNo: current, pageSize } , supplierId: id }); + if (code === 200) { + setCategoryData(data.records); + setCategoryPagination({ current, pageSize, total: data.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), + // }} + pagination={{...tableProps.pagination, total: accessPagination.total }} + onChange={(pagination) => { + fetchCategoryData( pagination.current!, pagination.pageSize!) + }} + style={{ flex: 1, minHeight: 0 }} + scroll={{ y: 'calc(100vh - 350px)' }} + /> + + + +
fetchCategoryData(page, pageSize), + // }} + + pagination={{...tableProps.pagination, total: categoryPagination.total }} + onChange={(pagination) => { + fetchCategoryData( pagination.current!, pagination.pageSize!) + }} + style={{ flex: 1, minHeight: 0 }} + scroll={{ y: 'calc(100vh - 350px)' }} + /> + + + ); +}; + +export default AccessCategoryTable; diff --git a/src/pages/supplier/ViewReviewPage/GlobalModal/components/ContactsInfo.tsx b/src/pages/supplier/ViewReviewPage/GlobalModal/components/ContactsInfo.tsx new file mode 100644 index 0000000..d758b82 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/GlobalModal/components/ContactsInfo.tsx @@ -0,0 +1,122 @@ +import React, { useEffect, useState } from 'react'; +import { Table, Tooltip } from 'antd'; +import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; +import { getCoscoSupplierUserPage } from '../services'; +import { useIntl } from 'umi'; + +interface getCoscoSupplierUser { + id: string; + attachmentsType?: string; + fileName?: string; + filePath?: string; + fileSize?: string; + fileType?: string; + fileUrl?: string; + supplierId?: string; + certificateUrl?: string; + delFlag: string; + type: string; +} + +interface Props { + id?: string; +} +const OtherAttachmentsTab: React.FC = ({ id }) => { + //语言切换 + 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 (pageNo = 1, pageSize = 10) => { + setLoading(true); + try { + const { code, data } = await getCoscoSupplierUserPage({ pageNo, pageSize, supplierId: id }); + if (code === 200) { + setData(data.records); + setPagination({ current: pageNo, pageSize, total: data.total }); + } + } finally { + setLoading(false); + } + }; + + + //初始化 + useEffect(() => { + if (id) { + getList(); + } + }, [id]); + + + const columns: ColumnsType = [ + { + title: intl.formatMessage({ id: 'page.workbench.attachments.index' }), + render: (_: any, __: any, index: number) => index + 1, + width: 60, + }, + { + title: '联系人', + dataIndex: 'contactsName', + key: 'contactsName', + }, + { + title: '手机号', + dataIndex: 'contactsPhone', + key: 'contactsPhone', + }, + { + title: '邮箱', + dataIndex: 'contactsEmail', + key: 'contactsEmail', + }, + { + title: '负责品类', + dataIndex: 'coscoSupplierUserCategoryList', + key: 'coscoSupplierUserCategoryList', + ellipsis: true, + width: 160, + render: (value: { categoryName: string; categoryPathName: string }[] = []) => { + if (!value || value.length === 0) return '-'; + + // 多于1条 + const allNames = value.map(item => item.categoryPathName).join('\n'); + return ( + {allNames}} overlayStyle={{ zIndex: 1200 }}> + + {value[0].categoryName} + {value.length !== 1 && ( + + )} + + + ); + }, + }, + + ]; + return ( +
+
({ + ...column, + title: column.title + }))} + dataSource={data} + pagination={pagination} + loading={loading} + onChange={(pagination) => getList(pagination.current!, pagination.pageSize!)} + /> + + + ); +}; + +export default OtherAttachmentsTab; \ No newline at end of file diff --git a/src/pages/supplier/ViewReviewPage/GlobalModal/components/RiskList.tsx b/src/pages/supplier/ViewReviewPage/GlobalModal/components/RiskList.tsx new file mode 100644 index 0000000..f3897be --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/GlobalModal/components/RiskList.tsx @@ -0,0 +1,87 @@ +import React, { useEffect, useState } from 'react'; +import { Descriptions, message } from 'antd'; +import { queryRiskInfo } from '../services' +import { useIntl } from 'umi'; +import type { ColumnsType } from 'antd/es/table'; + +interface data { + hit: string; + countries: string; + d1: string; + d2: string; + [key: string]: any; +} + + +const RiskList = ({ id }: { id: string }) => { + const intl = useIntl(); + const columns: ColumnsType = [ + { + title: intl.formatMessage({ id: 'component.globalModal.hit', defaultMessage: '命中对象' }), + dataIndex: 'hit', + key: 'hit', + }, + { + title: intl.formatMessage({ id: 'component.globalModal.countries', defaultMessage: '涉及国家/地区' }), + dataIndex: 'countries', // 推荐拼写为 countries + key: 'countries', + }, + { + title: intl.formatMessage({ id: 'component.globalModal.riskD1', defaultMessage: '风险主类' }), + dataIndex: 'd1', + key: 'd1', + }, + { + title: intl.formatMessage({ id: 'component.globalModal.riskD2', defaultMessage: '风险分类' }), + dataIndex: 'd2', + key: 'd2', + }, + ] + //合规风险 + const [dataList, setDataList] = useState([]) + //加载 const [loading, setLoading] = useState(false); + + //分页 + + //数据渲染 + const getList = async () => { + // setLoading(true); + try { + const { code, data } = await queryRiskInfo({ supplierId: id }); + if (code === 200) { + setDataList(data); + } else { + message.error(intl.formatMessage({ id: 'component.globalModal.fetchRiskFail' })); + } + } catch (error) { + message.error(intl.formatMessage({ id: 'component.globalModal.apiError' })); + } finally { + // setLoading(false); + } + }; + //初始化 + useEffect(() => { + if (id) { + getList(); + } + }, [id]); + + return ( + <> + { + dataList.map((item, idx) => ( + + {columns.map(col => ( + + {item[(col as any).dataIndex] ?? '-'} + + ))} + + )) + } + + + ); +}; + +export default RiskList; diff --git a/src/pages/supplier/ViewReviewPage/GlobalModal/components/SupplierRegisterInfo.tsx b/src/pages/supplier/ViewReviewPage/GlobalModal/components/SupplierRegisterInfo.tsx new file mode 100644 index 0000000..6c0f13b --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/GlobalModal/components/SupplierRegisterInfo.tsx @@ -0,0 +1,384 @@ +import React, { useEffect, useState } from 'react'; +import { Descriptions, Table, Spin } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import { useIntl } from 'umi'; +import { getDictList } from '@/servers/api/dicts'; +import { dictRegion } from '@/servers/api/user' +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 codeNameCache = new Map(); +const fetchRegionNames = async (codes: string[]) => { + const waitCodes = codes.filter(code => code && !codeNameCache.has(code)); + if (waitCodes.length === 0) return; + // 批量接口推荐你后端支持,单个请求也兼容如下 + await Promise.all(waitCodes.map(async (code) => { + try { + const { code: status, data } = await dictRegion(code); + if (status === 200 && data && data.name) { + codeNameCache.set(code, data.name); + } + } catch { /* ignore */ } + })); +}; + +const SupplierRegisterInfo = ({ registerInfo }: { registerInfo: any }) => { + //币种 + const [currencyMap, setCurrencyMap] = useState<{ [code: string]: string }>({}); + const [enterpriseTypeMap, setEnterpriseTypeMap] = useState<{ [code: string]: string }>({}); + + const [contactsTypeMap, setContactsTypeMap] = useState<{ [code: string]: string }>({}); + const [taxpayerTypeMap, setTaxpayerTypeMap] = useState<{ [code: string]: string }>({}); + const [regionLoading, setRegionLoading] = useState(false); + const [, forceUpdate] = useState({}); // 用于触发重新渲染 + + useEffect(() => { + getDictList('currency').then((res) => { + if (res.code === 200) { + const map: { [code: string]: string } = {}; + res.data.forEach((item: { code: string, dicName: string }) => { + map[item.code] = item.dicName; + }); + setCurrencyMap(map); + } + }); + getDictList('taxpayer_type').then((res) => { + if (res.code == 200) { + const map: { [code: string]: string } = {}; + res.data.forEach((item: { code: string, dicName: string }) => { + map[item.code] = item.dicName; + }); + setTaxpayerTypeMap(map); + } + }) + getDictList('contacts_type').then((res) => { + if (res.code === 200) { + const map: { [code: string]: string } = {}; + res.data.forEach((item: { code: string, dicName: string }) => { + map[item.code] = item.dicName; + }); + setContactsTypeMap(map); + } + }); + + getDictList('enterprise_type').then((res) => { + if (res.code === 200) { + const map: { [code: string]: string } = {}; + res.data.forEach((item: { code: string, dicName: string }) => { + map[item.code] = item.dicName; + }); + setEnterpriseTypeMap(map); + } + }); + + }, []); + useEffect(() => { + if (!Array.isArray(registerInfo?.coscoSupplierBank)) return; + // 收集所有 code + const codes: string[] = []; + registerInfo.coscoSupplierBank.forEach((item: BankInfo) => { + if (item.nation) codes.push(item.nation); + if (item.province) codes.push(item.province); + if (item.city) codes.push(item.city); + }); + setRegionLoading(true); + fetchRegionNames(codes).then(() => { + setRegionLoading(false); + forceUpdate({}); + }); + }, [registerInfo?.coscoSupplierBank]); + // 通用渲染 + const renderRegionName = (code: string) => { + if (!code) return ''; + if (codeNameCache.has(code)) { + return codeNameCache.get(code); + } + return ; + }; + + 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', render: (code: string) => currencyMap[code] || code }, + { title: intl.formatMessage({ id: 'component.globalModal.country' }), dataIndex: 'nation', key: 'nation', render: renderRegionName }, + { title: intl.formatMessage({ id: 'component.globalModal.province' }), dataIndex: 'province', key: 'province', render: renderRegionName }, + { title: intl.formatMessage({ id: 'component.globalModal.city' }), dataIndex: 'city', key: 'city', render: renderRegionName }, + ]; + if (!registerInfo) return
{intl.formatMessage({ id: 'component.globalModal.loading' })}...
; + + return ( + <> + + {registerInfo.coscoSupplierBase.supplierType === 'dvs' && ( + <> + + {intl.formatMessage({ id: 'component.globalModal.domesticEnterprise' })} + + + {registerInfo.coscoSupplierBase.licenceAccessory ? ( + + 营业执照 + + ) : ( + 无附件 + )} + + + )} + + {registerInfo.coscoSupplierBase.name} + + + {registerInfo.coscoSupplierBase.nameEn} + + {registerInfo.coscoSupplierBase.supplierType !== 'dvs' && ( + <> + + {registerInfo.coscoSupplierBase.nation} + + + {registerInfo.coscoSupplierBase.vat} + + + {registerInfo.coscoSupplierBase.taxpayerId} + + + {registerInfo.coscoSupplierBase.currency} + + + )} + + {registerInfo.coscoSupplierBase.range} + + + {registerInfo.coscoSupplierBase.workAddress} + + + {registerInfo.coscoSupplierBase.parentCompanyInvestor} + + + {registerInfo.coscoSupplierBase.legalPerson} + + + {registerInfo.coscoSupplierBase.capital} + + {registerInfo.coscoSupplierBase.supplierType === 'dvs' && ( + <> + + {registerInfo.coscoSupplierBase.socialCreditCode} + + + {registerInfo.coscoSupplierBase.regAddress} + + + {contactsTypeMap[registerInfo.coscoSupplierBase.contactsType] || registerInfo.coscoSupplierBase.contactsType} + + + {registerInfo.coscoSupplierBase.idCard} + + + {enterpriseTypeMap[registerInfo.coscoSupplierBase.enterpriseType] || registerInfo.coscoSupplierBase.enterpriseType} + + + {registerInfo.coscoSupplierBase.contactsPhone} + + + )} + + {registerInfo.coscoSupplierBase.contactsName} + + + {registerInfo.coscoSupplierBase.contactsEmail} + + + {registerInfo.coscoSupplierBase.telephone} + + + +
{intl.formatMessage({ id: 'component.globalModal.qualificationInfo' })}
} + /> + + {registerInfo.coscoSupplierBase.supplierType === 'dvs' && ( + + + { taxpayerTypeMap[registerInfo.coscoSupplierInvoice.taxpayerType] || registerInfo.coscoSupplierInvoice.taxpayerType } + + + {registerInfo.coscoSupplierInvoice.head} + + + {registerInfo.coscoSupplierInvoice.taxpayerCode} + + + {registerInfo.coscoSupplierInvoice.address} + + + {registerInfo.coscoSupplierInvoice.phone} + + + {registerInfo.coscoSupplierInvoice.bank} + + + {registerInfo.coscoSupplierInvoice.account} + + + + {intl.formatMessage({ id: 'component.globalModal.viewAttachment' })} + + + + )} + +
{intl.formatMessage({ id: 'component.globalModal.bankAccount' })}
} + /> +
{intl.formatMessage({ id: 'component.globalModal.ethicsQuestionnaireTitle' })}
+ + + + {registerInfo.coscoSupplierSurvey.supplierName} + + + {registerInfo.coscoSupplierSurvey.name} + + + {registerInfo.coscoSupplierSurvey.position} + + + {registerInfo.coscoSupplierSurvey.phone} + + + {registerInfo.coscoSupplierSurvey.email} + + + {registerInfo.coscoSupplierSurvey.dateTime} + + + +
+
index + 1 }, + { title: intl.formatMessage({ id: 'component.globalModal.question' }), dataIndex: 'questionName', key: 'questionName' }, + { title: intl.formatMessage({ id: 'component.globalModal.answer' }), dataIndex: 'replyValue', key: 'replyValue', width: 120 }, + ]} + pagination={false} + title={() =>
{intl.formatMessage({ id: 'component.globalModal.questionnaire' })}
} + /> + + + + + {registerInfo.coscoSupplierSurveyAttachments.map((item: any) => { + const { attachmentsType, fileUrl, fileName } = item; + return attachmentsType === 'commitment' ? ( + {fileName} + ) : null + })} + + + + + + {registerInfo.coscoSupplierSurveyAttachments.map((item: any) => { + const { attachmentsType, fileUrl, fileName } = item; + return attachmentsType === 'accessory' ? ( + {fileName} + ) : null + })} + + + + ); +}; + +export default SupplierRegisterInfo; diff --git a/src/pages/supplier/ViewReviewPage/GlobalModal/components/TianyanchaInfo.tsx b/src/pages/supplier/ViewReviewPage/GlobalModal/components/TianyanchaInfo.tsx new file mode 100644 index 0000000..338ddf3 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/GlobalModal/components/TianyanchaInfo.tsx @@ -0,0 +1,136 @@ +import React, { useState, useEffect } from 'react'; +import { Descriptions, Button, message } from 'antd'; +import type { ColumnsType } from 'antd/es/table'; +import type { IntlShape } from 'react-intl'; +import { getQuery, getQueryAndUpdate } from '../services'; +import { useIntl } from 'umi'; + + +interface TianyanchaInfoProps { + 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; // 最后更新时间 +} + + +const TianyanchaInfo = ({ id }: { id: string }) => { + const intl = useIntl(); + const [loading, setLoading] = useState(false); + const [dataList, setDataList] = useState([]); + const [updateTime, setUpdateTime] = useState(''); + + //列表 + const getList = async () => { + setLoading(true); + try { + const { code, data } = await getQuery({ supplierId: id }); + if (code === 200) { + if(data) { + setDataList(data); + setUpdateTime(new Date(Number(data.updateTimes)).toLocaleDateString()); + } + } + } finally { + setLoading(false); + } + }; + //更新 + const getUpdateList = async () => { + setLoading(true); + try { + const { code, data } = await getQueryAndUpdate({ supplierId: id }); + if (code === 200) { + setDataList(data); + setUpdateTime(new Date(Number(data.updateTimes)).toLocaleDateString()); + } + } finally { + setLoading(false); + } + }; + + const onUpdateTime = () => { + setUpdateTime(new Date().toLocaleDateString()); + getUpdateList(); + message.success(intl.formatMessage({ id: 'component.tianyancha.updateSuccess'})); + }; + useEffect(() => { + if (id) { + getList(); + } + }, [id]); + // 渲染 + const getColumns: 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' }, + ]; + return ( + <> + + + {intl.formatMessage({ id: 'component.tianyancha.title' })} + + ({intl.formatMessage({ id: 'component.tianyancha.updateTime' })}:{updateTime}){' '} + + + } bordered column={3}> + {getColumns.map(col => ( + + {dataList[(col as any).dataIndex] ?? '-'} + + ))} + + + ); +}; + +export default TianyanchaInfo; diff --git a/src/pages/supplier/ViewReviewPage/GlobalModal/index.tsx b/src/pages/supplier/ViewReviewPage/GlobalModal/index.tsx new file mode 100644 index 0000000..95995d6 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/GlobalModal/index.tsx @@ -0,0 +1,142 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Button, Space, Descriptions } from 'antd'; +import { useIntl } from 'umi'; +import { coscoSupplierBase } from './services' +import SupplierRegisterInfo from './components/SupplierRegisterInfo'; +import AccessCategoryTable from './components/AccessCategoryTable'; +import TianyanchaInfo from './components/TianyanchaInfo'; +import ContactsInfo from './components/ContactsInfo'; +import RiskList from './components/RiskList'; + +// 推荐只接收外部传入的 props +interface ViewGlobalModalModalProps { + visible: boolean; + id?: string; + onCancel: () => void; +} + +const ViewGlobalModalModal: React.FC = ({ + visible, + id, + onCancel, +}) => { + const intl = useIntl(); + + // 页面显示具体tab类型 + const [modalType, setModalType] = useState<'register' | 'category' | 'tianyancha' | 'risk' | 'ContactsInfo'>('register'); + //供应商信息数据 + const [registerInfo, setRegisterInfo] = useState(null); + + //获取供应商信息 + const fetchRegisterInfo = () => { + if (!id) return; + coscoSupplierBase(id).then((res) => { + let { code, data } = res; + if (code === 200) { + setRegisterInfo(data); + } + }) + }; + + // 初始化 + useEffect(() => { + if (visible && id) { + setModalType('register'); + fetchRegisterInfo(); + } else { + setRegisterInfo(null); + } + // eslint-disable-next-line + }, [visible, id]); + + // 切换tab + const handleSwitch = (type: typeof modalType) => setModalType(type); + + // 渲染tab内容 + const renderContent = () => { + if (modalType === 'register' && registerInfo?.coscoSupplierBase) { + return ; + } + if (modalType === 'category') { + return ; + } + if (modalType === 'tianyancha') { + return ; + } + if (modalType === 'risk') { + return ; + } + if (modalType === 'ContactsInfo') { + return ; + } + return null; + }; + + return ( + { + setRegisterInfo(null); + onCancel && onCancel(); // 只调外部传入的onCancel + }} + footer={null} + title={ +
+ {intl.formatMessage({ id: 'component.globalModal.title' })} +
+ } + > + {(registerInfo?.coscoSupplierBase?.supplierType !== 'pe' && registerInfo) ? + ( +
+ + + + + + + +
+ {renderContent()} +
+
+ ) : ( +
+ + + + +
+ {modalType === 'category' && ( + + )} + {modalType === 'register' && ( + + + {registerInfo?.coscoSupplierBase?.personName} + + + {registerInfo?.coscoSupplierBase?.idCard} + + + {registerInfo?.coscoSupplierBase?.personPhone} + + + {registerInfo?.coscoSupplierBase?.personBank} + + + {registerInfo?.coscoSupplierBase?.personAccount} + + + )} +
+
+ ) + } +
+ ); +}; + +export default ViewGlobalModalModal; diff --git a/src/pages/supplier/ViewReviewPage/GlobalModal/services.ts b/src/pages/supplier/ViewReviewPage/GlobalModal/services.ts new file mode 100644 index 0000000..ce4726a --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/GlobalModal/services.ts @@ -0,0 +1,67 @@ +import request from '@/utils/request'; + + +/** + * 天眼查查询 + */ +interface getQuery { + supplierId: string; +} +export const getQuery = (params: getQuery) => request.get('/tycAndFxSupplierBase/query', { params}); +/** + * 天眼查更新 + */ +interface getQuery { + supplierId: string; +} +export const getQueryAndUpdate = (params: getQuery) => request.get('/tycAndFxSupplierBase/queryAndUpdate', { params}); + + +/** + * 合规风险查询 + */ +interface queryRiskInfo { + supplierId: string; +} +export const queryRiskInfo = (params: queryRiskInfo) => request.get('/tycAndFxSupplierBase/queryRiskInfo', { params}); + + +/** + * 供应商注册信息 + */ +export const coscoSupplierBase = (id: string) => request.get(`/coscoSupplierBase/${id}` ); + +/** + * 准入品类 + */ +interface getCategoryPage { + pageNo: number; + pageSize: number; + supplierId?: string; +} +export const getCategoryPage = (data: getCategoryPage) => request.post('/coscoSupplierBase/getCategoryPage', { data }); + + +/** + * 准入品类 + */ +interface supplierIdPage { + basePageRequest: basePageRequests; + supplierId?: string; +} +interface basePageRequests { + pageNo: number; + pageSize: number; +} +export const supplierIdPage = (data: supplierIdPage) => request.post('/cosco/library/supplierIdPage', { data }); + + +/** +* 联系人分页列表 +*/ +interface getCoscoSupplierUserPage { + pageNo: number; + pageSize: number; + supplierId?: string; +} +export const getCoscoSupplierUserPage = (data: getCoscoSupplierUserPage) => request.post('/coscoSupplierUser/getPage', { data }); diff --git a/src/pages/supplier/ViewReviewPage/components/ResultModal.tsx b/src/pages/supplier/ViewReviewPage/components/ResultModal.tsx new file mode 100644 index 0000000..e7018e1 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/components/ResultModal.tsx @@ -0,0 +1,263 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Table, Button, Spin } from 'antd'; +import { reviewInfoData, groupByList } from '../services'; +import { connect } from 'umi'; +import GlobalModal from '../GlobalModal/index' + +interface ResultModalProps { + visible: boolean; + record?: { id?: string;[key: string]: any } | null; + onCancel: () => void; + dispatch: any; +} + +// 只读备注弹窗 +const RemarkViewModal: React.FC<{ + visible: boolean; + onCancel: () => void; + remark: string; + file?: any; +}> = ({ visible, onCancel, remark, file }) => ( + +
+
+ 备注:{remark || '无'} +
+ {file && file.fileUrl && ( +
+ 附件: + + {file.fileName} + +
+ )} +
+
+); + +const ResultModal: React.FC = ({ + visible, + record, + onCancel, + dispatch +}) => { + const [loading, setLoading] = useState(false); + const [suppliers, setSuppliers] = useState([]); + const [items, setItems] = useState([]); + const [groupSummaryResult, setGroupSummaryResult] = useState<{ [k: string]: '0' | '1' | undefined }>({}); + const [remarkModal, setRemarkModal] = useState({ open: false, remark: '', file: undefined as any }); + const [visibleGlobalModal, setVisibleGlobalModal] = useState(false); + const [id, setId] = useState(''); + // 拉取数据 + useEffect(() => { + if (visible && record?.id) { + setLoading(true); + // 拉取评审项(groupByList)+供应商+评审数据(reviewInfoData) + Promise.all([ + groupByList({ accessWorkId: record.id }), + reviewInfoData({ id: record.id }) + ]).then(([groupRes, reviewRes]: any) => { + // 评审项(按接口数据确定字段,兼容你的接口:itemType !== 'summary') + const allItems = (groupRes?.data || []).filter((it: any) => it.itemType !== 'summary'); + setItems(allItems); + + // 供应商及评审数据 + const supplierList = reviewRes?.data || []; + // 为每个 supplier 构造 coscoAccessItemList(根据 groupByList 的 items + supplier.coscoAccessUserItemList 拼装),这样 columns 渲染就不依赖接口中的 coscoAccessItemList + supplierList.forEach((sup: any) => { + // 按 itemName+itemType 拆分(兼容后续render逻辑) + const itemList = allItems.map((item: any) => { + // 找所有该供应商的userItem(同一itemName, itemType) + const userItems = (sup.coscoAccessUserItemList || []).filter( + (u: any) => u.itemName === item.itemName && u.itemType === item.itemType + ); + return { + ...item, + coscoAccessUserItemList: userItems + } + }); + // summary 行 + const summaryItems = (sup.coscoAccessUserItemList || []).filter( + (u: any) => u.itemType === 'summary' + ); + if (summaryItems.length) { + itemList.push({ + itemName: summaryItems[0].itemName, + itemType: 'summary', + coscoAccessUserItemList: summaryItems + }); + } + sup.coscoAccessItemList = itemList; + }); + setSuppliers(supplierList); + + // 汇总行初始化 + const summaryMap: { [k: string]: '0' | '1' | undefined } = {}; + supplierList.forEach((sup: any) => { + const summaryItem = (sup.coscoAccessItemList || []).find((i: any) => i.itemType === 'summary'); + summaryMap[sup.supplierId] = summaryItem?.coscoAccessUserItemList?.[0]?.reviewResult; + }); + setGroupSummaryResult(summaryMap); + }).finally(() => setLoading(false)); + } else if (!visible) { + setSuppliers([]); + setItems([]); + setGroupSummaryResult({}); + } + }, [visible, record]); + + // 构造二级表头 + const columns: any[] = [ + { + title: '评审项', + dataIndex: 'itemName', + key: 'itemName', + width: 180, + fixed: 'left', + }, + ...suppliers.map(sup => { + // 当前公司所有人员(所有非summary项,取 coscoAccessUserItemList 多个) + const reviewerMap = new Map(); + (sup.coscoAccessItemList || []).forEach((item: any) => { + if (item.itemType !== 'summary' && Array.isArray(item.coscoAccessUserItemList)) { + item.coscoAccessUserItemList.forEach((u: any) => { + reviewerMap.set(u.reviewBy, u.reviewName); // 工号 -> 姓名 + }); + } + }); + const reviewers = Array.from(reviewerMap.entries()).map(([reviewBy, reviewName]) => ({ reviewBy, reviewName })); + + return { + title: ( + + ), + children: reviewers.map(({ reviewName, reviewBy }) => ({ + title: reviewName, + dataIndex: `${sup.supplierId}_${reviewBy}`, + key: `${sup.supplierId}_${reviewBy}`, + width: 100, + align: 'center', + render: (_: any, row: any) => { + // 在sup.coscoAccessItemList里找该itemName下该人员 + const item = (sup.coscoAccessItemList || []).find( + (it: any) => it.itemName === row.itemName && it.itemType !== 'summary' + ); + if (!item) return null; + const userItem = (item.coscoAccessUserItemList || []).find((u: any) => u.reviewBy === reviewBy); + if (!userItem) return null; + return ( +
+ {userItem.reviewResult === '0' + ? 合格 + : userItem.reviewResult === '1' + ? 不合格 + : ''} + {userItem.remark && ( + + )} +
+ ); + } + })) + } + }) + ]; + + // 组装表格行数据 + const tableData = items.map(item => { + const row: any = { + key: item.id, + itemName: item.itemName + }; + suppliers.forEach(sup => { + // 每个人员一格 + const reviewerSet = new Set(); + (sup.coscoAccessItemList || []).forEach((it: any) => { + if (it.itemType !== 'summary' && Array.isArray(it.coscoAccessUserItemList)) { + it.coscoAccessUserItemList.forEach((u: any) => { + reviewerSet.add(u.reviewBy); + }); + } + }); + const reviewers = Array.from(reviewerSet); + reviewers.forEach((reviewBy: string) => { + row[`${sup.supplierId}_${reviewBy}`] = null; // 具体显示用render + }); + }); + return row; + }); + + // summary 行 + const summaryRow = ( + + + 结果汇总 + + {suppliers.map((sup, index) => { + // 统计reviewer个数 + const reviewerSet = new Set(); + (sup.coscoAccessItemList || []).forEach((item: any) => { + if (item.itemType !== 'summary' && Array.isArray(item.coscoAccessUserItemList)) { + item.coscoAccessUserItemList.forEach((u: any) => { + reviewerSet.add(u.reviewBy); + }); + } + }); + const colSpan = reviewerSet.size || 1; + return ( + + + {groupSummaryResult[sup.supplierId] === '0' ? '合格' : '不合格'} + + + ) + })} + + ); + + return ( +
+

查看评审结果

+ +
summaryRow} + scroll={{ x: 300 * suppliers.length + 200 }} + /> + + setRemarkModal({ open: false, remark: '', file: undefined })} + remark={remarkModal.remark} + file={remarkModal.file} + /> + + setVisibleGlobalModal(false)} /> + + ); +}; + +export default connect()(ResultModal); diff --git a/src/pages/supplier/ViewReviewPage/components/ViewModal.tsx b/src/pages/supplier/ViewReviewPage/components/ViewModal.tsx new file mode 100644 index 0000000..5c3e97e --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/components/ViewModal.tsx @@ -0,0 +1,161 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, Descriptions, Spin } from 'antd'; +import { useSupplierDetailModal } from '@/components/SupplierDetailModalContext/SupplierDetailModalContext'; +import GlobalModal from '../GlobalModal/index' +import { coscoAccessWork } from '../services' + +//数据接口 +interface Data { + coscoAccessWork: coscoAccessWorks; + coscoAccessSupplierList: coscoAccessSupplierLists[]; + coscoAccessCategoryList: coscoAccessCategoryLists[]; + coscoAccessUserls: coscoAccessUserl[]; + coscoAccessWorkAttachments: coscoAccessWorkAttachments; +} + +interface coscoAccessWorkAttachments { + fileName: string; + fileUrl: string; +} +interface coscoAccessUserl { + deptName: string; + deptId: string; + userName: string; + userId: string; +} +interface coscoAccessCategoryLists { + categoryName: string; + [property: string]: any; +} +interface coscoAccessSupplierLists { + supplierName: string; + [property: string]: any; +} +interface coscoAccessWorks { + deptId: string; + deptName: string; + startTime: string; + endTime: string; + reviewStatusText: string; + accessType: string; + accessDesc: string; + approveStatusText: string; +} + +const ViewModal: React.FC<{ + visible: boolean; + record?: any; + onCancel: () => void; +}> = ({ visible, record = {}, onCancel }) => { + //渲染数据 + const [data, setData] = useState < Data | null > (null); + const supplierDetailModal = useSupplierDetailModal(); + const [loading, setLoading] = useState(false); + const [visibleGlobalModal, setVisibleGlobalModal] = useState(false); + const [id, setId] = useState(''); + + //初始化 + useEffect(() => { + console.log(record,visible); + + if (visible && record?.id) { + setLoading(true); + coscoAccessWork(record.id) + .then((res) => { + const { code, data } = res; + if (code == 200) { + setData(data); + } + }) + .finally(() => setLoading(false)); + } else { + setData(null); + } + }, [visible, record]); + + return ( +
+

查看详情

+ + {data && ( + + {data.coscoAccessWork.deptName} + + {data.coscoAccessSupplierList.map((item) => { + return ( + { + setId(item.supplierId) + setVisibleGlobalModal(true) + }} + > + {item.supplierName} + + //
supplierDetailModal?.(item.supplierId)} >{item.supplierName}
+ + ) + })} +
+ + {data.coscoAccessWork.accessType === 'scattered' && ( + <> + + {data.coscoAccessCategoryList.map((item) => { + return ( +
{item.categoryPathName}
+ ) + })} +
+ {data.coscoAccessWork.accessDesc} + + {data.coscoAccessWorkAttachments.fileName} + + + )} + {data.coscoAccessWork.accessType === 'offline' && ( + <> + + {data.coscoAccessCategoryList.map((item) => { + return ( +
{item.categoryPathName}
+ ) + })} +
+ + {data.coscoAccessWorkAttachments.fileName} + + + )} + + + {data.coscoAccessWork.accessType === 'online' && ( + <> + + {data.coscoAccessCategoryList.map((item) => { + return ( +
{item.categoryPathName}
+ ) + })} +
+ {data.coscoAccessWork.startTime} + {data.coscoAccessWork.endTime} + + {data.coscoAccessUserls.map((item) => { + return ( +
{item.deptName} - {item.userName}
+ ) + })} +
+ {data.coscoAccessWork.approveStatusText} + + )} + +
+ )} +
+ setVisibleGlobalModal(false)} /> +
+ ); +}; + +export default ViewModal; \ No newline at end of file diff --git a/src/pages/supplier/ViewReviewPage/index.tsx b/src/pages/supplier/ViewReviewPage/index.tsx new file mode 100644 index 0000000..f5edb31 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/index.tsx @@ -0,0 +1,41 @@ +// ViewReviewPage.tsx +import React, { useState, useEffect } from 'react'; +import { useLocation } from 'umi'; // 或者 'react-router-dom' +import ResultModal from './components/ResultModal'; +import ViewModal from './components/ViewModal'; + +const ViewReviewPage: React.FC = () => { + const [modalVisible, setModalVisible] = useState(false); // 控制弹窗 + const [modalRecord, setModalRecord] = useState(null); + + // 解析url参数 ?id=xxx + const location = useLocation(); + useEffect(() => { + // 获取id参数 + const params = new URLSearchParams(location.search); + const id = params.get('id'); + if (id) { + setModalRecord({ id }); // 只传id也可以,后面可以根据id扩展更多参数 + + setModalVisible(true); + } + }, [location.search]); + + return ( +
+ {/* 其他页面内容 */} + setModalVisible(false)} + /> + setModalVisible(false)} + /> +
+ ); +}; + +export default ViewReviewPage; diff --git a/src/pages/supplier/ViewReviewPage/services.ts b/src/pages/supplier/ViewReviewPage/services.ts new file mode 100644 index 0000000..a9d1dd3 --- /dev/null +++ b/src/pages/supplier/ViewReviewPage/services.ts @@ -0,0 +1,126 @@ +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 groupByListoData { + accessWorkId: string; + } + export const groupByList = (params: groupByListoData) => request.get(`/coscoAccessItem/groupByList`, { params }); + +/** + * 未准入的供应商分页列表查询 + */ +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 }); + + + + + + +interface getUserPage { + basePageRequest: basePageRequest; + userId: string; +} +interface basePageRequest { + pageNo: number; + pageSize: number; +} + + +export const getUserPage = (data: getUserPage) => request.post('/v1/sysuser/getPageByUserCompany', { 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, + + }); +}; + +/** + * 发起审批 + */ +interface startApproveData { + id: string; +} +export const startApprove = (params: startApproveData) => request.get(`/coscoAccessWork/startApprove`, { params }); + +/** + * 查看评审结果 + */ +export const reviewInfoData = (params: startApproveData) => request.get(`/coscoAccessWork/reviewInfoData`, { params }); + +export const supplierChangeApprove = (data: { id:string; approveStatus:string }) => request.post('/synchronous/receiveApprove', { data }); \ No newline at end of file